# start-cli Architecture `start-cli` is intentionally tiny. Almost all behavior lives in the shared [`start-core`](../../shared-libs/crates/start-core) crate (cargo package `start-core`, lib name `start_core`); this crate is just the `start-cli` entrypoint plus build/feature wiring. ## Place in the monorepo This crate lives at `projects/start-cli/` and produces the standalone `start-cli` bin. Like the other product bins (`startbox`, `start-container`, `registrybox`, `tunnelbox`) it depends on the shared `start-core` crate, declared as `start-core = { path = "../../shared-libs/crates/start-core" }`. See the root [`ARCHITECTURE.md`](../../ARCHITECTURE.md) for the overall monorepo layout. ## What this crate contains - `Cargo.toml` — declares the `start-cli` bin, the `start-core` dependency, and the feature flags (`beta`, `console`, `dev`, `test`, `unstable`) that forward to `start-core`. - `src/main.rs` — the entrypoint. It: 1. Reads `STARTOS_USE_PODMAN`; if unset/false it sets `PREFER_DOCKER` (so local `s9pk` packaging uses Docker by default). 2. Builds a `start_core::bins::MultiExecutable`, enables the `start-cli` subcommand, sets it as the default, and `execute()`s. `MultiExecutable` is the same multiplexer the OS uses for `startbox`: a single binary that dispatches to a named sub-bin based on `argv[0]`/the first argument. `start-cli` enables only the `start-cli` bin (and a deprecated `embassy-cli` alias) and defaults to it, so as a standalone binary it behaves like a dedicated CLI. ## Request flow ``` argv ─▶ MultiExecutable ─▶ start_cli::main (in start-core) │ ├─ build CliApp from start-core::main_api() ├─ load ClientConfig (flags + -c + workspace + ~/.startos + /etc/startos) └─ run: • remote command ─▶ CliContext ─HTTPS RPC─▶ StartOS server • local command ─▶ executed in-process (s9pk, keys, util) ``` The actual CLI bin (`start_cli::main`) lives in [`shared-libs/crates/start-core/src/bins/start_cli.rs`](../../shared-libs/crates/start-core/src/bins/start_cli.rs). It constructs an `rpc_toolkit::CliApp` from `start-core::main_api()` and a `ClientConfig`, renames the command to `start-cli`, stamps the start-cli product version, and runs it. Errors are unwrapped from the RPC envelope and printed to stderr; the process exits with the RPC error code. ### Remote vs. local commands `main_api()` (in [`shared-libs/crates/start-core/src/lib.rs`](../../shared-libs/crates/start-core/src/lib.rs)) registers every subcommand once with `with_call_remote::()` — those are dispatched over the network to a server's RPC endpoint, authenticated by signing each request with the enrolled identity key (or, on the server itself, the local authcookie sent as a Bearer token). Commands without a remote handler (`s9pk`, `init-key`, `pubkey`, `util`) execute locally. The `registry` and `tunnel` groups can target a separate registry/tunnel host via `--registry`/`--tunnel`. ### Configuration `ClientConfig` (in `start-core::context::config`) is the single config type — the workspace `.startos/config.yaml`, `~/.startos/config.yaml`, `/etc/startos/config.yaml`, and `-c` files all deserialize into it (a `schema` key marks a real workspace for the walk-up). `host`/`registry` are each a `Profiles` namespace with one config key; `-H`/`-r` parse (via `Profiles`' `FromStr` / `ValueParserFactory`) straight into `{ default: }`, so the flag is just the top layer of that namespace — no separate selector field. `ClientConfig::load` then layers the config files under the flags: a `-c` chain, the discovered workspace (only its `host`/`registry`), `~/.startos`, then `/etc/startos`. A bare URL is shorthand for `default`, and the layers combine into one namespace — a union of aliases where a name defined at more than one level takes the higher tier's value. `resolve_target` follows the `default` profile, chasing a value that names another profile until it reaches a URL, which it parses only then. Its shared `resolve_profile` primitive also resolves explicit `--from`/`--to` endpoints for registry package and OS promotion. `CliContext` retains the merged `registry` profiles for these endpoint selections. `ClientConfig` also carries tunnel, proxy, identity key path, root CAs, and the `--insecure` toggle. The loaded config becomes a `CliContext` that the RPC handlers use. ## Man pages `start_cli.rs` (in `start-core`) has a `#[test] export_manpage_start_cli` that runs `clap_mangen` to write the committed man pages into this project's `man/` dir (`projects/start-cli/man/`). Run it with `cargo test -p start-core export_manpage_start_cli`. ## Where to make changes - New/changed CLI commands → `start-core` (`main_api()` and the relevant `src//` module), not here. This crate only changes when the entrypoint, features, or bin wiring change. - Verifying a command end to end → build `start-cli` and run it against a test server/VM. ## Further reading - [`README.md`](./README.md) — what `start-cli` is and how to use it. - [`AGENTS.md`](./AGENTS.md) — how to contribute to this crate.