Machines and links
Every conversation and routine run works on a machine; runs about one pull request share one, and anything a machine serves opens for your members at a standards.run link.
A machine is a live or stopped computer an agent works on: a copy of an environment's image, or a conversation's own sandbox. A pilot drives it for one run or one turn: our agent or a harness (Claude Code, Codex, Cursor). A link is one port of a machine, published at https://<slug>.standards.run, that the members allowed to read the work can open in their browser, even after the machine has gone to sleep.
So a routine that says "the feature is on PR #6747" can hand you the running app: you open it, try the gesture, and approve or not.
The machines
Every machine has one row in the workspace, whatever started it, and each is known by its key:
| Key | Started by | Ends when |
|---|---|---|
| Conversation | A conversation's first turn, on any environment or none | The conversation is archived |
| Routine run | A routine run on a custom environment that does not share a machine | The run ends |
| Branch | The first run about a pull request, of a routine with Reuse the branch's machine on | Every pull request of that branch, across the environment's repositories, is merged or closed |
Routine runs on a default environment, or on none, run on a machine that ends with the run and is not listed here.
What keeps a machine. A machine is destroyed when its key has ended and none of its links is valid and no open decision points at it. Until then it is stopped with its snapshot, and woken when someone opens one of its links. Its snapshot's expiry is the hard limit: past it, the machine is destroyed whatever holds it. A conversation's sandbox is destroyed the same way once its conversation is archived.
Who sees a machine. You read a machine when you can use its environment (when it has a custom one) and read what owns it: the conversation, the run, or at least one run of the branch. Supervising someone's conversations does not let you read their machines or open their links.
The lease
One holder at a time acts on a machine: a routine run, a conversation turn, the wake or the relaunch of a link, or its destruction. The others wait, polling every 2 seconds, and a waiter gives up after 10 minutes with 403 SCHEMA_STATE_GATE. A holder silent for 90 seconds has lapsed: the next one takes the machine over, stops whatever processes the previous holder left running, and only then acts. A write by a holder whose turn was taken answers MACHINE_LEASE_FENCED and stops. A run holds its machine as one holder: every turn of the run and its sub-agents join that hold without waiting, each keeps it alive, and it is released by the last of them to finish; a run that crashed releases it 90 seconds after its last sign of life.
The compute of a machine is metered to the person its current holder acts for, not to whoever created it. Running out of credits stops the machine once its current holder is done, never under it.
Changes
A change is a head branch in one environment. When a routine's Reuse the branch's machine is on and its run concerns a pull request, the run works on the branch's machine of the run's environment: pull requests in several repositories of one environment that share a head branch are one change, tested together on one machine.
These events concern a pull request: pull_request (every action), pull_request_review, pull_request_review_comment, issue_comment on a pull request, and check_run, check_suite and workflow_run attached to one. For the events that do not carry the head, Standards reads the pull request to find its head repository, its head branch and the base repository's default branch; when GitHub cannot be read, the run gets a machine of its own.
A run gets a machine of its own instead when:
- the pull request comes from a fork, or its head branch is the repository's default branch;
- its content is not readable by the whole workspace: a private run, a run of a routine with an audience, or a run under someone's own grants. A branch's machine only hosts runs every member may read;
- it was fired by the pull request closing or merging;
- a harness pilots it (Claude Code, Codex or Cursor). A harness reads its variables from its machine, set once when the machine is made, so a later run on a shared machine would not get its own.
One run at a time, in order. The runs of one branch queue in the order they arrived: a review, a fix, then a second review all run, one after the other, and none waits in a worker while it queues.
Checkout. Before a run starts, every repository of the environment is fetched and reset to the branch when it exists on that repository's remote, to its default branch otherwise. A branch deleted or force-pushed between two runs is checked out from the default branch, and the run's report says so. Later turns of the same run never reset it again, and a repository whose reset failed is reported to the run, not reset again by its next turn; only a machine whose copy was lost mid-run checks the branch out again, on its fresh copy.
Closing. When a pull request closes or merges and no open pull request of the environment's repositories has that head branch any longer, the branch's machine has ended. It is destroyed at the next sweep, unless a valid link or an open decision keeps it.
Links
Publishing a port
A pilot publishes a port with publish_port (standards_publish_port on a harness), in any conversation and in the runs of custom environments. It starts the server first, listening on any interface, then calls the tool with:
| Field | Value |
|---|---|
port | The local port, from 1 to 25535, never 4000 (the harness bridge). |
command | The shell command that starts the server, rerun when the link is relaunched. |
cwd | Its working directory, under the machine's working directory; on a custom environment, usually one of its repositories. |
durationHours | How long the link lives: 12 hours unless given, from 1 to 24. |
The tool answers the link's url, its port and expiresAt. Publishing the same port again extends its link and replaces its command and its publisher, never a second link: on a machine several runs share, the link opens for the readers of the run that published it last. A machine exposes 15 ports at most, the bridge's included; a port over the cap answers 400 SCHEMA_VALIDATION_FAILED. A link's expiry closes its port; Relaunch opens it again.
The command runs with STANDARDS_RUN_URL (its own link) and STANDARDS_RUN_URL_<port> for every valid link of the machine, so an app can build its public URLs. Runs on a default environment or on none publish nothing, and neither does a run on an environment whose image was built before machines had one working directory, until it is rebuilt.
The link serves the machine's current state, not a picture of the moment it was published.
Opening a link
Opening and relaunching a link require both that you can use the machine's environment, when it has a custom one, and that you can read the run or conversation that published it. A link of a conversation without a custom environment opens for whoever reads that conversation. There are no public share links.
Open mints a single-use token bound to that link, valid for one minute. The standards.run proxy spends it, answers a redirect to the same address without it, and sets a cookie for that one address, valid 12 hours; your access is checked again each time the cookie renews, so a member who lost access is refused at the next renewal. A token is refused on a second use and for another link with LINK_TOKEN_INVALID, whatever the reason.
A woken machine runs as you. Opening a stopped machine or relaunching a command runs as the member who clicks, and its compute is metered to them. The command receives the workspace, environment and repository variables (on a machine without a custom environment, the workspace variables that reach every environment), never anyone's personal variables. A woken machine starts at the environment's size.
What you see while the link opens:
| State | You see |
|---|---|
| Running, the server answers | The app |
| Stopped | "Starting the instance", then the app |
| Held by a run | A page naming what holds it, then the app once released |
| Running, the server does not answer | Relaunch |
| The wake or the command failed, or the port did not answer within 3 minutes | What happened, and Relaunch |
| Not allowed | A refusal naming the environment only if you can see it |
| The link expired, the machine is alive | Relaunch |
| The machine is gone | A page that says so |
Waking is done once per machine, however many tabs ask: a second tab waits for the first wake. A machine woken by a link stops after 15 minutes without traffic (its session is extended at most once a minute, however busy the page), and when its last link expires. A stopped machine's snapshot lives 7 days: the machine's page shows that expiry, and past it the machine is destroyed whatever holds it. An expired link's tokens go at once; its address, command and publisher are kept 7 days, then purged.
The gate. The port a machine exposes to the internet is not the app's: it is a small gate on the machine that forwards to the app only when the request carries the link's secret, which only the proxy holds. A leaked provider address serves nothing, and the provider's host never reaches your browser.
Links are served by the hosted platform only. A deployment offers publish_port once its *.standards.run proxy is live: the proxy ships first, then the deployment is given the proxy's shared secret as STANDARDS_LINKS_PROXY_SECRET, and only then is the tool mounted, so no pilot is ever handed an address that resolves nowhere. Without the secret, the proxy routes answer 404 and links stay dark.
In the app
- Link card. Wherever a link shows: its address, port and command, a Live or Asleep pill, Open in a tab, Enlarge and Collapse. It loads the app inside the card only when you expand it, so scrolling past a link never wakes its machine. In a conversation it also carries the duration menu: 1 hour, 4 hours, 12 hours, or 24 hours, marked maximum.
- Conversation. The card shows in the thread where the link was published; above the composer, Live links holds one chip per valid link of the conversation's machine, with its time left.
- Decision. When a decision points at a link and you pass the access check, its card shows the link collapsed, with Test beside the answers and the link's expiry. The tab opens on your gesture; a blocked tab or a refused link says so.
- Run row. A run that worked on a branch's machine links to it, for members who read the machine.
The Machines page
Machines, in the sidebar, lists every machine you read, running then asleep, filtered by kind (Branches, Conversations, Routines) and environment. Each row shows its owner, pilot, size and link.
Each machine has its own page: its key and environment, its state and size, its snapshot's expiry, its links (address, port, command, time left) with Open and Relaunch, each listed only for members who may open it, and its runs and turns (routine, pilot, size, outcome, duration), each listed only for members who may read it. Only the machine's owner sees Destroy the machine: a conversation's creator for its sandbox, the environment's owner for a routine run's or a branch's machine. It asks first, naming its live links and what is using it; a machine in use is not destroyed: the dialog says what is using it, and you destroy it once that finishes.
API
All routes sit under https://api.standards.new/v1. Each answers 404 for a machine or a link you cannot read.
| Route | Purpose |
|---|---|
GET /machines?environmentId=&kind=&cursor= | The machines you read, cursor-paged: { items, nextCursor }. Each item carries machine, links (only those you may open), lease, owner ({ label }) and pilot (null for our agent). kind is conversation, run or change. |
GET /machines/:id | { machine, links, lease }. machine holds id, environment, key, status (running, stopped or destroyed), vcpus, snapshotExpiresAt, destroyedAt and createdAt; lease is what holds it ({ kind, label }) or null; canDestroy is whether you may destroy it. |
GET /machines/:id/runs?cursor= | Its runs and conversation turns, each filtered by its own visibility: runId, definitionId, sessionId, label, pilot, size, status, startedAt, completedAt. |
GET /agents/sessions/:id/machine | A conversation's machine; 404 before its first turn. |
DELETE /machines/:id | 204: destroy it. Its owner only (the conversation's creator, else the environment's owner); another reader gets 403 SCHEMA_FORBIDDEN. 403 SCHEMA_STATE_GATE at once while something holds it (a run, a turn, a wake, a relaunch), its message naming that holder by kind; nothing is destroyed. |
POST /links/:id/open | { url }: the link's address carrying a single-use token. |
POST /links/:id/relaunch | Reruns its command, waking the machine first; an expired link lives 12 hours again. 409 SCHEMA_CONFLICT while something else holds the machine or wakes it. |
POST /links/:id/duration | { durationMs }: the link expires that long from now, 24 hours at most (86400000). |
A link reads id, machineId, slug, url, port, command, cwd, publishedBy, publishedFrom ({ runId } or { sessionId }), createdAt and expiresAt. A run that worked on a branch's machine carries its machineId. The /links/by-slug/… routes serve the standards.run proxy and are not called by members.
Errors
| Code | HTTP | When |
|---|---|---|
LINK_TOKEN_INVALID | 400 | A link token that is unknown, already spent, expired or minted for another link. |
MACHINE_LEASE_FENCED | 409 | A holder whose hold on the machine was taken over tried to act on it. |
SCHEMA_STATE_GATE | 403 | A run or a link waited 10 minutes for a machine something else held. |
SCHEMA_CONFLICT | 409 | A relaunch while the machine is held or being woken, or a duration change on a link a newer one replaced. |
SCHEMA_VALIDATION_FAILED | 400 | A duration over 24 hours, a port over the cap of 15. |
SCHEMA_RECORD_NOT_FOUND | 404 | The link expired (on open), the link or the machine is gone, or you cannot read it. |
See errors for the shared codes.
Next steps
Read environments for the recipe a machine is copied from, the pilots it accepts and the variables it receives, and agents and routines for triggers and runs.