Goal
Make learn/agentos/cloud-deployment/ genuinely easy for a first real deployment. Backport the friction-tested lessons from the first real public cloud deployment (2026-06-02) into the canonical docs. The Day0Tutorial is a solid MVP but assumes too much and has no troubleshooting safety net — a first-timer hits a multi-layer wall the docs don't cover. (Capability framing only — no client/customer names.)
Scope — verified enhancements (each surfaced empirically this session)
- New "Connection troubleshooting" section — the full connect ladder, each error → what it means (which layer you cleared) → the fix:
-32000 Invalid Host — SDK DNS-rebinding host-allowlist (localhost-only by default); fix = the allowlist config (neo #12371, merged) or Caddy header_up Host.
Not Acceptable: Client must accept text/event-stream — client missing Accept: application/json, text/event-stream (it's an MCP endpoint, not a webpage).
302 → /oauth2/sign_in — oauth2-proxy auth gate.
Missing proxy identity header — trustProxyIdentity=true + Caddy strips client-supplied identity headers (anti-spoof).
Server not initialized — MCP requires the initialize handshake before tool calls.
- SSE response shape (
event: message / data:) is normal, not an error.
- Caddyfile is baked into the image → a Caddyfile edit needs
docker compose up -d --build caddy, not a restart (recurring gotcha — bit us twice).
- Ports are network-internal (only the ingress is host-published) → test via Caddy or
docker compose exec <server> node ./ai/scripts/diagnostics/mcpHealthcheck.mjs --url http://127.0.0.1:<port>; a host-side curl 127.0.0.1:<port> finds nothing.
- The two auth layers (oauth2-proxy cookie + MCP
trustProxyIdentity header) — an "auth model" callout so deployers stop fighting the wrong layer.
- Dev/no-auth test profile vs prod auth profile — a documented toggle (open Caddy +
trustProxyIdentity=false / static-identity injection) for first-time testing.
- Verify-from-outside recipe — the 2-step handshake curl (initialize → capture
mcp-session-id → notifications/initialized → tool call) with the Accept header; expect SSE.
- Empty-KB / first-ingest gap — a healthy KB shows
count: 0 until ingestion runs (the ingestion lane is off in the cloud-safe profile); how to trigger a first ingest so queries return results.
- Day0Tutorial hardening — Colima/macOS silent-mount gotcha (deploy dir must be colima-mounted),
docker-compose plugin wiring on macOS, compose -p <project> naming, "run ai:sync-kb exactly once before querying," tenant-identity-is-server-stamped (not the header), clone-mode custom-parser gap.
- Redeploy-survival checklist + operator-handoff template — backport the concrete versions proven on the friction-tested deployment (vs the canonical's vague Milestone 7).
Companion feature idea (separate ticket if wanted)
A GET /health auth-free liveness endpoint on the neo MCP server (TransportService only exposes /mcp today) — so external health checks don't need a session cookie.
Cross-refs / non-overlap
- The auth-specific login docs (PAT Bearer recipes) are covered by the auth epic sub #12380 — this ticket is the broader first-deployment easiness; don't duplicate the auth-login content.
-32000 Invalid Host host-allowlist substrate already merged: #12371.
Acceptance Criteria
- A first-timer can follow the docs through a real public deployment without hitting an undocumented wall.
- The troubleshooting section maps each connect-ladder error to its fix.
- Day0 covers the macOS/Colima + compose-plugin gotchas.
- Redeploy-survival + operator-handoff are concrete (backported), not vague.
- No client/customer names; capability framing.
Owner: @neo-opus-ada (this is the "Track 1" cloud-deployment-docs lane). One coherent docs PR (or a small number by doc-area only if genuinely too large).
Goal
Make
learn/agentos/cloud-deployment/genuinely easy for a first real deployment. Backport the friction-tested lessons from the first real public cloud deployment (2026-06-02) into the canonical docs. TheDay0Tutorialis a solid MVP but assumes too much and has no troubleshooting safety net — a first-timer hits a multi-layer wall the docs don't cover. (Capability framing only — no client/customer names.)Scope — verified enhancements (each surfaced empirically this session)
-32000 Invalid Host— SDK DNS-rebinding host-allowlist (localhost-only by default); fix = the allowlist config (neo #12371, merged) or Caddyheader_up Host.Not Acceptable: Client must accept text/event-stream— client missingAccept: application/json, text/event-stream(it's an MCP endpoint, not a webpage).302 → /oauth2/sign_in— oauth2-proxy auth gate.Missing proxy identity header—trustProxyIdentity=true+ Caddy strips client-supplied identity headers (anti-spoof).Server not initialized— MCP requires theinitializehandshake before tool calls.event: message/data:) is normal, not an error.docker compose up -d --build caddy, not a restart (recurring gotcha — bit us twice).docker compose exec <server> node ./ai/scripts/diagnostics/mcpHealthcheck.mjs --url http://127.0.0.1:<port>; a host-sidecurl 127.0.0.1:<port>finds nothing.trustProxyIdentityheader) — an "auth model" callout so deployers stop fighting the wrong layer.trustProxyIdentity=false/ static-identity injection) for first-time testing.mcp-session-id→notifications/initialized→ tool call) with the Accept header; expect SSE.count: 0until ingestion runs (the ingestion lane is off in the cloud-safe profile); how to trigger a first ingest so queries return results.docker-composeplugin wiring on macOS, compose-p <project>naming, "runai:sync-kbexactly once before querying," tenant-identity-is-server-stamped (not the header), clone-mode custom-parser gap.Companion feature idea (separate ticket if wanted)
A
GET /healthauth-free liveness endpoint on the neo MCP server (TransportServiceonly exposes/mcptoday) — so external health checks don't need a session cookie.Cross-refs / non-overlap
-32000 Invalid Hosthost-allowlist substrate already merged: #12371.Acceptance Criteria
Owner: @neo-opus-ada (this is the "Track 1" cloud-deployment-docs lane). One coherent docs PR (or a small number by doc-area only if genuinely too large).