Skip to content

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 node
  • N8N_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

  1. Cloudflare Zero Trust Access on n8n.justinkrystal.com — email auth → [email protected]. External hits get 302 to justinkrystal.cloudflareaccess.com/cdn-cgi/access/login/.... Internal LAN (http://192.168.86.82:5678) bypasses CF entirely so forge agents stay fast.
  2. Encryption key offsite (NordPass) — ukTaUYAykQeG187laylhkUUZwRRlEysJ
  3. n8n moved from n8n.justinwieb.comn8n.justinkrystal.com via media-server tunnel; forge-n8n tunnel + cloudflared on n8n LXC retired
  4. n8n login MFA enabled
  5. 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