carlos / platform Public

docs: carlos run, and what a run's command is started with (run spec §4, §6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev c6ff01d08ebe842cca17cbfbfcbef1f3a7376027 parent 4e9d5ad
2 files changed, +31 −1
  • approved_run_copy_test.go +1 −1
  • docs/site/cli.md +30 −0

CI passed — run details

diff --git a/approved_run_copy_test.go b/approved_run_copy_test.go
index cdeb35113..23a6d0888 100644
--- a/approved_run_copy_test.go
+++ b/approved_run_copy_test.go
@@ -33,7 +33,7 @@ var runCopyIDs = func() []string {
// runCopyBuilt is every deck id whose text the code or docs now carry. The
// task that writes a row's text adds its id here in the same commit.
-var runCopyBuilt = []string{"RUN-C1", "RUN-C2", "RUN-C3", "RUN-C4", "RUN-C5", "RUN-C6", "RUN-C7", "RUN-C8", "RUN-C9", "RUN-C10", "RUN-C11", "RUN-C12", "RUN-C13", "RUN-C14", "RUN-C15", "RUN-C16", "RUN-C17", "RUN-C18", "RUN-C19", "RUN-C20", "RUN-C21", "RUN-C22", "RUN-C23", "RUN-C24", "RUN-C25", "RUN-C26", "RUN-C27", "RUN-C28", "RUN-C29", "RUN-C30", "RUN-C31", "RUN-C32", "RUN-C33", "RUN-C34", "RUN-C35", "RUN-C36", "RUN-C37", "RUN-C38", "RUN-C39", "RUN-C40", "RUN-C41", "RUN-C42", "RUN-C43", "RUN-C44", "RUN-C45", "RUN-C46", "RUN-C47", "RUN-C48", "RUN-C49", "RUN-C50", "RUN-C51", "RUN-C52", "RUN-C53", "RUN-C54", "RUN-C55", "RUN-C56", "RUN-C57", "RUN-C58", "RUN-C59", "RUN-C60", "RUN-C61", "RUN-C62", "RUN-C63", "RUN-C64", "RUN-C65", "RUN-C66", "RUN-C67", "RUN-C82", "RUN-C83"}
+var runCopyBuilt = []string{"RUN-C1", "RUN-C2", "RUN-C3", "RUN-C4", "RUN-C5", "RUN-C6", "RUN-C7", "RUN-C8", "RUN-C9", "RUN-C10", "RUN-C11", "RUN-C12", "RUN-C13", "RUN-C14", "RUN-C15", "RUN-C16", "RUN-C17", "RUN-C18", "RUN-C19", "RUN-C20", "RUN-C21", "RUN-C22", "RUN-C23", "RUN-C24", "RUN-C25", "RUN-C26", "RUN-C27", "RUN-C28", "RUN-C29", "RUN-C30", "RUN-C31", "RUN-C32", "RUN-C33", "RUN-C34", "RUN-C35", "RUN-C36", "RUN-C37", "RUN-C38", "RUN-C39", "RUN-C40", "RUN-C41", "RUN-C42", "RUN-C43", "RUN-C44", "RUN-C45", "RUN-C46", "RUN-C47", "RUN-C48", "RUN-C49", "RUN-C50", "RUN-C51", "RUN-C52", "RUN-C53", "RUN-C54", "RUN-C55", "RUN-C56", "RUN-C57", "RUN-C58", "RUN-C59", "RUN-C60", "RUN-C61", "RUN-C62", "RUN-C63", "RUN-C64", "RUN-C65", "RUN-C66", "RUN-C67", "RUN-C68", "RUN-C69", "RUN-C70", "RUN-C71", "RUN-C72", "RUN-C73", "RUN-C74", "RUN-C75", "RUN-C76", "RUN-C77", "RUN-C78", "RUN-C79", "RUN-C80", "RUN-C81", "RUN-C82", "RUN-C83"}
func mustParseRunRecord(t *testing.T) []copyRow {
t.Helper()
diff --git a/docs/site/cli.md b/docs/site/cli.md
index 95bf4bc59..33c702a59 100644
--- a/docs/site/cli.md
+++ b/docs/site/cli.md
@@ -469,6 +469,26 @@ instances that sleep have a replica. An instance on your own fleet, in
another region, or in a pool cannot be read this way, and the command says
which.
+### carlos run
+
+Runs one command of your app against one instance, as that instance: the same build, user, directories, database and environment it serves with. Output streams back to your terminal about a second behind, and carlos exits with the command's own status.
+
+```sh
+carlos run --app titogo --instance acme.go.tito.io --file checkins.csv -- import checkins.csv
+```
+
+Any person who is a member of the account can run a command. A service credential cannot.
+
+Each --file is uploaded with the request and placed, under its own name, in the directory CARLOS_RUN_DIR names. Together they can come to 64 MB. The directory is deleted when the run ends.
+
+The command runs for at most 30 minutes unless you pass --timeout, up to 6 hours. Ctrl-C cancels it on the box. A second Ctrl-C stops waiting, and the cancel still happens. If your terminal goes away the run carries on, and carlos run --follow picks its output up again. Output is kept until carlos has read all of it, and for 24 hours at most.
+
+A hibernating instance is woken first. A live one keeps serving, and the command opens the same database beside it. One run at a time per instance. carlos run needs the instance's box on the nested layout, and it is refused while the instance is moving.
+
+carlos run exits with the command's own status, 128 plus the signal number when a signal ended it, 124 when it timed out, 125 when an instance stop or an agent restart interrupted it, 130 when you cancelled it, 3 when the box refused it or did not pick it up within 60 seconds, and 2 when the console refused it.
+
+Arguments are kept in the account's audit log, with who ran them. Never put a secret in an argument. Pass it in a --file, or keep it in the instance's environment.
+
### carlos schedule
Gives an app a timetable. A schedule is a time and a path: at each fire the
@@ -699,6 +719,16 @@ no bundle can override:
A seed (the `--seed` command of `carlos instances enable`) runs with the same
environment, `CARLOS_HOST` included, and no control socket.
+A command started with carlos run gets the same environment, without CARLOS_CONTROL_SOCKET or CARLOS_PURGE, and three variables of its own:
+
+| Variable | Value |
+|---|---|
+| `CARLOS_DB` | the instance's database, the path --db names when the instance starts |
+| `CARLOS_RUN_ID` | this run's id |
+| `CARLOS_RUN_DIR` | a directory holding the files passed with --file, under their own names. Always set, even with no files. Deleted after the run |
+
+Its arguments are exactly what followed --, after your binary. The platform adds nothing. It runs in the instance's data directory. On a cancel, a timeout or an instance stop it gets SIGTERM, then SIGKILL 20 seconds later.
+
#### Storage of its own, for every instance
`--private-storage` on `enable` is one declaration that changes what every