id: cli
title: Six binaries with six naming conventions, and no way for anyone else to add a seventh
status: open
crates: [didbot-agentd, didbot-claim, didbot-serve, didbot-setup, didbot-swarm, didbot-verify, didbot-brand]
dependsOn: []
exitCriterion: >
A new binary is accepted by the rules written here rather than by a
reviewer's taste: it is named for the seat it runs in, its verbs are bare
imperatives, it spells the server, the DID, the state directory and its
errors the way the conventions table says, and no credential reaches it in
argv or forwarded by a dispatcher. #
cli #
There are eight binaries: didbot-pds, didbot-setup, didbot-swarm,
didbot-claim, didbot-verify, didbot-brand, didbot-agentd and
didbot-oauth, each under a crates/*/src/bin/ of its own, each with a
hand-rolled parser and a USAGE string, each named by whoever added it. No
two spell the server's address the same way, three exit statuses mean "bad
arguments", and --help exits 0, 2, or starts a daemon, depending on
which one you asked.
The occasion for this audit is a ruling: the verb an agent named
become-host — the host half of handshake's reservation, on
the claude/host-reservation branch — will not be called that, and nothing
else gets a name until the whole surface has been looked at once. This epic
is that look, and a proposal. Each item below is for the owner to accept,
amend or refuse; the rename table is a proposal, and every name in it marked
proposed is one nothing on main answers to yet.
This epic is about how a person reaches a surface, not what the surface does. Each surface has its own epic.
What is on PATH today #
Every claim in this table is read from the parser, not from a --help that
was remembered.
| binary | crate, source | who runs it | verbs, and their form | the server | the DID | the credential |
|---|---|---|---|---|---|---|
didbot-pds |
didbot-serve, src/bin/didbot-pds.rs (parse_args) |
the server host (Dockerfile:106, infra/pds/templates/user_data.sh.tftpl); a developer through scripts/dev-pds.sh |
none. Thirty-odd --flags; --list-names and --list-avatars print and exit |
is the server: --zone <host> names its own hostname, --port <n> |
--owner <did>, the operator answerable for every account (DEFAULT_OWNER is a placeholder) |
none on the command line. Keys live under --data; Route53 credentials come over IMDSv2 (USAGE, --route53-zone-id) |
didbot-setup |
didbot-setup, src/bin/didbot-setup.rs (Options::parse) |
a developer, on the machine that runs the stack (scripts/dev-profile.sh evaluates didbot-setup env) |
bare verbs (check, env, show, bind, unbind, disk), a verb with a word (debug on|off), and noun-verb groups (service add, profile new) |
--pds <service>, a name in stack.toml, not an address; env prints DIDBOT_PDS_URL |
none | none; check reads /health over HTTP (check.rs:143) |
didbot-swarm |
didbot-swarm, src/bin/didbot-swarm.rs (parse_args) |
a developer, through scripts/dev-swarm.sh |
none; flags only | --pds <url> (default http://localhost:3000) |
none; it mints its own | none; provisions through the front door |
didbot-claim |
didbot-claim ([[bin]] in its Cargo.toml), src/bin/didbot-claim.rs (parse_args) |
the operator, on their own machine, never the server (crate description) | none; two positionals, and --check <hostname> as a mode |
<hostname>, positional |
<operator-handle-or-did>, positional, the operator's own |
a browser OAuth grant against the operator's own PDS, scoped to scope::operator_write_scope, held in MemoryAuthStore |
didbot-verify |
didbot-verify ([[bin]]), src/bin/didbot-verify.rs (parse) |
anyone, on a stranger's machine (crate description) | none; the mode is chosen by which flags are present — <agent-did> alone, or --owner with --server |
--server <did>, a DID, not an address |
<agent-did> positional; --owner <did> |
none, by design |
didbot-brand |
didbot-brand, src/bin/didbot-brand.rs (main) |
a developer and CI, through scripts/build-brand.sh (scripts/ci.sh:61 passes --check) |
none; <destination> positional, --check as a mode |
not applicable | not applicable | none |
didbot-agentd |
didbot-agentd, src/bin/didbot-agentd.rs (main) |
the agent host, through scripts/dev-agentd.sh or a unit |
none; it reads no argument at all | DIDBOT_PDS, an environment variable, required |
none; accounts are minted per context through registrar::Pds |
none at start; what it mints it holds in memory behind the socket |
didbot-oauth |
didbot-agentd, src/bin/didbot-oauth.rs (main) |
the agent, on the agent host; a CI job or a token-holding host with --direct |
bare verbs: pending, show <url>, approve <token>|--url <url>, decline <token>|--url <url> [--reason] |
none over the socket; DIDBOT_PDS in --direct mode (direct.rs::SERVER) |
never — every_command_this_binary_takes_is_in_its_usage asserts no --as and no did: |
none over the socket; DIDBOT_AGENT_TOKEN or DIDBOT_AGENT_TOKEN_FILE in --direct mode |
The wrappers under scripts/ front four of these and add spellings of their
own. dev-pds.sh reads DIDBOT_PDS_PORT, DIDBOT_ZONE, NAMES and
DIDBOT_PDS_DATA and passes them as flags; dev-agentd.sh reads
DIDBOT_PDS and DIDBOT_SOCK and passes them as environment; dev-swarm.sh
reads DIDBOT_PDS_URL, DIDBOT_SWARM_AGENTS and DIDBOT_SWARM_RATE and
passes them as flags; build-brand.sh fixes the positional to site/public.
dev-profile.sh is the seam between them: it evaluates didbot-setup env,
so stack.toml reaches the others as exports.
Two verb surfaces have no binary. The e-stop admin socket
(crates/didbot-serve/src/estop_admin.rs) takes STATUS, PAUSE, REVOKE,
RELEASE, ANNOUNCE and NOTIFY as upper-case lines over socat
(docs/deployment.md:371). The agent daemon's socket
(crates/didbot-agentd/src/protocol.rs::Message) takes report, approve,
decline, pending and show as an asks field, and the branch adds
become and host to it — the two verbs the ruling left unnamed.
The conventions in use #
| convention | who does what |
|---|---|
| binary name | didbot-<word> everywhere. The word is a role (pds, agentd), a verb (claim, verify, setup), a thing (swarm, brand), or a protocol (oauth). |
| verb form | didbot-setup mixes bare verbs with noun-verb groups; didbot-oauth is bare verbs; didbot-claim and didbot-brand choose a mode with --check; didbot-verify chooses one by which flags are present; didbot-pds, didbot-swarm and didbot-agentd have none. |
| flag form | --kebab-case with the value as the next word, in every binary. --name=value is read only by didbot_agentd::cli::flag. -h is read by six; didbot-claim reads no help flag and didbot-agentd reads nothing. -y (didbot-setup) is the only other short flag. --version exists in didbot-setup alone. |
| the object | positional in didbot-claim, didbot-verify, didbot-brand and didbot-oauth's token; a flag in didbot-swarm (--pds) and didbot-pds (--zone); didbot-oauth show takes its URL either way. |
| the server's address | --pds <url> (didbot-swarm), DIDBOT_PDS (didbot-agentd, didbot-oauth), DIDBOT_PDS_URL (didbot-setup env, dev-swarm.sh), <hostname> (didbot-claim), --server <did> (didbot-verify), --zone <host> (didbot-pds, its own). Six spellings for one thing. |
| the DID | --owner <did> in didbot-pds and didbot-verify; positional in didbot-verify (agent) and didbot-claim (operator, or a handle); forbidden in didbot-oauth. |
| state directory | didbot-pds: --data <dir>, memory without it. didbot-setup: $XDG_CONFIG_HOME/didbot/stack.toml and $XDG_STATE_HOME/didbot/<service> (didbot_stack::default_config_path, default_data_dir). dev-pds.sh: $XDG_STATE_HOME/didbot/pds, spelled in shell. Sockets: $XDG_RUNTIME_DIR/didbot-agent/agent.sock (socket::default_socket_path), $XDG_RUNTIME_DIR/didbot-estop/estop.sock (estop_admin::default_socket_path), $XDG_RUNTIME_DIR/didbot/<name>-<port>.pid (dev-pidfile.sh). The other four keep nothing. |
| errors | prefixed didbot-setup: (didbot-setup.rs:63) and didbot-oauth <verb>: (fail); bare eprintln! in didbot-claim, didbot-verify, didbot-brand; a tracing error! line in didbot-pds, didbot-swarm, didbot-agentd. |
| bad arguments | stdout and exit 2 in didbot-pds and didbot-swarm (println!("{message}"); ExitCode::from(2)); stderr and exit 2 in didbot-verify; stderr and exit 1 in didbot-setup, didbot-oauth, didbot-claim, didbot-brand. |
--help |
exit 0 on stdout in didbot-setup, didbot-oauth, didbot-brand; exit 2 on stdout in didbot-pds and didbot-swarm, because -h is an Err(USAGE); exit 2 on stderr in didbot-verify, whose -h is Err("what to check:"); in didbot-claim it is a missing operator, exit 1; in didbot-agentd it starts the daemon. |
| logging | RUST_LOG, else a default: didbot_serve::DEFAULT_LOG_FILTER for didbot-pds and didbot-claim (init_tracing), info for didbot-swarm, didbot_agentd=info for didbot-agentd. didbot-stack carries its own copy of the filter (lib.rs:43). |
One rule for binaries, one for verbs #
-
today under the rules why didbot-pds [flags]didbot-pds [flags]the server's seat; one thing to do; no object didbot-agentddidbot-agentd servethe host's seat. It gains verbs the moment host identity lands, and a binary with verbs has one for its main job too (socket become, branch)didbot-agentd reserve <server>— proposedthe server-side transition is a reservation ( ReservationPolicy,--pending-reservations, thelist-reservationsroute on the branch), and the verb says what the caller asks for(socket host, branch)didbot-agentd show— proposedwhat showmeans indidbot-setupanddidbot-oauth: print what this seat is. The key, and the identity once a server has reserved onedidbot-oauth pending|show|approve|declinedidbot-agent pending|show|approve|decline— proposedthe agent's seat. oauthis the protocol the answers travel over, not who is answeringdidbot-claim <hostname> <operator>didbot-operator claim <hostname> <operator>— proposedthe operator's seat, and the name of the record it writes didbot-claim --check <hostname>didbot-operator check <hostname>— proposeda second kind of run is a second verb, as didbot-setup checkalready isdidbot-verify <agent-did> [--owner]didbot-operator verify <agent-did> [--owner <did>]— proposedreads bot.did.operator; the question is an operator question, and it holds nothing, so sharing a binary withclaimleaks nothingdidbot-verify --owner --serverdidbot-operator survey --owner <did> --server <did>— proposedthe parser already refuses both at once with "check one agent, or survey a server"; the verb is the one the binary uses for itself didbot-setup service add/profile newdidbot-setup service add/profile addone action, one verb didbot-setup bind|unbind|check|env|show|debug|diskunchanged the developer's seat, named for what it describes didbot-swarm [flags]didbot-swarm [flags]the developer's seat; no object didbot-brand <dir>/--check <dir>didbot-brand write <dir>/check <dir>the same object, two things done to it The cost is in the docs:
didbot-claimis named 27 times acrossplan/,docs/and the README,didbot-oauth16,didbot-verify8, anddocs/deployment.mdis a runbook. The harness adapter speaks the socket, not the binary, sodidbot-agentcosts nothing in the other repository.
Which crate, by seat #
What a seventh binary must do #
Defects, as distinct from conventions #
Each of these is a disagreement between two places in the code, not a choice nobody made.
Done #
Nothing closed yet.