Sync Obsidian both ways so notes Simba writes reach your devices #86
@@ -195,6 +195,10 @@ OBSIDIAN_E2E_PASSWORD=
|
||||
OBSIDIAN_DEVICE_NAME=simbarag
|
||||
# Set to true to run continuous sync in the background
|
||||
OBSIDIAN_CONTINUOUS_SYNC=false
|
||||
# bidirectional: notes and journal tasks Simba writes sync back to your devices.
|
||||
# pull-only: download only, Simba's writes stay in the container's copy.
|
||||
# mirror-remote: download only and revert local changes.
|
||||
OBSIDIAN_SYNC_MODE=bidirectional
|
||||
OBSIDIAN_SYNC_INTERVAL=60
|
||||
# Local path to Obsidian vault (where files are synced)
|
||||
OBSIDIAN_VAULT_PATH=/app/data/obsidian
|
||||
|
||||
@@ -225,6 +225,14 @@ def validate_configuration(env: Mapping[str, str], *, production: bool = True) -
|
||||
problem(flag, "signature validation must remain enabled in production")
|
||||
for name in ("METRICS_TOKEN", "CRON_SECRET", "EMAIL_HMAC_SECRET"):
|
||||
secret(name, minimum=32)
|
||||
if env.get("OBSIDIAN_SYNC_MODE", "bidirectional") not in {
|
||||
"bidirectional",
|
||||
"pull-only",
|
||||
"mirror-remote",
|
||||
}:
|
||||
problem(
|
||||
"OBSIDIAN_SYNC_MODE", "must be bidirectional, pull-only or mirror-remote"
|
||||
)
|
||||
if env.get("OBSIDIAN_CONTINUOUS_SYNC") == "true":
|
||||
for name in ("OBSIDIAN_EMAIL", "OBSIDIAN_PASSWORD", "OBSIDIAN_VAULT_ID"):
|
||||
if not value(name):
|
||||
|
||||
@@ -61,6 +61,7 @@ x-raggr-common: &raggr-common
|
||||
- OBSIDIAN_E2E_PASSWORD=${OBSIDIAN_E2E_PASSWORD}
|
||||
- OBSIDIAN_DEVICE_NAME=${OBSIDIAN_DEVICE_NAME}
|
||||
- OBSIDIAN_CONTINUOUS_SYNC=${OBSIDIAN_CONTINUOUS_SYNC:-false}
|
||||
- OBSIDIAN_SYNC_MODE=${OBSIDIAN_SYNC_MODE:-bidirectional}
|
||||
- OBSIDIAN_VAULT_PATH=${OBSIDIAN_VAULT_PATH:-/app/data/obsidian}
|
||||
- OBSIDIAN_VAULT_NAME=${OBSIDIAN_VAULT_NAME:-}
|
||||
- S3_ENDPOINT_URL=${S3_ENDPOINT_URL}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ make logs
|
||||
make migrate-history
|
||||
```
|
||||
|
||||
Startup validates configuration, runs `aerich upgrade`, optionally sets up Obsidian pull-only sync, and replaces the shell with the Python process. **A migration failure stops startup; the application is never launched.** Obsidian login/setup failures still log a warning and allow the application to continue.
|
||||
Startup validates configuration, runs `aerich upgrade`, optionally starts Obsidian sync in the background (`OBSIDIAN_SYNC_MODE`, default `bidirectional` so notes and tasks Simba writes reach your devices; containers take turns via a lock file in the shared vault), and replaces the shell with the Python process. **A migration failure stops startup; the application is never launched.** Obsidian login/setup failures still log a warning and allow the application to continue.
|
||||
|
||||
An empty database without Aerich metadata may need explicit initialization. Inspect its state first. Run the migration tool in a one-off container because the app cannot start until migrations pass:
|
||||
|
||||
|
||||
+28
-19
@@ -19,27 +19,36 @@ if [ "${OBSIDIAN_CONTINUOUS_SYNC:-false}" = "true" ]; then
|
||||
if [ -z "${OBSIDIAN_EMAIL:-}" ] || [ -z "${OBSIDIAN_PASSWORD:-}" ] || [ -z "${OBSIDIAN_VAULT_ID:-}" ]; then
|
||||
echo "WARNING: OBSIDIAN_EMAIL, OBSIDIAN_PASSWORD, or OBSIDIAN_VAULT_ID not set. Skipping sync."
|
||||
else
|
||||
echo "Setting up Obsidian sync..."
|
||||
|
||||
VAULT_PATH="${OBSIDIAN_VAULT_PATH:-/app/data/obsidian}"
|
||||
SYNC_MODE="${OBSIDIAN_SYNC_MODE:-bidirectional}"
|
||||
mkdir -p "${VAULT_PATH}"
|
||||
|
||||
# Login and setup sync (foreground, must complete before sync starts)
|
||||
if ob login --email "${OBSIDIAN_EMAIL}" --password "${OBSIDIAN_PASSWORD}" && \
|
||||
ob sync-setup \
|
||||
--vault "${OBSIDIAN_VAULT_ID}" \
|
||||
--path "${VAULT_PATH}" \
|
||||
--password "${OBSIDIAN_E2E_PASSWORD:-}" \
|
||||
--device-name "${OBSIDIAN_DEVICE_NAME:-simbarag}"; then
|
||||
# Remove stale lock from previous container run
|
||||
rm -rf "${VAULT_PATH}/.obsidian/.sync.lock"
|
||||
# Set sync to pull-only (read-only) mode
|
||||
ob sync-config --mode pull-only --path "${VAULT_PATH}"
|
||||
# Start continuous sync in background
|
||||
echo "Starting Obsidian continuous sync (pull-only)..."
|
||||
ob sync --continuous --path "${VAULT_PATH}" &
|
||||
else
|
||||
echo "WARNING: Obsidian sync setup failed. Continuing without sync."
|
||||
fi
|
||||
# Blue and green share the vault volume. flock on a file in it works
|
||||
# across containers, so this container only sets up and runs sync once
|
||||
# the previous container's sync process has exited. Two clients writing
|
||||
# one vault and its sync state would otherwise race. Setup runs in the
|
||||
# background so the app starts while it waits.
|
||||
(
|
||||
flock 9
|
||||
echo "Setting up Obsidian sync (${SYNC_MODE})..."
|
||||
if ob login --email "${OBSIDIAN_EMAIL}" --password "${OBSIDIAN_PASSWORD}" && \
|
||||
ob sync-setup \
|
||||
--vault "${OBSIDIAN_VAULT_ID}" \
|
||||
--path "${VAULT_PATH}" \
|
||||
--password "${OBSIDIAN_E2E_PASSWORD:-}" \
|
||||
--device-name "${OBSIDIAN_DEVICE_NAME:-simbarag}"; then
|
||||
# Safe now: the lock means no other container's sync is running.
|
||||
rm -rf "${VAULT_PATH}/.obsidian/.sync.lock"
|
||||
if ob sync-config --mode "${SYNC_MODE}" --conflict-strategy merge \
|
||||
--path "${VAULT_PATH}"; then
|
||||
echo "Starting Obsidian continuous sync (${SYNC_MODE})..."
|
||||
exec ob sync --continuous --path "${VAULT_PATH}"
|
||||
fi
|
||||
echo "WARNING: Obsidian sync-config failed. Continuing without sync."
|
||||
else
|
||||
echo "WARNING: Obsidian sync setup failed. Continuing without sync."
|
||||
fi
|
||||
) 9>"${VAULT_PATH}/.raggr-sync.lock" &
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ def test_missing_required_settings(production_env, name):
|
||||
("CHECKPOINT_DB_PATH", "/data/invalid\x00.db"),
|
||||
("CONVERSATION_SUMMARY_DB_PATH", ""),
|
||||
("OBSIDIAN_CONTINUOUS_SYNC", "yes"),
|
||||
("OBSIDIAN_SYNC_MODE", "push-only"),
|
||||
],
|
||||
)
|
||||
def test_malformed_and_unsafe_settings(production_env, name, value):
|
||||
@@ -202,6 +203,7 @@ def test_compose_forwards_runtime_settings():
|
||||
"CHECKPOINT_DB_PATH",
|
||||
"CONVERSATION_SUMMARY_DB_PATH",
|
||||
"OBSIDIAN_SYNC_INTERVAL",
|
||||
"OBSIDIAN_SYNC_MODE",
|
||||
"OIDC_AUTHORIZATION_ENDPOINT",
|
||||
"OIDC_TOKEN_ENDPOINT",
|
||||
"OIDC_USERINFO_ENDPOINT",
|
||||
|
||||
Reference in New Issue
Block a user