Starting a bottle recreates the orchestrator, dropping already-running bottles' egress tokens (403s) #381
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Repro
./cli.py startbottle A. Confirm it can make an authed egress call (e.g. its LLM provider) successfully../cli.py startbottle B (a second, parallel agent).egress: route for '<host>' declared auth but env var 'EGRESS_TOKEN_0' is unset.A now needs a full restart to work again — it lost its injected credential permanently, even though nothing about A changed.
Diagnosis
OrchestratorService.ensure_running()(bot_bottle/orchestrator/lifecycle.py) unconditionally recreates the fixed-namebot-bottle-orchestratorcontainer on every call — added in a prior fix ("recreate the orchestrator container on ensure_running") so bind-mounted code changes take effect, since the orchestrator's Python process never reloads its bind-mounted source.launch_consolidated()callsservice.ensure_running()on every single bottle launch. So starting bottle B tears down and restarts the orchestrator container that was already serving bottle A.The orchestrator's per-bottle egress auth tokens live only in an in-process dict (
Orchestrator._tokensinbot_bottle/orchestrator/service.py), keyed bybottle_id, deliberately never persisted to disk (secrets shouldn't hit disk, per the token-injection design). Recreating the container starts a fresh Python process with an empty_tokensdict — wiping bottle A's token even though only bottle B is launching. The registry DB (bottle metadata, policy, identity tokens) does persist across the restart, so A still resolves and looks "active" — it just has no injected credential anymore, so its next authed route 403s.Root cause:
ensure_runningconflates "is the orchestrator container running" with "is it safe to recreate" — it recreates even when nothing about the source changed, unconditionally sacrificing every other active bottle's in-memory state.Fix
Only recreate the orchestrator container when its bind-mounted source has actually changed (content hash of the
bot_bottlepackage, stored as a container label and compared on eachensure_running, mirroring the existing image-staleness check already used for the gateway container). A bottle launch that isn't accompanied by a code change now leaves a healthy orchestrator — and its other bottles' in-memory tokens — alone.