Stop treating a service that answers as a service that is current - #137
Merged
Merged
Conversation
`start.sh` skipped work for anything already up: a Bot container answering its health route was left out of `docker compose up` entirely, and a server answering `/api/capabilities` was never restarted. Both read as optimisations. Both are the same mistake, which is that answering proves a process is alive and says nothing about whether it still agrees with the deployment. So a secret minted by this script never reached the things that use it. `AGENT_TOOL_TOKEN` is generated here and written to .env; the container kept the previous one because it was healthy, and the server kept its own because it had read .env once at startup. Every callback a Bot made was refused, which reaches a person as "no files were returned" rather than as an error, about a Drive that has the files. Two changes, matching the two ways it went wrong. Bot services are always handed to compose, which is declarative and is the thing that can actually compare a container's configuration. The server is restarted when this run minted a secret, because nothing can diff a process that read its environment an hour ago. Driven: rotate the token with the whole stack up, re-run, and a Bot returns real Drive filenames again. Before, it reported none.
davidmckayv
requested review from
MikeRyanDev,
guidovizoso and
tylerslaton
as code owners
August 22, 2026 02:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The drift #136 made visible. That change put the failure on the audit trail; this one stops producing it.
The mistake, twice
start.shskipped work for anything already up:Both read as optimisations. Both are the same error: answering proves a process is alive and says nothing about whether it still agrees with the deployment.
A container left out of
compose upis never compared against its configuration, so compose cannot notice its environment changed. A server that is answering was started earlier and read.envonce, then.What it cost
AGENT_TOOL_TOKENis minted by this script and written to.env. On a run where the stack was already up, neither side got it: the container kept the previous value because it was healthy, the server kept its own because it had already read the file.Every callback a Bot made was then refused. Which does not surface as an error — it surfaces as:
about a Drive containing three matching files. That is how this was found, and it took an hour, because before #136 the refusal left no row anywhere.
The two fixes
Bot services always go to compose.
docker compose up -dis declarative and is the only thing that can actually compare a container's configuration against the file. Naming them all costs a comparison and buys the guarantee.The server is restarted when this run minted a secret. Nothing can diff a process that read its environment an hour ago, so the trigger is the one fact the script knows for certain: it generated a secret just now. A
SECRETS_ROTATEDflag is set whereverMANAGED_AGENT_TOKENorAGENT_TOOL_TOKENis minted, and the server block consults it before deciding a running server is fine.Deliberately not "restart the server every run". It is the API; bouncing it when nothing changed is a cost with no reason. The flag is the honest signal.
Proof
Rotation, with the whole stack up. Cleared
AGENT_TOOL_TOKEN, re-ranstart.sh:Then, in the browser, the same question that had been answered "no files":
audited as
mcp.call_succeeded,reachedAsequal to the asking user.Drift, before the fix, reproduced deliberately: rotate the token in
.envand re-run. The container stayed stale, and the trail showedmcp.callback_refused | Not authorised.— which is #136 working, and is what made this diagnosable in seconds rather than an hour.bun run testbun run typecheckbun run format:checkThe cost, stated
Bot containers now go through compose on every run, so a run that rebuilds an image recreates them: about five seconds, and it bounces an idle Bot.
supervisoralready behaved this way, so this makes the three consistent with it rather than introducing something new.The alternative is what we had: fast re-runs that can leave a Bot holding a secret the deployment has stopped accepting, reporting empty results to whoever asks.
Tests
None.
start.shis a shell entry point with no harness in this repo, and building one is a larger change than this. It is driven above instead, in both directions.