n8n outbound calling — forge agents → n8n → external services¶
Set up 2026-04-27 as the foundation for the Second Brain MVP. The architecture: forge LLM never holds OAuth tokens; instead, every action that touches Gmail/Calendar/Notion/TickTick goes through a single-purpose n8n webhook. Trust boundary lives at "which workflows exist" — not "where tokens live."
Files¶
| Path | Purpose |
|---|---|
~/.forge-secrets/n8n.env |
N8N_BASE_URL + N8N_WEBHOOK_SECRET (chmod 600, outside repo) |
scripts/n8n/call.sh |
The CLI — call.sh <webhook-path> '<json>' |
infra/n8n/outbound-pattern.md |
The conventions doc (read before building new workflows) |
infra/n8n/workflows/*.json |
Source of truth for every forge workflow definition (export from n8n UI after edits) |
n8n container env (on CT 106 at /opt/n8n/docker-compose.yml)¶
FORGE_WEBHOOK_SECRET=<same as N8N_WEBHOOK_SECRET in forge-secrets>— checked by every workflow's auth nodeN8N_BLOCK_ENV_ACCESS_IN_NODE=false— required so Code nodes can read$env.FORGE_WEBHOOK_SECRET
After editing compose: cd /opt/n8n && docker compose up -d --force-recreate. Then re-import any active workflows (n8n reads from DB, not compose).
Importing a workflow into n8n via CLI¶
docker exec n8n n8n import:workflow --input=/tmp/<file>.json after docker cp-ing into the container. Workflow JSON must have a top-level id field in nanoid format (16 chars alphanumeric, mixed case — NOT plain hex). After import, activate with docker exec n8n n8n update:workflow --id=<id> --active=true THEN docker compose restart (n8n caches workflow state in memory; DB updates don't take effect without restart).
UI import is more forgiving — picks up missing fields automatically.
Test workflow¶
forge-hello (id NtWVpdFuOXFVALXm) — POST /webhook/forge-hello, validates X-Forge-Auth header against $env.FORGE_WEBHOOK_SECRET, returns {ok, received, ts}. Use to validate plumbing after changes.
scripts/n8n/call.sh forge-hello '{"msg":"ping"}'
# → {"ok":true,"received":{"msg":"ping"},"ts":"..."}
Phase 1 hardening — COMPLETE 2026-04-27¶
- ✓ Cloudflare Zero Trust Access on
n8n.justinkrystal.com— email auth →[email protected]. External hits get 302 tojustinkrystal.cloudflareaccess.com/cdn-cgi/access/login/.... Internal LAN (http://192.168.86.82:5678) bypasses CF entirely so forge agents stay fast. - ✓ Encryption key offsite (NordPass) —
ukTaUYAykQeG187laylhkUUZwRRlEysJ - ✓ n8n moved from
n8n.justinwieb.com→n8n.justinkrystal.comvia media-server tunnel; forge-n8n tunnel + cloudflared on n8n LXC retired - ✓ n8n login MFA enabled
- ✓ Webhook secret in NordPass
Next gate before OAuth credentials: Phase 0 account-level hardening on the two Gmail accounts ([email protected] + [email protected]) — printed backup codes, hardware key enrollment, recovery email = Vault, audit existing OAuth grants.
Existing n8n credentials (as of 2026-04-27)¶
| ID | Name | Type |
|---|---|---|
N4en6wi0o81nWDcn |
Cloudflare | cloudflareApi |
tc1oVB8LQUUzlBiZ |
Home Assistant | homeAssistantApi |
nk7XoeWXeZkUigKm |
Frigate NVR (local) | httpBasicAuth |
mEef8a4KkOzn3d7y |
Eight Sleep (wellness) | httpBasicAuth |
saT0X6MMAFLKrBOT |
Garmin Connect (wellness) | httpBasicAuth |
mgQHSiQOlbURyLs1 |
Notion | notionApi |
ZZFiC6ljPZ3DWUDF |
UDev SSH (justinwieb) | sshPrivateKey |
Missing for Second Brain MVP: Gmail (×2), Google Calendar (×2), TickTick. Add via n8n UI when ready.
Workflow naming¶
<verb>-<thing>, kebab-case. Tag every forge workflow with forge + one of outbound / inbound.
Examples planned (not yet built):
- create-calendar-event, list-calendar-events
- create-gmail-draft, read-emails
- create-notion-project
- create-ticktick-task