Replace manual JSON-export-and-drag-into-chat with a Push Live button on every dashboard. End-to-end loop closed 2026-05-10 22:44 BST. Transport cut over to Cloudflare Tunnel 2026-05-17 (Phase 7) — new public hostname inbox.nutritionalproducts.org/jobs + x-api-token auth. Original test (kept for historical record): POST /inbox-api/jobs → HTTP 200 → client_ip=100.100.216.69 source=tailnet → file landed in F:/Agentic-OS/inbox/creative-review/. Session 25 (original) + Session 49B (Phase 7).
This plan page is the historical record of the v5 design (Coolify Traefik → private Tailnet → FastAPI). The current production architecture is the Phase 7 cutover: browser POSTs cross-origin to inbox.nutritionalproducts.org/jobs (Cloudflare Tunnel) with x-api-token → cloudflared Windows service → FastAPI on localhost:7392. Same FastAPI server, same envelope contract, same Claude inbox folder — just a different public ingress (2 hops instead of 4, removed the 502'ing proxy chain).
Current SOPs: Inbox over Cloudflare Tunnel (Phase 7 rebuild recipe) · Original Push Live Inbox SOP (Phases 1-3 + 6-8 still apply unchanged) · Dashboard SOP — Build a New Dashboard v2.1 at F:/Agentic-OS/reference/it-sops/dashboard-sop.md (canonical for new dashboards — markdown only; triggers: "Build me a new dashboard" / "dashboard SOP").
Today, every Command Centre dashboard already exports a self-describing JSON envelope (the contract banked in session 25 / commit 5a35da1, documented at feedback_self_describing_exports.md). Today's flow:
Dashboard → click "Export JSON" → download → drag into Claude chat
→ Claude reads _instructions_for_claude → runs the listed command → done
The new flow keeps the envelope unchanged. Only the handoff step is automated:
Dashboard → click "Push Live" → POST same envelope to local server (via Coolify proxy + Tailnet)
→ Server writes to F:/Agentic-OS/inbox/<dashboard>/<timestamp>.json
→ Claude (next active session) reads inbox → processes via _instructions_for_claude → archives
| Iter | Approach | Why rejected |
|---|---|---|
| v0 | Today's flow — manual export, drag into chat | Works but every bank requires user friction. |
| v1 | CF Worker + KV with 60-second polling on local PC | Too much infra. Needs new KV namespace, Worker code, polling daemon. Latency. |
| v2 | CF Worker + Tailscale push to local | Worker still adds maintenance overhead. Tailscale alone solves reachability. |
| v3 | Tailscale Funnel direct — expose local PC publicly via tailnet edge | Public exposure of local machine. Unnecessary because Coolify VPS is already on Tailnet. |
| v4 | Coolify proxy + FastAPI server with per-op-type handler dispatcher + universal client module across 7 dashboards | Calum: "you're overcomplicating it because for every dashboard there was just a simple JSON export and that's all you need to do". The envelope IS the protocol — no handler registry needed. |
| v5 | Coolify proxy + FastAPI single endpoint that writes JSON to inbox folder; Claude consumes the existing envelope when next active | LOCKED. Smallest possible change to ship the desired UX. ~7 files, mostly small. |
| File | Action | Approx. size | Purpose |
|---|---|---|---|
F:/Agentic-OS/tools/inbox_server.py | NEW | ~60 lines | FastAPI single endpoint POST /inbox/jobs. Bind to Tailscale interface only. Source-IP allowlist (Tailnet CGNAT). Write body to disk. |
F:/Agentic-OS/tools/setup_inbox_server_task.bat | NEW | ~20 lines | Task Scheduler entry — start at boot, restart on crash. |
F:/brainzyme-git/_shared/inbox-client.js | NEW | ~30 lines | Tiny shared module: pushToInbox(envelope) helper. Imported by every dashboard. |
Each dashboard's index.html | MODIFY | +5 lines each | Add or rename "Push Live" button. Swap downloadJson() handler for pushToInbox(). Identical JSON payload — nothing else changes. |
| Coolify reverse-proxy (Traefik labels) | MODIFY | 2 labels | Add route /inbox-api/* → http://100.127.50.113:7392/inbox/*. |
F:/Agentic-OS/reference/services/tailscale.md | NEW | ~80 lines | Service contract per Connections Registry rule. |
F:/Agentic-OS/reference/connections-registry.md | MODIFY | +1 row | New row for Tailscale. |
CLAUDE.md | MODIFY | +5 lines | Startup hook: scan F:/Agentic-OS/inbox/ on session start; process pending files via _instructions_for_claude. |
_format field plus _instructions_for_claude already tells Claude what to do.tools/ingest_vault_export.py) is the handler._version).STATE_KV stays scoped to the quiz email relay.F:/Claude Root/memory/feedback_self_describing_exports.md — banked in session 25 (commit 5a35da1, 11 April 2026), applied in session 38 (Shopify Action Centre).C:/Users/PC/.claude/projects/F--Agentic-OS/memory/feedback_dashboard_inbox_push_live.mdexportVault() in F:/brainzyme-git/google-ads-audit/v4.0-creative/competitors.htmlF:/Claude Root/tools/ingest_vault_export.pysrv843884 (Coolify VPS, 100.100.216.69) · ai-workstation-gf (this PC, 100.127.50.113)| Phase | Status | Detail |
|---|---|---|
1. tools/inbox_server.py | DONE | FastAPI single-endpoint, source-IP allowlist (Tailnet CGNAT 100.64.0.0/10 + loopback), envelope validation, dashboard slug whitelist, writes to F:/Agentic-OS/inbox/<dashboard>/<timestamp>.json. Smoke-tested locally: POST /inbox/jobs with smoke envelope → HTTP 200 → file landed at inbox/_test/20260510T204622Z_brainzyme_smoke_test_export.json. |
2. tools/setup_inbox_server_task.bat | DONE | Task Scheduler entry: BrainzymeDashboardInbox, schedule ONLOGON, RL HIGHEST. User to run once (not run yet) to register the boot-startup. Logs to F:/Agentic-OS/logs/inbox_server_task.log. |
3. _shared/inbox-client.js | DONE | Three exports: pushToInbox(envelope, opts) · pushAndToast(...) · createInboxUndoStack(storageKey). Validates envelope before sending. Returns rejection on timeout (10s default). |
4. reference/services/tailscale.md | DONE | Tier 2 service contract per Connections Registry rule. Documents 4 tailnet machines, 3 users, source-IP allowlist pattern. Registry row added. |
5. CLAUDE.md startup hook | DONE | New section "Dashboard Inbox check" added to Returning Mode. On session start, scan F:/Agentic-OS/inbox/, execute each pending file's _commands[0], archive to _processed/. |
| 6. Memory + index pointer | DONE | New: feedback_dashboard_inbox_push_live.md. MEMORY.md updated with index entry. |
7. Coolify reverse-proxy /inbox-api/* | DONE | 9 Traefik labels added to Container Labels textarea on s8sk0ks4gk40kwsgco480cgg. Untick "Readonly labels" first; tick "Escape special characters" to protect bcrypt $ chars. Recipe banked at reference/services/coolify.md + feedback_coolify_custom_labels.md. |
| 7.5. Windows Firewall rule (TCP 7392 from 100.64/10) | DONE | Self-elevating bat at tools/setup_inbox_firewall.bat. Scoped to Tailnet CGNAT range only — never publicly exposed. |
| 8. Wire creative-review Push Live button | PENDING | Drop-in: add <script src="../_shared/inbox-client.js"></script> to creative-review/index.html; add a "Push Live" button next to "Export Vault" that calls pushAndToast(envelope, { dashboard: 'creative-review' }). Same envelope as exportVault(). |
| 9. E2E test through Coolify | DONE 22:44 BST | Full loop closed. Browser-style request → Cloudflare → Coolify Traefik → private Tailnet → Windows Firewall → FastAPI → JSON written to disk. POST /inbox-api/jobs → HTTP 200 · client_ip=100.100.216.69 source=tailnet · file size 497B at F:/Agentic-OS/inbox/creative-review/. Test script: .tmp-drive-pull/e2e_test.py. |
Cloudflare here is DNS + CDN + WAF only — not a data store. The domain apps.nutritionalproducts.org resolves to a Cloudflare edge IP, so every request to that hostname hits Cloudflare first regardless of architecture. Cloudflare then proxies through to the Coolify VPS at 168.231.115.117.
No KV involvement. v5 deliberately rejected the Cloudflare Workers KV pattern (it was v1 in the rejected-iterations table above). Workers KV is only used by the separate quiz email relay Worker (white-flower-17a2-brainzyme-reviews), not by this inbox.
Cloudflare adds free TLS, edge caching for static dashboards, WAF, and DDoS protection at zero infra cost. The one rough edge we hit during testing: Cloudflare's default WAF flagged the Python urllib User-Agent (error 1010); a real browser doesn't trigger this. If you ever need to test programmatically, use a browser-like User-Agent header.
Open http://168.231.115.117:8000/project/a0g0sscgcoo4cow44ckcok00/environment/vc8048wogo00c88gw4wk0g0k/application/s8sk0ks4gk40kwsgco480cgg → Configuration → Advanced (or wherever Custom Labels live) and add the following Traefik labels:
traefik.http.routers.inbox-api-s8sk0ks4gk40kwsgco480cgg.rule=Host(`apps.nutritionalproducts.org`) && PathPrefix(`/inbox-api`)
traefik.http.routers.inbox-api-s8sk0ks4gk40kwsgco480cgg.entrypoints=https
traefik.http.routers.inbox-api-s8sk0ks4gk40kwsgco480cgg.tls=true
traefik.http.routers.inbox-api-s8sk0ks4gk40kwsgco480cgg.middlewares=inbox-strip,http-basic-auth-s8sk0ks4gk40kwsgco480cgg
traefik.http.routers.inbox-api-s8sk0ks4gk40kwsgco480cgg.service=inbox-backend
traefik.http.middlewares.inbox-strip.stripprefix.prefixes=/inbox-api
traefik.http.services.inbox-backend.loadbalancer.server.url=http://100.127.50.113:7392
traefik.http.services.inbox-backend.loadbalancer.passhostheader=true
Verify after deploy: curl -i -u admin:correct-horse-battery-staple-success https://apps.nutritionalproducts.org/inbox-api/health should return 200 with JSON containing "client_ip":"100.100.216.69" (the Coolify VPS Tailnet IP).
Once verified, run F:/Agentic-OS/tools/setup_inbox_server_task.bat on this PC to register the Task Scheduler entry, and the system is fully live.