carlos / platform Public

carlos: group the top-level command list by role

Bare `carlos` printed every verb in one flat list, each with its caveats
inline — the route entry alone ran 24 lines. It read as a wall of text
rather than an offer of what to do next.

Three sections now, by the role you are in when you type the command:
your apps (auth, ship, promote, env, secrets, ...), your deployment
(bootstrap, add, route, release-keygen, ...), and what systemd runs on
the host (edge, agent, adopt, ops). One line each; the caveats all still
print from each command's own usage, bare or with -h.

Also: `rollback` was wired up in main.go but missing from the list, so it
is listed now; `canary` is dropped from it, since it log.Fatals "not
implemented" and advertising it was noise (the case is untouched).
`help`/`-h`/`--help` now print to stdout and exit 0, so `carlos help |
less` works — a bare `carlos` still goes to stderr with exit 2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
James Adam 6a79b1ee17bc600903366565a9f22eace206a155 parent dd87bf6
1 file changed, +48 −75
  • main.go +48 −75
diff --git a/main.go b/main.go
index bcdc89bd5..a2abeffe4 100644
--- a/main.go
+++ b/main.go
@@ -4,6 +4,7 @@ package main
import (
"fmt"
+ "io"
"log"
"os"
@@ -16,11 +17,15 @@ import (
func main() {
if len(os.Args) < 2 {
- usage()
+ usage(os.Stderr)
os.Exit(2)
}
cmd, args := os.Args[1], os.Args[2:]
switch cmd {
+ // Asking for help is not an error: it prints on stdout and exits 0, so
+ // `carlos help | less` works. A bare `carlos` still fails (stderr, 2).
+ case "help", "-h", "--help":
+ usage(os.Stdout)
case "version":
fmt.Printf("carlos %s\n", version.Build)
case "edge":
@@ -70,83 +75,51 @@ func main() {
case "canary":
log.Fatalf("not implemented: %s", cmd)
default:
- usage()
+ usage(os.Stderr)
os.Exit(2)
}
}
-func usage() {
- fmt.Fprintln(os.Stderr, `usage: carlos <command>
+// usage prints the top-level command list, grouped by the role you are in
+// when you type the command: shipping your own app, standing up and
+// administering a deployment, or the verbs a host runs on itself. The
+// one-liners here are deliberately thin — every command prints its own full
+// usage (bare, or with -h), and that is where the detail belongs. A flat
+// alphabetical list of every verb with every caveat inline was the previous
+// shape, and it read as a wall rather than as an offer of what to do next.
+func usage(w io.Writer) {
+ fmt.Fprintln(w, `usage: carlos <command> [flags]
-commands:
- version print the build id
- edge run the mesh front door (router)
- agent run edge + activator + release adoption on this host
- add | remove | instances manage routes (bare "instances" lists THIS
- box's registry routes; "instances enable"
- opts an app into instance provisioning —
- signed-in mode pins it to the account's
- platform subdomain; "instances
- create|list|delete" manage instance records
- through the console API, signed-in only)
- route -host <h> | -app <a> [-account <sqid|name|all>]
- [-channel <c>] [-environment <name>] [-dry-run]:
- change which release channel and/or named
- config environment a route follows — at least
- one of -channel/-environment is required, both
- may be combined (-account defaults to ops;
- -app is all-or-nothing and skips
- platform-minted routes; -channel refuses a
- version change unless -allow-version-change is
- passed; -environment refuses a bundle that
- does not exist yet, and "-" clears to default)
- -host <h> (-grant | -revoke) <port:n|host-role>:
- grant or revoke an operator-consented capability
- for ONE route on THIS box (never -app, never
- combined with a repoint; 22/80/443 are reserved
- and ungrantable)
- -host <h> -hibernate[=false]: hibernate when idle
- (refused for a route whose release declares the
- daemon capability)
- -host <h> -backing <unit|"">: who owns this
- route's process — a carlos-app@<host> systemd
- unit, or an exec child of the agent (the
- migration's one write; grants need unit backing)
- auth login | whoami | logout — who this terminal is
- (headless? don't log in: pass -owner instead)
- accounts create | list | migrate — the tenancy primitive
- apps create | delete | restore — claim a new app
- name (console API only, rate limited per
- address per day; the console form is not),
- trash an app for 30 days (routes drop
- everywhere; refused while flagged production),
- or undo that within the window (no routes come
- back; carlos add <host> after); delete/restore
- run signed-in via the console or headless
- against the bucket (-account-id required
- headless); no purge here — "Delete now" is
- console-only
- env set | unset | list — plain per-app config vars
- secrets set | unset | list | genkey — sealed per-app secrets
- ship | promote | channels publish and promote releases (-account <sqid|name>)
- promote climbs the ladder canary -> edge -> beta
- -> stable; -hotfix is the recorded bypass
- ledger append | head | list | publish | blob | verify —
- the open-ledger primitive (carlos ledger -h for
- the full usage); every sub but "verify -url"
- goes through the console API, never bucket
- credentials — verify -url needs no credentials
- at all
- release-keygen mint the deployment's root signing key, or with
- -scope <rungs> -name <label> [-expires <dur>] a
- SCOPED key that can only sign those rungs
- (stable is never delegable; needs the root key
- in CARLOS_RELEASE_KEY to sign the grant)
- adopt run one release-adoption pass (the hourly timer's verb)
- canary ship this worktree to an ephemeral canary channel
- bootstrap prepare a host (units, dirs, user)
- ops litestream | restore-verify
- status -dry-run <dir>: fleet status page leak self-check
- (live publish has no CLI mode; the agent owns it)
- vet check an app release against the platform contract`)
+One binary, three roles. "carlos <command> -h" prints a command's own flags.
+
+Your apps — ship and run software on a CARLOS deployment
+ auth log in, see who this terminal is, log out
+ apps create | delete | restore an app name
+ ship publish a release of an app
+ promote move a version up the ladder: canary → edge → beta → stable
+ rollback point a channel back at an earlier version
+ channels show what each of an app's channels is serving
+ env plain per-app config vars: set | unset | list
+ secrets sealed per-app secrets: set | unset | list | genkey
+ instances an app's instances: enable | create | list | delete
+ ledger open ledgers: append | head | list | publish | blob | verify
+ vet check a release against the platform contract
+
+Your deployment — stand up and administer boxes, routes and accounts
+ bootstrap prepare a host: units, dirs, user
+ accounts the tenancy primitive: create | list | migrate
+ add route a hostname on this box to an app
+ remove drop one of this box's routes
+ route repoint a route's channel or config environment, grant or
+ revoke a capability, hibernate it, change its backing
+ instances with no sub-verb: list THIS box's registry routes
+ release-keygen mint the root signing key, or a scoped signing key
+ status -live: what every box last reported; -dry-run: leak check
+
+On the host — what systemd runs; rarely typed by hand
+ edge the mesh front door (router)
+ agent edge + activator + release adoption for this host
+ adopt one release-adoption pass (the hourly timer's verb)
+ ops box operations: litestream | restore-verify
+ version print the build id`)
}