Files
claude-projects/agent-builder/.claude/context.md
T
Backtalk6858 23bbcb4a6d note: next session — secrets proxy sandbox precondition scaffolding
Before the shell sandbox dry-run, proxy needs to detect missing state
(files, services, iptables rules) and create it so the test is faithful.
2026-06-22 20:50:20 -05:00

268 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
project_name: agent-builder
# Agent Builder — Session Context
## What this project does
Design, build, and test autonomous N8N agents on server-01 sandbox before any production promotion.
First two agents: Agent Builder Agent + N8N Builder Agent.
## Scheduled work (2026-06-16, running behind — started ~6:38 PM)
1. Vision Alignment Grill-Me — Agent Builder + N8N Builder vision + testing methodology
2. Agent Builder Agent — Deploy + Test (server-01 sandbox)
3. N8N Builder Agent — Deploy + Test (server-01 sandbox)
## Architecture
- Agents run as N8N workflows on server-01 (n8n-sandbox, port 5679)
- Sandbox-first: all agents tested in sandbox before any production promotion
- server-01 sandbox stack: n8n-sandbox, postgres-sandbox, vault-sandbox, bitwarden-bridge-sandbox, vaultwarden-sandbox
- Sandbox N8N API key: prod Vault at secret/sandbox/n8n
- Sandbox reachable at 192.168.1.90
## Key decisions (set during vision grill-me — 2026-06-16)
- Agent Builder Agent: builds `claude_agent` and `script` types — Ollama (llama3.1:8b) does the building, `claude -p` is overseer/validator
- N8N Builder Agent: builds `n8n_automation` types — Ollama generates workflow JSON, imports via N8N API, assigns credentials
- automation_ideas schema changes needed: rename `description``task_description` (full structured spec), add `type` (n8n_automation/claude_agent/script), add `builder_status`
- New `agent_test_results` table needed in api_business DB
- Sandbox must mirror production: AppRole, Vaultwarden, bridge all configured before any agent deploys
- Promotion = user approval required after all 4 test levels pass (not auto-promote in v1)
- Dedicated backfill session needed for all 48 existing automation_ideas rows (type + task_description)
- claude -p uses SDK credits (Pro = $20/month hard limit) — use sparingly, Ollama does the heavy lifting
- Local model: llama3.1:8b already pulled on server-01 (4.9GB, fits in RTX 2060 Super 8GB VRAM)
## Testing methodology
- Four levels: Structure → Deployment → Smoke → Assertion
- LLM outputs validated on structure/side-effects only, never exact string match
- All results logged to agent_test_results table
- NTFY notification on pass and fail
- Full methodology: .claude/playbook_testing_methodology.md
## Agents
### Agent Builder Agent
- Status: pending — prereqs not complete
- Purpose: Receives automation spec from automation_ideas DB, uses Ollama to build claude_agent or script type automations, deploys to sandbox, runs automated tests, notifies user for promotion approval
- Builds: claude agents (via claude -p) and Python scripts (Docker containers)
### N8N Builder Agent
- Status: pending — prereqs not complete
- Purpose: Receives automation spec from automation_ideas DB, uses Ollama to generate N8N workflow JSON using n8n_automations playbook as context, imports to sandbox N8N via API, assigns credentials, runs automated tests
- Will be used to build: id=12 (Media Pipeline Learning), id=7 (Friday Research Session Prep)
## Related personal_projects DB rows
- id=4: N8N Workflow Builder Script (pending, weekend_block1)
## Prereq checklist (must complete before any agent deployment)
- [x] Schema: rename automation_ideas.description → task_description, add type, add builder_status, add priority
- [x] Create agent_test_results table in api_business
- [x] Sandbox Vault: set up AppRole auth method (credentials at /opt/appdata/docker/docker-compose/vault/approle/ on server-01)
- [x] Sandbox Vault: store sandbox N8N API key at secret/sandbox/n8n (key name: claude-sandbox, verified working)
- [x] Verify sandbox Bitwarden bridge ↔ Vaultwarden sandbox end-to-end (bridge on port 8080, returns [] for empty vault — correct)
- [x] Write Agent Builder Agent playbook → .claude/playbook_agent_builder_agent.md
- [x] Write N8N Builder Agent playbook → .claude/playbook_n8n_builder_agent.md
- [x] Backfill session: **COMPLETE.** All pending rows reviewed across sessions 1-3. Blocked rows verified (32, 30, 13, 27, 23, 24, 22, 2, 21, 35, 36, 55, 56, 18 — all still blocked, no changes without proper review). Admin done: id=28 → p10, id=57 keeps p9, dummy row id=16 deleted. Session 3 (2026-06-18) final batch: ids 47, 48, 31, 26, 54.
- id=25: type→n8n_automation, status→pending (8b model lifts hardware block), full rewrite with SDK credit model, model router + cost gate + subscription monitor, model_effort_routing_log training table
- id=17: type→n8n_automation, expanded with token_waste_patterns DB table (evolution mechanism), JSONL-based detection, NTFY claude-audit gate, fan-out plan noted
- id=42: expanded with hybrid trigger (Claude flags + asks), B+C output (DB + wrap-up surface), human gate permanent, hard boundary (business research = id=55 only), voice note
- id=46: type→n8n_automation, Hermes ref removed, playbook_schema.md bootstrap design, three risk tiers, id=52 detector/id=46 executor boundary, fan-out plan noted
- id=43: Vault AppRole required (youtube-oauth-script, new policy), vault_registry entries, port 8085 (8080 reserved for bridge), dynamic Vault IP, write verification, token refresh companion
- id=45: hard dep on id=43 (Vault auth), niche_saturation_thresholds DB table (evolution), niche_check_results table, three-signal output (saturated/viable/unclear), id=18 stage 2 integration
## New automation_ideas rows added 2026-06-18 (ids 5967)
- id=59: Claude Audit Research Prep (n8n_automation, p12) — Saturday briefing for 12:45 PM review
- id=60: Claude Audit Topic Detector (n8n_automation, p11) — detects new topics → claude_config.audit_topics; must build first
- id=61: Claude Config Dev Work Scheduler (n8n_automation, p13) — Sunday session prep from Saturday decisions
- id=62: Monthly Security Audit (n8n_automation, p7) — first Monday of month, 7-area audit → security_audit DB
- id=63: Security Patch Backlog Handler (claude_agent, p7) — works security_audit.findings; Vault/prod always human-gate
- id=64: Business Projects Worker (claude_agent, BLOCKED p29) — 12:454:30 PM daily; blocked pending business_projects schema
- id=65: Personal Projects Worker (claude_agent, BLOCKED p29) — 5:308:00 PM daily; blocked pending personal_projects schema
- id=66: Media Pipeline Project Worker (claude_agent, BLOCKED p29) — Wednesday 5:308:00 PM; blocked pending id=65 + scope
- id=67: N8N Builder Agent (claude_agent, p2, ready_to_build) — builds all n8n_automation rows; deploy Monday June 22 alongside id=24
## Readiness check — COMPLETE (2026-06-18 evening)
All 8 prereq checklist items verified live on server-01:
- Vault AppRole: token acquired, N8N secret readable
- N8N sandbox API: returns 200 with real key
- Bridge /items: returns [] (correct for empty sandbox vault)
- Ollama: llama3.1:8b loaded
- agent_test_results table: confirmed correct schema + FK
## June 20 (Saturday) — Session progress
**id=57 partially complete.** Carried over from June 19 (sick day).
### id=57 — DONE this session:
- Production N8N migrated from primary server → server-01 port 5678
- Vault-backed start.sh: secrets pulled from production Vault at runtime, nothing on disk
- n8n-server01 AppRole created in production Vault (scoped to secret/data/n8n read-only)
- Stale root token in Bitwarden replaced — regenerated via generate-root (3/5 keys), updated, revoked
- Traefik static route live: /traefik/dynamic/n8n.yaml → 192.168.1.90:5678
- Rollback procedure documented + all 4 checks passed: /opt/appdata/docker/docker-compose/n8n/ROLLBACK.md
- claude-policy extended: AppRole management + sys/generate-root paths added
### id=57 — REMAINING (next session):
- Sandbox Vaultwarden bridge fix: 53 ciphers seeded (DB confirmed), but bw CLI WASM crash on list — bridge /items returns 500. Fix: update Vaultwarden image OR rebuild bridge with newer bw CLI OR patch bridge /items to use REST API
- N8N sandbox credentials (-sandbox suffix names) — Bridge API Key already exists; need postgres, Vault, NTFY
- Sudo bridge deploy on server-01 (Phase 2) — then disable NOPASSWD:ALL in /etc/sudoers.d/administrator on server-01
### id=51 — NOT STARTED. Scope locked:
- Option B zero-exposure proxy: agent sends "run X using secret Y", proxy executes + injects secret, returns result only
- Must be live before builder agents deploy
## June 20 Session 2 (continuation)
- Sandbox Vault auto-unseal deployed: vault-sandbox-unseal.sh + vault-sandbox-watch-unseal.sh + 2 systemd services, unsealed and verified
- Sandbox Vault AppRole mirror: 9 policies + 5 roles recreated from production (claude-code, claude-policy, n8n, n8n-outreach-policy, n8n-policy, n8n-rotation-policy, n8n-scheduling-policy, n8n-server01-policy, nextcloud-init)
- Production Bitwarden audit: 294 items total, 53 infra items identified (all username/password, some with notes)
- Sandbox Vaultwarden population BLOCKED: bw CLI 2026.5.0 WASM crypto error on create — use Vaultwarden REST API next session (id=70)
- sudo bridge NOT on server-01: server-01 has NOPASSWD:ALL; plan = deploy bridge first (Phase 2), then disable NOPASSWD
- ids added: 69 (Vaultwarden Version Monitor, p16), 70 (Sandbox Vaultwarden Seeder, p17)
- feedback_bw_cli_vaultwarden_create.md added to docker MEMORY.md
## June 20 Session 3 (continuation)
### Sandbox Vaultwarden — 53/53 dummy ciphers seeded (DB confirmed)
- Root cause of login failure: Bitwarden-Client-Version header required by Vaultwarden 1.36.0
- Original akey was encrypted with unknown master key (registration vs unlock password mismatch)
- Fix: reset sandbox@test.local password_hash + akey in postgres-sandbox via /tmp/vw_reset_and_seed.py
- Script: derives keys from u8X_P_zlGBypRUXYq5iGIg + PBKDF2/HKDF, generates fresh user_sym_key,
updates DB directly, logs in via REST API, POSTs 53 AES-256-CBC encrypted ciphers
- Runs via: `docker run --rm --network n8n-sandbox_default -v /tmp/vw_reset_and_seed.py:/tmp/script.py bitwarden-bridge:sandbox sh -c 'pip install cryptography psycopg2-binary --quiet && python3 /tmp/script.py'`
- Script at /tmp/vw_reset_and_seed.py on server-01 (also /tmp/vw_reset_and_seed.py locally)
- 53 ciphers confirmed: `SELECT COUNT(*) FROM ciphers WHERE user_uuid='896b5bbb-...'` → 53
- Bridge unlocks successfully with new password on every restart
### BLOCKER: bw list items WASM crash (same as bw create)
- Error: "Invalid key, throwing away stored keys" × 2, then "invalid type: unit value, expected a valid string"
- Empty vault worked; non-empty vault fails — bw CLI 2026.5.0 can't decrypt Vaultwarden 1.36.0 ciphers
- Bridge /items returns 500 error; ciphers ARE in DB but not serveable via bridge
- Fix options (next session, pick one):
1. Pull updated vaultwarden/server:latest (Docker pull currently broken due to daemon instability on server-01 — containerd-based Docker 29.x; images intact at /var/lib/containerd 26G)
2. Rebuild bitwarden-bridge:sandbox with newer bw CLI version
3. Patch bridge /items endpoint to use Vaultwarden REST API instead of bw CLI
- /tmp/vw_reset_and_seed.py script is reusable — if Vaultwarden is updated, just re-run to re-seed after container recreate
### Docker daemon incident (unrelated to our work)
- All containers exited during session — Docker 29.x daemon crash (not caused by our actions)
- Images are in /var/lib/containerd (26G), not /var/lib/docker/overlay2 (new storage model)
- Stack brought back up via: `cd /opt/appdata/docker/docker-compose/server-01 && docker compose up -d`
- Compose file location confirmed: /opt/appdata/docker/docker-compose/server-01/docker-compose.yml
### id=57 — COMPLETE ✅ (June 20 Session 5)
All sandbox prereqs done. Sandbox mirrors production.
**Completed in Session 5:**
- Bridge switched to Bitwarden cloud (megafreeman12@proton.me) — unlocks cleanly, /items returns 53
- Compose updated: BW_SERVER and NODE_TLS_REJECT_UNAUTHORIZED removed; vaultwarden-sandbox depends_on removed
- 53 dummy items seeded (dummy-infra-01 through dummy-infra-53)
- sandbox Vault secret/bitwarden updated with new master_password
- N8N sandbox credentials created: postgres-sandbox, vault-sandbox, n8n-internal-sandbox, Bridge API Key (Sandbox)
- n8n_agent_worker postgres role — api_business DB, scoped to automation_ideas + agent_test_results
- Credential in production Vault: secret/postgres/n8n-agent-worker
### id=51 — DEPLOYED ON PRIMARY ✅ (June 22 session 3)
Container running healthy at `https://secrets-proxy.reverseproxyserver.net` on primary.
Coolify service UUID: `ilus0cfdkheipodw1viurg1d`
**June 22 session 3 facts:**
- Coolify API allowed_ips was `172.16.16.1/32` — cleared to allow API access (set to empty string in instance_settings)
- Server-01 service (lv3xeu2manuimle458wbm9u9) stopped and deleted ✅
- Sandbox Vault and bridge now LAN-exposed on server-01: vault=192.168.1.90:8201, bridge=192.168.1.90:8083 (port bindings added to Coolify DB + sandbox compose file)
- secrets-proxy Vault AppRole created in prod (role: 3bf7b8a6-9e99-ca78-8e69-517881e26ea5) — creds at secret/proxy/vault-approle
- secrets-proxy Vault AppRole created in sandbox (role: f21416b3-2f3a-0461-fa38-d453a4e32865) — creds at secret/proxy/vault-approle-sandbox
- secrets_proxy DB user created in api_business (INSERT on proxy_executions only) — creds at secret/postgres/secrets-proxy
- NTFY secrets-proxy-bot created (topic: secrets-proxy-notifications, token at secret/ntfy/secrets-proxy-bot)
- Coolify bug: env vars for custom-image services can only be set via PATCH /api/v1/services/{uuid}/envs with key+value (not POST)
- Coolify DB YAML serializer mangles CMD-SHELL healthcheck single quotes (`'``''`) — fixed by using CMD array form in healthcheck
- psql heredoc via `docker exec ... << 'SQL'` doesn't work — use `docker cp` + `psql -f` instead
- pull_policy: never removed from docker-compose.yml (was server-01 workaround, no longer needed on primary)
- docker_compose_raw in Coolify DB must be updated via `docker cp` + `psql -f` for reliability
**June 22 session 4 facts:**
- E2E tests all passed: bridge_health_check (sandbox+prod), n8n_workflow_list (12 workflows), vault_secret_exists, vault_secret_write ✅
- proxy path format: callers pass RELATIVE path (e.g. `n8n`, `e2e-test`) — proxy prepends `secret/data/` automatically
- params go inside a `params: {}` object in the POST body (not at top level)
- iptables DNAT rule persisted: /etc/iptables/rules.v4 saved, /etc/sysctl.d/99-local-routing.conf written ✅
- iptables-persistent + netfilter-persistent already installed
- Used sudo-bridge (port 8082 on 192.168.1.88) with new bash -c allowlist entries
- POST /allowlist + GET /allowlist endpoints added to sudo-bridge ✅
- Validates tier (1/2), rejects danger-pattern matches, 409 on duplicate
- Writes to allowlist.json on disk (daemon reloads per-request — no restart needed)
- Audits to JSONL + Postgres, sends NTFY notification
- New image built, pushed to gitea.local, Coolify start called via proxy shell + env_secret injection
- sudo-bridge port: 8082 on 192.168.1.88 (LAN), container name: sudo-bridge-drbjegv07256ki2lpfyr00n8
- Coolify API from within Docker network: http://172.16.16.30:8080/api/v1/ (not 172.16.16.35)
- Coolify token: vault://coolify#api_key (field name is api_key)
- To call Coolify API from proxy: use /shell with env_secrets: {"TOKEN": "vault://coolify#api_key"}
- sudo-bridge image rebuild pattern: build → push to gitea.local → stop container → POST /api/v1/services/{uuid}/start
**June 22 session 4 (continued) facts:**
- Shell E2E all passed: read (sandbox_exit:0), write (notify+exec), destructive (NTFY approval→exec) ✅
- proxy-sandbox-mirror image: built from /secrets-proxy/mirror/Dockerfile — must exist on HOST (proxy calls host Docker daemon via socket)
- Dockerfile fix: docker.io → docker-cli (docker.io with --no-install-recommends on Debian Trixie does not install binary)
- NTFY bug: _ntfy() was hardcoding URL to secrets-proxy-notifications regardless of topic — fixed with topic param
- NTFY ACL: secrets-proxy-bot needed write-only on secrets-proxy-approvals; Smoked5003 needed read-only — both added via ntfy CLI in container
- secrets_proxy DB user: needed SELECT on proxy_executions for ON CONFLICT DO NOTHING — granted
- Coolify token plaintext: 2|95eQySElT9uQpTXqDACWq1z9kyOaySZOZP8sBxyJaebc2bbe (Sanctum format: id|plaintext)
- Coolify "Service is already running" after stop+rm: UPDATE service_applications SET status='stopped' WHERE service_id=... then POST /start
- 524 on destructive commands: Cloudflare kills long-poll at ~100s; approval must be tapped quickly; command still runs if approved before timeout, response just lost
**NEXT SESSION — in order:**
1. id=51 Secrets Proxy — sandbox precondition scaffolding
- Problem: sandbox dry-run is meaningless if the required state doesn't exist (e.g. file to delete isn't there, service isn't running)
- Feature: before running command in proxy-sandbox-mirror, classify what the command needs to be present, check if it exists, and create/set it up if missing — so the sandbox test is a faithful replica of what would happen on the host
- Examples: `rm /tmp/foo` → create /tmp/foo first; `systemctl stop nginx` → start nginx in sandbox first; `iptables -D` → add the rule first
- Classification must handle: file existence, directory existence, running process/service, iptables rules, sysctl values
- After scaffold → re-run command → result shown in NTFY approval body
2. id=24 Agent Builder Agent + id=67 N8N Builder Agent
- Bitwarden bridge LAN-exposed: 192.168.1.88:8083 ✅
### June 21 session 2 facts for next session:
- server-01 Coolify UUID: hvzbj1gkqb5696s7cc9lcf8y
- Sandbox stack Coolify service UUID: d2celewbvh7e4fer77fcp9b5
- n8n-prod Coolify service UUID: h10eww4au274owpxozgizysh
- Traefik on primary binds to 127.0.0.1:80 only (LAN push workaround: docker save | ssh | docker load on primary then push)
- sudo_bridge DB: server_id column added to executions + allowlist_changes, default='primary'
- app.py needs: server_id='server-01' + [server-01] NTFY prefix variant for server-01 deployment
- sudo-bridge image: gitea.local/backtalk6858/sudo-bridge:latest (same image, different env vars)
- sudo-bridge-server01 Coolify UUID: o2kz1puml1mmneyiqd96mouj (port 8082, pull_policy:never)
- Vault secret created: secret/data/sudo-bridge-server01 (api_key: 443ea35b43c3e640aac3c57ed3aae06b8822ab6f)
- Host daemon running: /opt/appdata/docker/sudo-bridge/sudo_bridge_daemon.py (systemd, enabled)
- vaultwarden-sandbox REMOVED from sandbox stack (no longer needed — Bitwarden cloud dummy account used)
- app.py updated: SERVER_ID, _server_prefix(), server_id audit writes, User-Agent for Cloudflare
- NTFY approval action buttons use BRIDGE_EXTERNAL_URL=http://192.168.1.90:8082 (LAN only until Tailscale)
- NOPASSWD:ALL disabled on server-01 ✅ (June 21 session 3)
- Both bridges now on public URLs via Traefik + Cloudflare: sudo-bridge.reverseproxyserver.net + sudo-bridge-server01.reverseproxyserver.net
- Danger veto in daemon blocks rm /etc/* regardless of allowlist — /etc sudoers removal done manually
- NEXT: id=51 Secrets Execution Proxy (grill-me DONE June 21 session 4 — build next session)
## June 20 Session 4 (continuation)
### Docker 29.6.0 crash — diagnosed and fixed
- Root cause: nala upgrade swept in Docker 29.6.0 which has SIGSEGV null pointer dereference bug in HTTP transport during docker pull
- Fix: downgraded to 29.5.3, pinned with `apt-mark hold docker-ce docker-ce-cli docker-ce-rootless-extras`
- Rule: never run nala/apt upgrade without holding docker-ce first
### bw CLI + Vaultwarden — permanently abandoned
- bw CLI 2026.4.1 AND 2026.5.0 both crash against Vaultwarden 1.36.0 with `orgKeys null` TypeError
- Root cause: Vaultwarden returns null for orgKeys in sync response for personal vaults; bw CLI expects {}
- Decision: drop Vaultwarden sandbox, use Bitwarden cloud dummy account (same as production)
- Dummy account created: megafreeman12@proton.me, master_password=Infra6746Dummy$
- All credentials stored at secret/sandbox/bitwarden in production Vault (4 fields: email, master_password, client_id, client_secret)
- Sandbox bridge Dockerfile.sandbox moved to /opt/appdata/docker/docker-compose/server-01/bitwarden-bridge/ (pinned bw CLI 2026.4.1 — moot now but kept for reference)
- bridge:sandbox image rebuilt and pushed to gitea.reverseproxyserver.net (still has bw CLI 2026.4.1)
## June 22 (Monday) — Builder Agents (extended session)
Build, test, and push both builder agents to production. Work as long as it takes.
- id=24 Agent Builder Agent (claude_agent + script types)
- id=67 N8N Builder Agent (n8n_automation types)
- Prerequisites: id=51 live, id=57 FULLY complete (sandbox mirrors production)
## Priority queue summary (as of 2026-06-18)
- p1: id=51 (manual), id=57 (manual) — infrastructure foundation
- p2: id=24, id=67 — builder agents (built in sessions, build everything else)
- p3: id=54 NTFY Provisioner, id=58 Human Action Gate
- p4: id=25 Cost Intelligence, id=38 Credential Emergency Rollout
- p5: id=3 Secrets Rotation, id=8 Vault Token Audit, id=52 Session Wrap-up
- p6: id=37 N8N Log Scanner, id=40 Coolify UUID Monitor
- p7: id=62 Monthly Security Audit, id=63 Security Patch Handler
- p8p17: non-blocked pending/ready items
- p20p29: all blocked items (builder agents skip these)
## Update instructions
Update at the end of every agent-builder session. Keep agent status, key decisions, and prereq checklist current.