The 06:00 workflow is now one schedule-trigger -> Execute Command pair that runs scripts/morning-brief.sh on the host over a restricted SSH key. The earlier scan -> HTTP shape could not work: the n8n container cannot resolve the `remote` ssh alias (its ~/.ssh/config is a dangling nix symlink) and a split scan node hides scan failures before the notify node runs. - deploy/n8n-morning-brief.json: two-node workflow, scheduleTrigger v1.3 - deploy/setup-n8n-scan-key.sh: restricted (restrict,command=) trigger key - deploy/import-n8n-morning-brief.sh: import + publish + restart + verify - deploy/DEPLOYMENT.md, Makefile: document and expose both scripts (dry run) - docs/phase-7a-n8n-schedule.md: design, verification, failure drill Import into the shared n8n service still needs Sam's approval. Co-Authored-By: Paperclip <noreply@paperclip.ing>
4.6 KiB
Phase 7a — the 6:00am n8n schedule (AUD-115)
Phase 7 (AUD-111) proved the delivery path: POST /notify/morning, the per-boy and
combined briefs, the Apprise channels, and eight real deliveries. This phase makes
the schedule real: one n8n workflow that runs every day at 06:00 and sends the
briefs.
What changed
deploy/n8n-morning-brief.json is now a two-node workflow:
Schedule Trigger "0 6 * * *" (Australia/Melbourne)
|
v
Execute Command ssh … sam@192.168.20.13 'scripts/morning-brief.sh'
The earlier draft had three nodes (scan via ssh remote → POST /notify/morning).
Two problems made that shape wrong here:
ssh remotedoes not work. The n8n container mounts~/.sshread-only, and~/.ssh/configis a Nix symlink into/nix/store, which does not exist inside the container. The hostnameremotedoes not resolve, and the container key is not in~/.ssh/authorized_keys(the host alias fails too).- A split scan/send node hides scan failures. If the scan node fails, n8n stops
before the HTTP node, so
POST /notify/morningnever runs and the family gets no brief at all — the opposite of FR-12.
scripts/morning-brief.sh is already the bounded command that does both steps and
exits non-zero when the scan or a send fails. One Execute Command node is therefore
both simpler and more correct: a failed scan turns the n8n execution red and still
sends a brief that opens with the failure (SCAN FAILED), never "nothing due".
Because the single command runs on the host, n8n needs no SCHOOL_NOTIFY_TOKEN
and no HTTP node. The token stays in environment.d/school-service.env only.
The SSH trigger key
The n8n container mounts /home/sam/.ssh read-only at /home/node/.ssh. The trigger
uses a dedicated key with a forced command, so it can run exactly one thing:
restrict,command="cd /home/sam/paseo/projects/compass_teams_nhs && ./scripts/morning-brief.sh" ssh-ed25519 AAAA…
restrictdisables pty, agent forwarding, port forwarding, X11, and shell access.command=makes the server ignore whatever command the client sends.- The private key lives at
/home/sam/.ssh/id_ed25519_school_scan(mode 0600) and is readable only bysamand the n8n container (which already mounts~/.ssh). - The key is not sam's personal key and is not used anywhere else.
Create it with:
deploy/setup-n8n-scan-key.sh # dry run: print the plan
deploy/setup-n8n-scan-key.sh --apply # create the key + the authorized_keys line
Import and activate
deploy/import-n8n-morning-brief.sh # dry run: print the plan
deploy/import-n8n-morning-brief.sh --apply # import, publish, restart n8n
The script copies the JSON into the container with docker exec -i … sh -c 'cat > …'
(docker cp fails against the read-only ~/.ssh mount), runs
n8n import:workflow, then n8n publish:workflow --id=<id> (n8n 2.x replaced
update:workflow --active), restarts n8n once so the 06:00 schedule registers, and
prints n8n list:workflow --active=true.
Verification
docker exec n8n n8n list:workflow --active=true # the workflow is published
docker exec compass-teams-nhs /app/school notify status
A healthy morning looks like this in notify status:
1 Harry Harry email ok <date> 06:0x
2 Harry Harry whatsapp ok <date> 06:0x
3 Finn Finn email ok <date> 06:0x
4 Finn Finn whatsapp ok <date> 06:0x
5 combined Jo email ok <date> 06:0x
6 combined Jo whatsapp ok <date> 06:0x
7 combined Sam email ok <date> 06:0x
8 combined Sam whatsapp ok <date> 06:0x
Failure drill (never leave an empty day): point the key's forced command at a command that exits non-zero, trigger the workflow, and confirm the n8n execution is red and the brief body starts with the scan failure. Restore the command afterwards.
Proving a run
The Schedule Trigger only fires at 06:00, so the schedule itself is proved by
n8n list:workflow --active=true plus the next morning's notify status rows. To
prove the full send path without waiting, run the published workflow once by hand:
docker exec n8n n8n execute --id=<workflow-id>
This sends the real briefs, so run it only when a real delivery is wanted. n8n list:workflow prints the id. Both checks are recorded in this issue's final comment.
Not touched
- Compass / Teams: read-only. Nothing in this phase writes to the school.
- Other n8n workflows: untouched.
- Secrets: no address, phone number, Apprise key, session secret, or notify token is written into the workflow JSON, this document, or git.