diff --git a/README.md b/README.md index c9c4eb2..370f8b9 100644 --- a/README.md +++ b/README.md @@ -135,9 +135,9 @@ Bidirectional. Edit anywhere; it converges everywhere. Two sub-workflows, one or > **Why an orchestrator?** Two independent cron triggers can race each other and interleave commits — W1 mid-run clobbers a `.sync-state.json` update from W2, or vice versa. The orchestrator sequences `pull from Morgen → push merged state out` so the state file mutates serially. If you really want the un-sequenced version (e.g. you're self-hosting and have your own scheduling), pass `SKIP_ORCHESTRATOR=1` to the installer and leave W1/W2's own triggers active. > -> **Trade-off: W1 is no longer push-instant.** In the default install, you leave W1 inactive so only W0 can fire it — which means an edit to `06-Tasks/` propagates on the next 15-min tick, not on the next `git push`. If you want instant push→Morgen, either (a) activate W1 alongside W0 and accept occasional double-fires (n8n handles duplicate work cheaply because the jsCode is diff-aware), or (b) use `SKIP_ORCHESTRATOR=1` and run bare W1/W2 with their own triggers. +> **Trade-off: W1 is no longer push-instant.** In the default install, you leave W1 inactive so only W0 can fire it — which means an edit to `06-Tasks/` propagates on the next 20-min tick, not on the next `git push`. If you want instant push→Morgen, either (a) activate W1 alongside W0 and accept occasional double-fires (n8n handles duplicate work cheaply because the jsCode is diff-aware), or (b) use `SKIP_ORCHESTRATOR=1` and run bare W1/W2 with their own triggers. > -> **Known quirk: W0 self-overlap.** n8n's schedule triggers don't skip-if-running. If a 15-min tick lands while the previous W0 is still executing (possible during a large backfill), the second W0 queues up and starts immediately after — which re-introduces the very race W0 was built to prevent. In practice a full W2+W1 cycle finishes in well under 60 seconds, so the overlap window is tiny. If you see it, bump W0 to every 30 min. +> **Known quirk: W0 self-overlap.** n8n's schedule triggers don't skip-if-running. If a 20-min tick lands while the previous W0 is still executing (possible during a large backfill), the second W0 queues up and starts immediately after — which re-introduces the very race W0 was built to prevent. In practice a full W2+W1 cycle finishes in well under 60 seconds, so the overlap window is tiny. If you see it, bump W0 to every 30 min. ### Daemon (local, macOS) @@ -266,8 +266,8 @@ Open an issue or a discussion if you try it. Bug reports with `.sync-state.json` - **macOS only** for the daemon (launchd). Linux / Windows users need to port it. - **Morgen "inbox" task list only.** Morgen's API doesn't yet expose task-list management, so everything lands in your default inbox list. - **Morgen task-to-calendar promotion is unavailable** via API. You'll still drag tasks onto the calendar in Morgen's UI (or lean on Morgen's auto-scheduler). -- **Rate budget:** W1 is capped at ~100 Morgen ops per run to stay inside Morgen's 300 points / 15 min. Because W0 fires W2 + W1 serially on every 20-min tick, plan your ops budget against the *cumulative* per-15-min total (W2 + W1 combined) — not W1 in isolation. A fresh backfill that touches 300+ tasks will blow past the Morgen budget; pre-stage via `scripts/morgen-backfill.js` instead. -- **Existing users with a `W2-3-1-Sync-Orchestrator`:** the installer creates a new `W0-Sync-Orchestrator` beside your existing one. Delete the old `W2-3-1-Sync-Orchestrator` in the n8n UI before running `scripts/install-workflows.sh`, or you'll end up with two orchestrators firing on independent 15-min cadences — which defeats the whole serialization guarantee. +- **Rate budget:** W1 is capped at `RATE_BUDGET = 250` Morgen ops per run (well under Morgen's 300 points / 15 min API budget). Because W0 fires W2 + W1 serially on every 20-min tick, plan your ops budget against the *cumulative* per-20-min total (W2 + W1 combined) — not W1 in isolation. A fresh backfill that touches 300+ tasks will blow past the Morgen budget; pre-stage via `scripts/morgen-backfill.js` instead. +- **Existing users with a `W2-3-1-Sync-Orchestrator`:** the installer creates a new `W0-Sync-Orchestrator` beside your existing one. Delete the old `W2-3-1-Sync-Orchestrator` in the n8n UI before running `scripts/install-workflows.sh`, or you'll end up with two orchestrators firing on independent 20-min cadences — which defeats the whole serialization guarantee. --- diff --git a/SECURITY.md b/SECURITY.md index 349f1da..0cd31f0 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,7 +22,6 @@ If you discover a security vulnerability, please report it responsibly: | Credential | Grants | Rotation | |------------|--------|----------| | `GITHUB_TOKEN` | Read/write to the private task-mirror repo | GitHub → Settings → Developer settings → Fine-grained PATs | -| `NOTION_TOKEN` | Read/write to the Notion Tasks database you shared with the integration | Notion → My integrations → rotate secret | | `MORGEN_API_KEY` | Full access to your Morgen calendar + tasks | https://platform.morgen.so/developers-api | | `N8N_API_KEY` | Full access to your n8n instance (create / edit / activate workflows) | n8n UI → Settings → API → rotate | @@ -33,7 +32,7 @@ If you discover a security vulnerability, please report it responsibly: 1. Rotate the credential at its source (links above). 2. Update the value in your local `.env` file. 3. Re-run `./scripts/install-workflows.sh` to push the new credential into n8n. -4. In the n8n UI, deactivate and reactivate W1 / W2 / W3 so their credential references refresh. +4. In the n8n UI, deactivate and reactivate W1 / W2 so their credential references refresh. ## Scope @@ -42,16 +41,16 @@ If you discover a security vulnerability, please report it responsibly: - The local daemon (`src/auto-commit.js` + `daemon/install-daemon.sh`) - GitHub Actions workflows under `.github/workflows/` -Out of scope: your personal fork of this repo (`YOUR-VAULT-tasks`), your n8n instance, and any third-party services (Notion, Morgen, GitHub) that this project integrates with — please report issues in those systems to the upstream vendor. +Out of scope: your personal fork of this repo (`YOUR-VAULT-tasks`), your n8n instance, and any third-party services (Notion, Morgen, GitHub) that this project integrates with — please report issues in those systems to the upstream vendor. (As of 2026-05-04 the kit no longer integrates with Notion — Notion was dropped from the sync stack; the W3 worker is a no-op stub.) ## Defense in Depth - **No secrets at rest in the repo.** Workflow JSONs use `{{PLACEHOLDER}}` tokens substituted at install time by `scripts/install-workflows.sh`. The rendered files go to `/tmp/`, are POSTed to n8n, then deleted. - **CI grep guards.** `.github/workflows/validate.yml` scans workflow JSONs on every push for hardcoded token shapes (`ghp_`, `ntn_`, `sk-`, `ApiKey ...`). A PR that accidentally bakes a secret into a committed JSON will fail CI. -- **Bot-prefix echo guard.** Every automated commit carries a `[bot:daemon]` / `[bot:W1]` / `[bot:W2]` / `[bot:W3]` / `[bot:backfill]` prefix so W1's webhook handler can skip them and avoid an infinite ping-pong. -- **Flip-ratio guard.** W1 and W3 refuse to proceed if a single run would flip >25% (W1) / >30% (W3) of your task state. This catches "I accidentally deleted TASKS-URGENT.md" before it replicates to Notion + Morgen. +- **Bot-prefix echo guard.** Every automated commit carries a `[bot:daemon]` / `[bot:W1]` / `[bot:W2]` / `[bot:backfill]` prefix so W1's webhook handler can skip them and avoid an infinite ping-pong. +- **Flip-ratio guard.** W1 refuses to proceed if a single run would flip >25% of your task state. This catches "I accidentally deleted TASKS-URGENT.md" before it replicates to Morgen. ## Known Limitations - The daemon shells out to `git` via `execFileSync`. A hostile `$PATH` could swap out `git` for a malicious binary — the installer hardens `PATH` to well-known system locations (`/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin`), but if you install the daemon in an environment with a compromised `$PATH` at that moment, that hardening won't help. -- Morgen's API does not expose webhook notifications — W2 polls on a 60s cron. An attacker who compromises your Morgen account has a ≤60s window before W2 propagates the change to Obsidian + Notion. +- Morgen's API does not expose webhook notifications — W2 runs every 20 min via the W0 orchestrator. An attacker who compromises your Morgen account has a ≤20 min window before W2 propagates the change to Obsidian.