Surfaced empirically on 2026-07-31 while standing up a containerized MC + KB to prove the #16167 endpoint before the data move. Getting a working local, isolated, authenticated, loopback-exposed plane required hand-writing a seventh compose file in a scratchpad. That hand-written override is the finding, not the fix.
The endpoint proof itself succeeded — initialize → 200, tools/list → 42 tools on /mc/mcp and 13 on /kb/mcp through 127.0.0.1:3102 with GitHub-PAT bearer auth. The friction is that reaching that state was not expressible in ai/deploy as it stands.
Live latest-open sweep at 2026-07-31T00:55Z: no open issue covers compose layering, base bootability, or an isolated proof plane.
The Problem
1. The base compose cannot boot the servers it defines.
ai/deploy/docker-compose.yml sets NEO_AUTH_MODEzero times. It mentions the variable in two comments (:95, :179) and never assigns it. AuthService fails closed on the Streamable HTTP transport with no auth installer, so kb-server and mc-server exit 1:
Error: AuthService: no Streamable HTTP authentication installer is configured;
configure an OIDC endpoint, a built-in auth.mode, custom authMiddleware, or trusted-proxy identity
at AuthService.setup (ai/mcp/server/shared/services/AuthService.mjs:125)
Observed, not inferred: base + an override touching only image: and the ingress port produced exactly this on both services. Fail-closed is the correct server behaviour; the defect is that the canonical topology file ships a non-bootable default while owning all 7 named volumes.
2. Auth is re-decided per overlay instead of being a fragment.
Counting real key assignments (^\s*NEO_AUTH_MODE:), with the comment-only base as the positive control:
File
auth mode set
loopback 3102
live-plane bind
docker.sock
named volumes
docker-compose.yml
0
–
–
1
7
docker-compose.dev.yml
github-pat
–
–
1
–
docker-compose.local-agent-os.yml
github-pat
✓
2
2
–
docker-compose.parity-capture.yml
github-pat ×2
–
–
–
–
docker-compose.parity-ci.yml
local-bearer
–
–
–
–
docker-compose.test.yml
0
–
–
–
–
Five overlays, four independent auth decisions, one of them stated twice. There is no auth fragment to compose.
3. No configuration can prove the plane before it owns the data — the load-bearing item.
docker-compose.local-agent-os.yml welds four orthogonal concerns into one file: the auth profile, the loopback :3102 ingress, a bind of the live repo-root .neo-ai-data, and /var/run/docker.sock. You cannot take the loopback ingress without also taking the live plane.
The consequence shows up in the cutover runbook: ai/scripts/lifecycle/local-agent-os/README.md §5 ("Prove the replacement before resident release") runs after §3 has already mv'd the canonical plane. The only configuration that exposes a testable endpoint is the one that already owns production data, so the proof necessarily follows the irreversible step. On a machine holding ~94k memory rows that ordering is the wrong way round, and it is a property of the compose layout rather than of the runbook's author.
4. /var/run/docker.sock hardcoding — premise CORRECTED to documentation-only.
Corrected 2026-07-31 (comment, confirmed by @neo-gpt-emmy's independent container-plane evidence): the compose volume path resolves inside the Colima VM, where dockerd runs and /var/run/docker.sock exists — not on the macOS host. The orchestrator service does start as written under Colima; the live local-agent-os orchestrator container runs with the stock mount. The original "cannot start as written" claim was a host-path intuition applied to a VM-path contract.
The residual is real but documentation-only: a host-shell ls /var/run/docker.sock fails while the mount works, which is exactly the misread that cost a diagnosis round ("the Docker engine is down" — the engine was up, docker info → 29.2.1, context colima). The VM-vs-host path distinction belongs in the ai/deploy README.
5. Nothing tests bootability.
test/playwright/unit/ai/deploy/ParityPlaneVolumeScoping.spec.mjs gives ai/deploy coverage, but it asserts volume-scoping shape. A YAML-shape assertion cannot catch "this configuration cannot start" — which is why a non-bootable base survives a green suite.
The Architectural Reality
The concerns genuinely factor into four independent axes: auth mode, ingress exposure, data plane, host integration. Every existing file picks one point in that space and hardcodes it, so any unrepresented combination requires a new file. Six files today; the seventh was written tonight in a scratchpad; a contributor needing "isolated + public ingress" writes the eighth.
The base already models the isolated data plane correctly — 7 named volumes, no repo-root bind. That half is right and reusable; it is the auth and exposure axes that have no fragments.
Caddyfile.local-agent-os (loopback :8080, /mc/* → mc-server:3001, /kb/* → kb-server:3000) is already a clean standalone artifact and needs no change — evidence that the ingress axis separates cleanly.
The Fix
RE-SCOPED 2026-07-31 per the #16167 compliance course-correction. The immediate blocker is not a new Compose abstraction. Step 1 — unblock the migration: start the reviewed canonical Compose project with (a) stable project identity (project-name drift makes runtime service discovery see zero services; the production deployment pins it for exactly this reason), (b) Docker-owned named volumes as the data target, (c) no scratch override, (d) no bind of the pre-Docker checkout plane. The fragment-decomposition proposal below is preserved as analysis and proceeds only if still needed after the canonical relaunch — it must not compete with the compliance map's repair sequence.
Deferred decomposition sketch (post-relaunch material only):
Make the base bootable or explicitly abstract. Either ship a safe default auth mode, or fail with a directed error naming which fragment to add — not a stack trace from AuthService.
Extract auth fragments (github-pat, local-bearer, OIDC, trusted-proxy) so no overlay re-states a mode.
Extract ingress fragments: loopback (127.0.0.1:3102) and public (443).
Extract data-plane fragments: isolated named volumes, and canonical host bind.
Derive the Docker socket from the active context, or move it to a host-docker fragment, so Colima and Docker Desktop both work.Corrected 2026-07-31 — documentation-only: the stock mount already works under both (in-VM path); document the VM-vs-host distinction in the ai/deploy README instead of changing code or compose.
Add a bootability test that starts each supported composition far enough to prove the transport installs — an invocation, not a shape assertion.
Reorder the runbook's proof once an isolated composition exists: prove on named volumes, then swap one fragment to adopt the real plane. base + auth + ingress-loopback + plane-volumes is exactly the stack that worked tonight.
Contract Ledger
Target Surface
Source of Authority
Proposed Behavior
Fallback
Docs
Evidence
ai/deploy/docker-compose.yml
this ticket
boots the transport standalone, or fails with a directed fragment-naming error
never a bare AuthService stack trace
ai/deploy README
a test that boots base alone and asserts one of the two outcomes
auth / ingress / plane / host fragments
the four observed axes
each sets exactly one concern; no overlay states another's
an unrepresented combination is expressible without a new bespoke file
composition table in docs
every existing scenario reproduced as a composition
docker socket VM-vs-host note
corrected 2026-07-31: documentation-only — the stock in-VM mount works under Colima and Docker Desktop
README documents that the volume path resolves in the VM, so a failing host-shell ls is not evidence of a broken mount
–
ai/deploy README
live: local-agent-os orchestrator container running under Colima with the stock mount
supported compositions
the composition table
each boots far enough to install its transport
a broken composition fails CI, not a cutover
–
bootability test per composition
Acceptance Criteria
Step 1 (re-scoped 2026-07-31, gates everything else): the reviewed canonical Compose project boots with pinned project identity and Docker-owned named volumes — no scratch override, no checkout-plane bind — and runtime discovery sees every service. Evidence: docker compose ls / labels showing the tracked config files only, plus the orchestrator's service-discovery receipt.
Base compose either boots the HTTP transport unaided or fails with an error naming the missing fragment. No bare AuthService trace. (Deferred: post-relaunch, only if still needed.)
No overlay sets NEO_AUTH_MODE; each composition selects an auth fragment. Verified by a source sweep with a positive control, since the comment-only base is exactly the case a naive grep miscounts.
An isolated-plane composition exists that exposes the loopback ingress without binding repo-root .neo-ai-data, and it is the documented pre-cutover proof target.
Every current scenario (dev, local-agent-os, parity-ci, parity-capture, test) is reproducible as a composition of fragments, each proven equivalent to today's behaviour.
(Corrected 2026-07-31 — documentation-only) The ai/deploy README documents the VM-vs-host docker.sock distinction: the stock mount resolves inside the Colima VM and the orchestrator starts as written; the original "cannot start under Colima / needs context-derived socket" AC is withdrawn.
A bootability test covers each supported composition by starting it, not by asserting YAML shape.
The #16167 runbook's proof step is re-sequenced to run on the isolated composition before any data move, or a successor records why it cannot be.
Net file count does not grow without the composition table documenting what each fragment owns.
Out of Scope
The #16167 cutover itself, its data move, and its wake receiver.
AuthService's fail-closed behaviour — correct as-is; this is about configuration not supplying it a mode.
Rewriting Caddyfile.local-agent-os; it already separates cleanly.
Kubernetes, Swarm, or any non-Compose target.
Avoided Traps
Adding a seventh tracked compose file. That is the symptom. Without extracted axes the next unrepresented combination just adds an eighth.
Counting comment mentions as settings.grep -c NEO_AUTH_MODE reports 2 for the base, both prose. The first version of the table in this ticket was wrong for exactly that reason; the AC requires a positive control.
Fixing the runbook's proof ordering alone. The ordering is downstream of there being no isolated composition to prove against; reordering prose without adding one changes nothing.
Treating the Colima socket as a local quirk. It is a hardcoded host assumption in a file that also ships to cloud.Corrected 2026-07-31: the "hardcoded host assumption" premise was itself the trap — the path is a VM-side contract that works under both engines. The durable lesson is the inverse: verify a mount path in the namespace that consumes it before calling it broken.
A shape-only bootability test. Asserting an auth key exists is not proof a transport installs — the defect being fixed here is precisely one a shape assertion missed.
Decision Record impact
aligned-with ADR 0014 (deployment topology and the container-plane / host-edge split — this makes its intended configurations expressible rather than changing them). No ADR is amended. If the fragment decomposition proves to have wider blast radius than one lane, this graduates to a Discussion before implementation.
Related
#16167 — the cutover whose §5 proof ordering this explains; the endpoint proof that surfaced all of it ran during that lane
#16205 — provisioning templates still emitting the retired stdio shape
#16197 — orchestrator lanes scheduled without regard to the authority profile; same family of "configuration says one thing, runtime does another"
Retrieval Hint: ai/deploy compose layering fragments base not bootable NEO_AUTH_MODE unset AuthService fail closed isolated plane proof before data move docker.sock colima VM path corrected bootability test
tobiu referenced in commit a249882 - "fix(ai): restore canonical local Compose target (#16206) (#16218) on Jul 31, 2026, 7:59 AM
Context
Surfaced empirically on 2026-07-31 while standing up a containerized MC + KB to prove the #16167 endpoint before the data move. Getting a working local, isolated, authenticated, loopback-exposed plane required hand-writing a seventh compose file in a scratchpad. That hand-written override is the finding, not the fix.
The endpoint proof itself succeeded —
initialize→ 200,tools/list→ 42 tools on/mc/mcpand 13 on/kb/mcpthrough127.0.0.1:3102with GitHub-PAT bearer auth. The friction is that reaching that state was not expressible inai/deployas it stands.Live latest-open sweep at 2026-07-31T00:55Z: no open issue covers compose layering, base bootability, or an isolated proof plane.
The Problem
1. The base compose cannot boot the servers it defines.
ai/deploy/docker-compose.ymlsetsNEO_AUTH_MODEzero times. It mentions the variable in two comments (:95,:179) and never assigns it.AuthServicefails closed on the Streamable HTTP transport with no auth installer, sokb-serverandmc-serverexit 1:Error: AuthService: no Streamable HTTP authentication installer is configured; configure an OIDC endpoint, a built-in auth.mode, custom authMiddleware, or trusted-proxy identity at AuthService.setup (ai/mcp/server/shared/services/AuthService.mjs:125)Observed, not inferred: base + an override touching only
image:and the ingress port produced exactly this on both services. Fail-closed is the correct server behaviour; the defect is that the canonical topology file ships a non-bootable default while owning all 7 named volumes.2. Auth is re-decided per overlay instead of being a fragment.
Counting real key assignments (
^\s*NEO_AUTH_MODE:), with the comment-only base as the positive control:docker-compose.ymldocker-compose.dev.ymlgithub-patdocker-compose.local-agent-os.ymlgithub-patdocker-compose.parity-capture.ymlgithub-pat×2docker-compose.parity-ci.ymllocal-bearerdocker-compose.test.ymlFive overlays, four independent auth decisions, one of them stated twice. There is no auth fragment to compose.
3. No configuration can prove the plane before it owns the data — the load-bearing item.
docker-compose.local-agent-os.ymlwelds four orthogonal concerns into one file: the auth profile, the loopback:3102ingress, a bind of the live repo-root.neo-ai-data, and/var/run/docker.sock. You cannot take the loopback ingress without also taking the live plane.The consequence shows up in the cutover runbook:
ai/scripts/lifecycle/local-agent-os/README.md§5 ("Prove the replacement before resident release") runs after §3 has alreadymv'd the canonical plane. The only configuration that exposes a testable endpoint is the one that already owns production data, so the proof necessarily follows the irreversible step. On a machine holding ~94k memory rows that ordering is the wrong way round, and it is a property of the compose layout rather than of the runbook's author.4.
/var/run/docker.sockhardcoding — premise CORRECTED to documentation-only.The residual is real but documentation-only: a host-shell
ls /var/run/docker.sockfails while the mount works, which is exactly the misread that cost a diagnosis round ("the Docker engine is down" — the engine was up,docker info→29.2.1, contextcolima). The VM-vs-host path distinction belongs in theai/deployREADME.5. Nothing tests bootability.
test/playwright/unit/ai/deploy/ParityPlaneVolumeScoping.spec.mjsgivesai/deploycoverage, but it asserts volume-scoping shape. A YAML-shape assertion cannot catch "this configuration cannot start" — which is why a non-bootable base survives a green suite.The Architectural Reality
Caddyfile.local-agent-os(loopback:8080,/mc/*→mc-server:3001,/kb/*→kb-server:3000) is already a clean standalone artifact and needs no change — evidence that the ingress axis separates cleanly.The Fix
Deferred decomposition sketch (post-relaunch material only):
AuthService.github-pat,local-bearer, OIDC, trusted-proxy) so no overlay re-states a mode.127.0.0.1:3102) and public (443).Derive the Docker socket from the active context, or move it to aCorrected 2026-07-31 — documentation-only: the stock mount already works under both (in-VM path); document the VM-vs-host distinction in thehost-dockerfragment, so Colima and Docker Desktop both work.ai/deployREADME instead of changing code or compose.base + auth + ingress-loopback + plane-volumesis exactly the stack that worked tonight.Contract Ledger
ai/deploy/docker-compose.ymlAuthServicestack traceai/deployREADMElsis not evidence of a broken mountai/deployREADMEAcceptance Criteria
docker compose ls/ labels showing the tracked config files only, plus the orchestrator's service-discovery receipt.AuthServicetrace. (Deferred: post-relaunch, only if still needed.)NEO_AUTH_MODE; each composition selects an auth fragment. Verified by a source sweep with a positive control, since the comment-only base is exactly the case a naive grep miscounts..neo-ai-data, and it is the documented pre-cutover proof target.ai/deployREADME documents the VM-vs-hostdocker.sockdistinction: the stock mount resolves inside the Colima VM and the orchestrator starts as written; the original "cannot start under Colima / needs context-derived socket" AC is withdrawn.Out of Scope
AuthService's fail-closed behaviour — correct as-is; this is about configuration not supplying it a mode.Caddyfile.local-agent-os; it already separates cleanly.Avoided Traps
grep -c NEO_AUTH_MODEreports 2 for the base, both prose. The first version of the table in this ticket was wrong for exactly that reason; the AC requires a positive control.Treating the Colima socket as a local quirk. It is a hardcoded host assumption in a file that also ships to cloud.Corrected 2026-07-31: the "hardcoded host assumption" premise was itself the trap — the path is a VM-side contract that works under both engines. The durable lesson is the inverse: verify a mount path in the namespace that consumes it before calling it broken.Decision Record impact
aligned-with ADR 0014(deployment topology and thecontainer-plane/host-edgesplit — this makes its intended configurations expressible rather than changing them). No ADR is amended. If the fragment decomposition proves to have wider blast radius than one lane, this graduates to a Discussion before implementation.Related
Origin Session ID: 0a7f5f1d-cf12-4698-984c-17b64eea5178
Retrieval Hint:
ai/deploy compose layering fragments base not bootable NEO_AUTH_MODE unset AuthService fail closed isolated plane proof before data move docker.sock colima VM path corrected bootability test