Lugar Común — Build Log
Building a Telegram-to-git commonplace book on n8n, TrueNAS, and a small AWS Lambda — log, snags, open questions.
A Telegram-driven commonplace book pipeline, built across phases for an n8n job application portfolio piece.
The idea
Telegram bot → n8n (self-hosted, TrueNAS) → AI enrichment → atomic commit to the life repo → Vercel rebuild → entries appear at life.tinkerer.tools/commonplace/.
Photos and text only for the MVP. Videos deferred. The author of an entry can be Ignacio, Laura, Zoe, or Diego — the AI infers from message content, with an ask-back to Telegram on low confidence.
Phase 0 — Requirements
- n8n deployment: TrueNAS catalog app.
- Content destination: git-native. The workflow commits markdown files to the
liferepo and the site rebuilds. - Media storage: photos in repo (
public/commonplace/media/{slug}/). Videos deferred to S3 if and when re-enabled. - Enrichment: AI infers
who,on, andsummaryfrom message context. Spanish summaries, original language preserved in the body. - Slug collisions: time suffix —
{who}-{YYYY-MM-DD}-{HHmm}in Atlantic/Canary tz. - Push auth: small Lambda (
commit-service) wraps the CodeCommitCreateCommitAPI, called via API key from n8n. Avoids hand-rolling SigV4 in n8n. - Allowlist: Ignacio only (Telegram user id
908844549). - Photo-only messages: bot replies “caption?” and waits. The caption becomes ground truth, no vision call.
- Privacy hard line: kid faces never go to any LLM, so vision is out — text-only enrichment.
- Confidence handling: ask-back via Telegram inline buttons when AI is unsure of
whooron.
Phase 1 — Plan
Architecture
Telegram ──► n8n (TrueNAS) ──► Anthropic API (text only)
│
└──► Lambda: commit-service ──► CodeCommit (life)
└──► Vercel rebuilds
Schema extension to life (commonplace collection)
z.object({
who: z.enum(['ignacio', 'laura', 'zoe', 'diego']), // added ignacio
on: z.coerce.date(),
summary: z.string().optional(),
media: z.array(z.object({
src: z.string(),
alt: z.string().optional(),
})).optional(),
})
Phase 2 — Execution log
Step 1 — Schema PR
Claude Code delivered a clean diff. astro check and npm run build both pass. The person color token for ignacio (life-ember) already existed in family.ts.
Step 2 — Commit-service Lambda
Project: commonplace-commit-service, separate repo from life. Stack deployed to eu-west-1. Node 22, arm64, 512 MB, 30 s timeout.
Endpoints:
GET /recent?limit=10— last N entries from CodeCommit, frontmatter-parsed, sorted byondesc.POST /commit— atomic multi-file commit (markdown + photos) viaCreateCommitCommand, retry onParentCommitIdOutdatedException.
IAM scoped to codecommit:GetFile, GetFolder, GetBranch, CreateCommit on the life repo only. No wildcards. Auth via API Gateway with an x-api-key header.
Live test: throwaway entry committed and read back successfully, then deleted.
Step 3 — n8n install on TrueNAS
Catalog app version 1.10.29. Stack includes Postgres + Redis + n8n. Initial install failed with postgres_upgrade exit 1 — Not writable path [/var/lib/postgresql]. Resolved with TrueNAS UI’s “automatic permissions” toggle, no manual chown needed. n8n up and reachable on the LAN.
Step 4 — Telegram bot
Created via BotFather. Token saved. Privacy mode disabled so the bot can see all DM text, not just commands.
Step 4.5 — Cloudflare Tunnel + n8n MCP
Subdomain n8n.tinkerer.tools already wired via existing Cloudflare Tunnel. Required env vars on the n8n app:
WEBHOOK_URL=https://n8n.tinkerer.tools/N8N_EDITOR_BASE_URL=https://n8n.tinkerer.tools/
Without these, n8n advertises its internal NodePort (e.g. :30109) in webhook and MCP URLs, which the Cloudflare proxy drops — it only forwards 80/443.
n8n’s built-in instance-level MCP enabled, connected to Claude Code via:
claude mcp add --transport http n8n https://n8n.tinkerer.tools/mcp \
--header "Authorization: Bearer <token>"
Step 5 — MVP workflow (in progress)
Three credentials staged in n8n: Telegram - Lugar Común, Anthropic, Commonplace Commit Service.
Workflow design (text-only MVP, no photos, ask-back, or recent-context yet):
- Telegram Trigger
- IF allowlist (
from.id === 908844549) - HTTP Request to Anthropic — strict JSON schema in the system prompt
- Code node — parse, build slug with HHmm time suffix
- HTTP Request to commit-service
/commit - Telegram Send Message — confirmation with live URL
- Error workflow → Telegram DM with node + error message
Snag — Vercel doesn’t watch CodeCommit
I had assumed Vercel had a native CodeCommit integration. It doesn’t. Commits land but nothing rebuilds.
Two paths considered:
- A. Migrate
lifeto GitHub. Vercel’s native integration replaces the deploy-hook plumbing, the commit-service gets simpler (GitHub Contents API instead of CodeCommit SDK + SigV4), tooling improves end to end. Recommended path. - B. Add a Vercel deploy hook call to the commit-service Lambda. Tactical fix that ships the MVP today and leaves CodeCommit debt for later.
Decision pending.
Open items
- Resolve the Vercel rebuild trigger (path A or B above).
- Vaultwarden has been down ~2 months on the same
postgres_upgrade exit 1pattern. Fix in the same session, then move secrets there. - Build photo branch.
- Build ask-back (Telegram inline keyboard + wait node).
- Wire recent-entries context into enrichment.
- Final canvas polish for the recruiter screenshot.
Reference values
- Telegram allowlist: user id
908844549 - Commit-service API:
https://1i8i4lnuz5.execute-api.eu-west-1.amazonaws.com/prod - Commit-service API key id:
65pk8m4vf4 - Region:
eu-west-1 - CodeCommit repo:
life(account071372696451) - Public n8n:
https://n8n.tinkerer.tools - Site:
https://life.tinkerer.tools/commonplace/ - Timezone:
Atlantic/Canary