feat: improve sync reliability and diagnostics#61
Conversation
Adds a reliability layer around NexaNote sync without changing the file-based storage layout or hardcoding any deployment details. - SyncPlan records intent (push/pull/ignore/conflicts/warnings) - dry-run mode builds the plan but writes nothing (no files, state, remote uploads, or log) - sanitized <data_dir>/sync_logs/latest.json log via GET /sync/log; never includes note body, passwords, tokens, or server URLs - conflict safety: both-changed notes are detected and surfaced instead of silently overwritten; local edits are preserved - failed syncs persist sync state atomically (no corruption) - tests for dry-run, log writing, sanitization, ignored-file diagnostics, conflict detection, and idempotent state https://claude.ai/code/session_01G4o3UeXj3sJNZrW9PxGNG3
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4cc5aade4b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # these, but a sync log is a durable artifact — belt and braces. | ||
| # FR: Filet de sécurité pour toute paire ``secret=valeur`` dans un message. | ||
| _KV_SECRET_RE = re.compile( | ||
| r"(?i)\b(password|passwd|pwd|token|secret|api[-_]?key|authorization|auth|credentials?)\s*[=:]\s*\S+" |
There was a problem hiding this comment.
Redact full Authorization header values
Update _KV_SECRET_RE so it removes the entire credential value, not just the first token. As written, sanitize_error("Authorization: Bearer abc.def.ghi") becomes Authorization=<redacted> abc.def.ghi, which still leaks the bearer token into sync_logs/latest.json and /sync/log. This affects any error message that uses multi-part auth formats (for example Bearer <token>), so sensitive credentials can still be exposed in persisted diagnostics.
Useful? React with 👍 / 👎.
Adds a reliability layer around NexaNote sync.
Includes:
This does not change the file-based storage layout or hardcode any deployment details.
What changed
Sync planning —
nexanote/sync/plan.pySyncPlanrecords the intent of a session:notes_to_push,notes_to_pull,notes_to_ignore,conflicts,warnings. It carries only metadata (note ids, titles, remote paths, reasons) — never note body — and is the single source the dry-run mode and the log read from.Dry-run mode —
nexanote/sync/client.pyNexaNoteSyncEngine(db, config, dry_run=True)(orPOST /sync/trigger?dry_run=true) builds the plan but writes nothing: no note files, no.nexanote_sync_state.json, no remotePUTs, no log. Every state mutation is funneled through small_apply_*guards that no-op in dry-run, so the "writes nothing" guarantee is easy to audit.Sanitized sync log —
nexanote/sync/sync_log.py<data_dir>/sync_logs/latest.json(resolves to/data/sync_logs/latest.jsonin the Docker image — derived from the configured data dir, not hardcoded), exposed viaGET /sync/log.key=valuecredential pairs.Conflict safety —
nexanote/sync/client.py(conflit …)copy is preserved so both versions survive on disk. Existing conflict-resolution behavior (andconflicts_resolved) is unchanged for theKEEP_BOTHpath.Idempotent state
os.replace), so a mid-session crash cannot corrupt.nexanote_sync_state.json.Constraints honored
_pull/_pushflow.Test plan
tests/test_sync_reliability.py— 13 new tests covering: dry-run writes no files/state, sync log written after sync, log excludes secrets/body, ignored legacy files appear in diagnostics, conflict detected (not silently overwritten) + local preserved, failed sync keeps state valid, plus URL/secret sanitization units.Generated by Claude Code