activity family lists records and shows one in full. Use it to answer what changed, who changed it, and whether it succeeded.
Every result that the CLI prints carries a Gateway request ID. That ID is the link between a command you ran and the Activity the Gateway kept for it. Project create and update requests record the sent display name, source defaults, and task check as activity fields; an omitted field stays omitted.
node:wireguard-key records the caller, target, outcome, and request ID with empty Activity input, including refused or invalid submissions. Neither the submitted public key nor unknown body fields are retained. A failed or interrupted request can leave the desired key stored before hub convergence finishes; the Activity outcome does not imply rollback. Follow the command’s recovery steps and replay the desired key after restoring an authorized management path.
Project development deploy step changes record project:dev-deploy-step:create, project:dev-deploy-step:update, or project:dev-deploy-step:destroy. Their Activity input omits commands, including on refused requests. Use the request ID to find a step edit without exposing its shell command.
Project Document commands record their operation, target path, caller, outcome, and request ID. Activity input is empty for this family, including refused writes, so document bytes are never parsed or recorded before authorization. Storage credentials remain redacted.
Conn T3 server registration records conn:environment:register with only the environment ID, label, and URL, so the admin session never enters Activity. A profile settings write records only section names and versions, because the document can hold workspace pictures, themes, and device values. conn:profile:settings:update records the document version it replaced and the names of the sections it sent. conn:profile:settings:patch records each section name with the version the client sent.
Commands
Every command accepts
--json. The list and show commands need Gateway access.
orbit activity:list
List Activity records, newest first. Filters combine. A value that matches nothing returns an empty list.
The CLI sends the filters as
before_id, status, command, caller_node_id, and target_node_id. The Gateway rejects an invalid filter with validation.failed (422). The list never includes the list request itself. Fewer rows than --limit means there is no older page.
orbit activity:show
Show one Activity attempt.activity that is not a positive integer fails with activity.id_invalid. Human output uses a detail tree with the command and status, the request ID the Gateway assigned to that attempt, the time, duration, exit code, and error code. JSON output returns every field below.
What the Gateway records
The target is the request path, so reading the logs of two Processes keeps a record for each. A polling client therefore adds about one record per command and target per minute. When the sampling cache fails, the Gateway records the read and logs a warning.
--request-id finds no record for a successful read inside another read’s window. A read has no running state, because the Gateway writes it once, when it ends.
These routes record no Activity, for any method: the Node agent endpoints, the annotation event stream, the Grafana access check, the two GitHub App registration pages, the Schedule completion report, the Instance wake check, the log stream renewals, and the task agent list and stream. An MCP tool call records the Activity of the API request it runs. In an MCP batch, a call that starts after the batch has used up its forward time is recorded as failed with command.deadline_exceeded.
Interrupted requests
A request can end before it records its outcome. PHP-FPM ends a Gateway request that passes its 600-second limit, the kernel can kill a worker that runs out of memory, and a Gateway deploy restarts its workers. The Gateway then ends the record asfailed with the error code activity.interrupted. Its duration and exit code stay null.
- After a fatal error in PHP, such as exhausted memory or an exceeded execution time, the Gateway ends the record when the request shuts down.
- A killed worker runs no code. The Gateway scheduler runs
php artisan orbit:activity-finalize-interruptedevery five minutes, without overlap, as a job on the Gateway queue. It ends every record stillrunning900 seconds after it started, at most 500 records per run, oldest first. PHP-FPM checks its 600-second limit every 200 seconds, so a request ends by about 800 seconds. The sweep therefore never ends a command that still works. - The sweep’s overlap lock expires after 10 minutes, so a killed sweep blocks later runs for at most 10 minutes.
- The sweep runs only while the Gateway scheduler,
php artisan schedule:workin the Gateway checkout, is running. The Gateway install does not start the scheduler. Without it, a killed request staysrunning.
succeeded. The Activity, not the client’s error, shows the outcome.
activity.interrupted means the Gateway lost the request, not that the operation failed on its target. The operation may have finished on the Node, so check the target before you run the command again. orbit.activity_interrupted_after in the Gateway configuration holds the 900-second bound. A test keeps it more than 60 seconds above four thirds of the PHP-FPM limit.
What a record holds
properties holds method, path, and input: the request body after redaction. So a process:create record keeps the Process command and working directory, and an environment map keeps its variable names without values. A command that returns a remote command result adds its redacted stdout and stderr and output_truncated. A deployment or rollback adds its terminal status, selected release, failed step, and error code. An env:sync record for an Instance that owns its DB database adds testing: the file .env.testing, the status written, unchanged, or skipped_tracked, and the test database name. The SDK redacts credential-shaped values from properties again before the CLI prints them.
Redaction
The Gateway redactsproperties before it stores a record, for every command:
- A field is stored as
[REDACTED]when any word of its name iskey,token,secret,password,passwd,passphrase,credential,bearer,pem,authorization, orcookie, singular or plural. Words are split on case changes and separators, socliproxy_management_key,managementKEY,apiToken,X-Api-Key, andSet-Cookieall match. The joined formsapikey,appkey,authtoken,accesstoken, andprivatekeycount too.valkey_connectiondoes not match. A new field that matches this rule is redacted without a code change. - The rule covers header maps, so
Authorization,Proxy-Authorization,Cookie, andSet-Cookievalues are redacted. - A number in a matching field is redacted too, except a usage count whose name ends in
tokensorcount, such asinput_tokensortoken_count. Booleans and null stay. - A short list of public fields is exempt:
public_key,wireguard_public_key,ssh_public_key,public_pem,host_key_fingerprint,runtime_key,env_key,idempotency_key, andtoken_expires_at. - Environment maps keep variable names and replace every value.
- Inside other text values, the Gateway redacts secret assignments such as
MANAGEMENT_KEY=value,PASSWORD_HASH=value, andSECRET_KEY_BASE=value, URL user information,Authorizationheaders, bearer tokens, and PEM blocks. Prose with a bare key, such asMissing key: APP_NAME, stays. - Every other input field stays, so a record still shows the target, the Node, and the non-secret options the caller supplied.
Why it works this way
These reasons explain the design. Check them before you propose a change.Sampled reads
The log answers what changed, who changed it, and whether it succeeded. A successful read changes nothing. Keeping every read would cost two SQLite writes per poll and would hide the changes between thousands of polls. Dropping every successful read would hide whether a Node or an agent reads the API at all. One record per command, caller, and target each minute keeps that answer. A failed read stays, because it can show a broken Node or a missing access grant. A credential read stays, because it shows who holds a secret. Everyschedule:* operation stays, as the Schedule contract promises.
Interrupted, not a new status
A new status would force every client and filter to learn it.failed with a stable error code says the same thing. One bound for every command follows the one PHP-FPM limit that ends every request. The Gateway keeps interrupted rows, because the attempt, its caller, and its target are audit history.
Filters on the Gateway, paging by id
before_id keeps an older page on the same rows when new rows arrive. An offset would shift. Caller and target stay two filters, because the log separates who called from what changed.
Related
doctorreports drift; use the request ID it prints to find its Activity.- Using the CLI explains request IDs and the JSON error envelope.
- The web app lists Activity live from these records.
- Realtime events defines
activity.createdandactivity.updated.