Deploys that run without your computer
Deploys used to need one specific computer — the one that had claimed the operator role — because the permission, the pause switch and the record of what shipped were all files on it. Moving a deploy onto Google Cloud Build removes that computer, so this adds a second allowed asker (the build itself, proved rather than claimed), copies your one pause switch to a place a container can read, and keeps the record somewhere that outlives the machine. Nothing changes on your own computer: until the cloud side is provisioned, every cloud build refuses loudly and every ordinary deploy behaves exactly as it did.
What it is
Every deploy asks one question before it ships: is the asker allowed, is the commit on master with a green verdict, and is no owner hold on. That question lives in one module, deploy-safety.mjs, and this work adds a second sanctioned answer to its first leg:
| Who is asking | How it is proved |
|---|---|
operator-computer |
the app on this computer says it claimed the role — unchanged |
cloud-build |
Google's own machine metadata names the deploy road's service account |
A build is proved, not taken at its word. Two environment variables anyone can set are not
enough to identify a build — that would let any process on any machine, including this one, remove the
operator lock by setting them. So the build must also be running as
amc-deployer@agentmc-shares.iam.gserviceaccount.com, read from the metadata service a plain shell
cannot serve. A run that only looks like a build is refused, and never quietly treated as an
ordinary deploy.
Your hold stays one switch. On this computer the hold is the global pause in the ops scheduler. When you flip it, the app copies that state to a small object in cloud storage, and at every start it checks the object against the pause ledger — so a hold you set before this shipped, or lifted while the app was closed, still ends up right. A build reads that object before shipping and stops if it is set. If the object cannot be read at all, the build stops too: the safe direction is always "assume paused".
The record follows the deploy. Both deploy ledgers now read the rows a build leaves behind, so "what is production running, and who put it there" stays answerable even though no file on your machine saw the deploy.
Where to find it
- The allow/refuse decision — deploy-safety.mjs, with the cloud store itself in deploy-cloud-store.mjs.
- The deploy record — gateway-deploy-ledger.mjs for the gateway and backend, web-publish-ledger.mjs for the web units.
- The hold mirror — the ops scheduler, ops-task-scheduler.ts; the app never freezes while it writes.
- The rules — deploy-safety-contract.md.
- The off switch —
AMC_DEPLOY_CLOUD_ROAD=0in the code, and the trigger's owndisabledflag for the operating switch. Both default to ON, so there is nothing to configure.
How it behaves
- A cloud deploy that cannot prove itself refuses
not-a-sanctioned-deploy-principal, naming why. It is never passed to the ordinary-deploy rule as a fallback. - A cloud deploy is held exactly like yours — the same hold, the same reason shown back to you.
- Your own deploys are untouched. An unreadable cloud store costs a missing cloud row, never a refused deploy on this computer: the pre-deploy checks still judge exactly what they judged before.
- Only the newest rows are read, per unit, in two cloud calls — so a busy unit can never push a quiet one out of the answer and make it look like it was never deployed.
- It creates nothing. The storage bucket, the service account and the triggers come from a separate provisioning step. Before that runs, cloud deploys refuse; nothing else changes.
- One road is not covered yet. The web publisher still carries its own "is this the operator computer" check, so that road refuses before it reaches the shared question.
Related
- deploy-safety-contract.md — the rules and the full census of every deploy road.
- deploy-systems-inventory.md — every deploy system, in one map.
- firebase-hosting-from-this-box.md — the web publish road this store's second half records.
- deploy-profiles.md — where the desktop deploy gets its settings.
Last verified 2026-10-26