Secrets
Values delivered into tmpfs inside a machine, and never readable back out.
GET /v1/machines/{id}/secrets names, versions and delivery state
PUT /v1/machines/{id}/secrets store or replace one
DELETE /v1/machines/{id}/secrets/{name} revoke one
GET /v1/secrets every secret name in the organization
await machine.secrets.set("GITHUB_TOKEN", token);
A stored value is never returned
There is no read route, and the response shape has no field a value could travel in. That is not an omission to be fixed later: a product that can hand a secret back is a product whose secrets are in every response log, error report and browser cache that ever touched one.
What you can read is the name, the version — bumped on every replacement, so you can tell a
change from a no-op — and delivered_at.
Names are shell-shaped
A name must match ^[A-Z_][A-Z0-9_]*$: an uppercase environment variable name. Accepting a name
a guest cannot export would store a secret that silently never arrives.
Values are up to 64 KB and are stored encrypted.
Delivery
Secrets land at /run/computer/secrets.env inside the machine, mode 0600. /run is a tmpfs on
every Linux the product ships, which is the entire point:
A disk capture cannot contain what was never on the disk.
So a snapshot, fork or template does not carry a secret. A machine that restarts takes delivery again from storage.
A login shell picks them up through a loader at /etc/profile.d/10-computer-secrets.sh. That
file is on disk and holds no secret — three lines that source the tmpfs file if it is readable.
Putting the values there instead would put them in every snapshot the machine ever takes.
const { stdout } = await machine.terminal.exec(["/bin/sh", "-lc", "echo $GITHUB_TOKEN"]);
delivered_at is null when a secret is stored and not yet delivered, which is the normal state
for one set on a machine that is not running — it lands on the next boot. A running machine takes
delivery within seconds.
Revoking
await machine.secrets.delete("GITHUB_TOKEN");
Removes it from storage and from the running machine. It cannot remove a copy the guest already made: a process that read the value and wrote it elsewhere still holds it. Revoking at the source is this product’s half; rotating at the issuer is yours.
Deployment requirement
Secrets need an encryption keyring configured on the deployment. Without one the API refuses with
unsupported_operation rather than storing a value in a form it could be read back from.
Rotation is appending a key version, never replacing one: the newest key seals new values and every older key stays available to open what it sealed. Dropping an old key does not rotate anything — it makes every value sealed under it permanently unreadable.
Secrets versus the browser profile
The two are deliberately opposite, and picking the wrong one is the common mistake:
| Carried by a snapshot? | Use for | |
|---|---|---|
| A browser session | yes | a login you want every future machine to inherit |
| A secret | no | a credential that must not end up in a captured disk |