Ctrl K
ComunidadGuías…

projects/start-cli/ARCHITECTURE.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# 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::<CliContext>()` — 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: <value> }`, 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/<area>/` 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.