Repository navigation
crates capto ipc
Active contributors: elwina
crates/capto-ipc/ is the shared wire contract for the local Capto control plane. It defines the request and response types the desktop HTTP server speaks, the JSON envelope every response is wrapped in, the lockfile the CLI uses to discover the desktop, and the log-redaction helper that keeps capto_* logs free of credentials. It has no business logic of its own, only the shapes and constants that the desktop and the CLI both lean on.
Three consumers sit on this crate: the desktop control plane (writes the lockfile, builds every response with Envelope), the CLI client (parses envelopes, maps errors to exit codes, reads the lockfile), and agent tooling that drives the desktop over docs/CLI.md. Anything one side serializes, another side deserializes.
| Path | Role |
|---|---|
crates/capto-ipc/src/lib.rs |
Module declarations, re-exports, API_PREFIX = /v1
|
crates/capto-ipc/src/types.rs |
Request and info types (RecordStartRequest, ShotRequest, output listing, DoctorInfo) |
crates/capto-ipc/src/envelope.rs |
Envelope<T>, ApiError, and the stable ExitCode enum |
crates/capto-ipc/src/lockfile.rs |
ServerLock, write/read/clear helpers, is_pid_alive, LOCK_VERSION
|
crates/capto-ipc/src/redact.rs |
Secret scrubbing (redact) and REQUEST_ID_HEADER
|
| Abstraction | File | What it is |
|---|---|---|
Envelope<T> |
crates/capto-ipc/src/envelope.rs |
The generic response wrapper { ok, data, error }. Constructors ok(data), err(code, message), from_result. data and error serialize only when present. |
ApiError |
crates/capto-ipc/src/envelope.rs |
{ code, message } carried inside the error envelope. |
ExitCode |
crates/capto-ipc/src/envelope.rs |
The stable i32 codes 0..6: Ok, Usage, DesktopUnavailable, StateConflict, Capture, Encode, ConfigIo. The CLI maps these from HTTP errors. |
RecordStartRequest |
crates/capto-ipc/src/types.rs |
Body for POST /v1/record/start; camelCase and mirrors the desktop StartArgs. |
ShotRequest |
crates/capto-ipc/src/types.rs |
Body for POST /v1/shot; source plus optional display/window/region. |
OutputEntry / OutputsList
|
crates/capto-ipc/src/types.rs |
One output file (path, name, bytes, modified timestamp) and the full listing with output_dir. |
OpenOutputsRequest |
crates/capto-ipc/src/types.rs |
Mutable-folder open request: optional path, open-folder flag, or "open newest". |
DoctorInfo |
crates/capto-ipc/src/types.rs |
doctor report: OS, capture backend, FFmpeg presence/health, control-plane pid and port, preferred encoder. |
ServerLock |
crates/capto-ipc/src/lockfile.rs |
{ pid, port, token, version } discovery record written by the desktop. |
write_server_lock / clear_server_lock / read_server_lock
|
crates/capto-ipc/src/lockfile.rs |
Lifecycle helpers around the lockfile; read_server_lock_at takes an explicit path for tests. |
redact / REQUEST_ID_HEADER
|
crates/capto-ipc/src/redact.rs |
Scrubbing function and the x-request-id HTTP header constant. |
The LOCK_VERSION constant lives in crates/capto-ipc/src/lockfile.rs (currently 1) and is stamped into every ServerLock so a stale lock from an older desktop is detectable.
Every control-plane response is a single generic shape. A successful call:
{
"ok": true,
"data": { "state": "Recording", "elapsedMs": 1200 }
}A failed call, always with a machine-readable code and a human message:
{
"ok": false,
"error": { "code": "stateConflict", "message": "already recording" }
}ok is the discriminator. The CLI reads envelope.ok first; if false it maps the error.code onto an ExitCode (for example an HTTP 409 with code stateConflict maps to ExitCode::StateConflict). The envelope is defined in crates/capto-ipc/src/envelope.rs and used as the return body of every handler in apps/desktop/src-tauri/src/cli_server.rs.
Discovery is a small file, not a fixed port. When the desktop starts its control plane, start_control_plane in apps/desktop/src-tauri/src/cli_server.rs binds 127.0.0.1:0, generates a fresh v4 UUID as the bearer token, and writes a ServerLock (pid, bound port, token, LOCK_VERSION) to %APPDATA%\Capto\cli-server.json via write_server_lock. When the spawned axum server exits, clear_server_lock removes it; shutdown_control_plane clears it on a graceful shutdown too.
The CLI reverse path is try_existing in crates/capto-cli/src/client.rs: it calls read_server_lock, uses is_pid_alive to drop a stale lock (and clears it), then probes GET /v1/status with the read token to confirm the desktop is really there. Only after that does it construct a client from the lock's port and token.
redact in crates/capto-ipc/src/redact.rs scrubs two known patterns from arbitrary text: Bearer <token> becomes Bearer ***, and the values of a fixed list of query keys (token, api_key, secret, password, auth, session, and others) become *** when they appear as key=value segments in a URL-like string. It is deliberately conservative, matching only those patterns and preserving everything else byte-for-byte so error text stays readable. The desktop routes anything that could carry credentials through it before it reaches tracing output, so capto_* logs only ever show scrubbed strings. This scrubbing is documented in docs/PRIVACY.md. The CLI also wraps user-facing error strings with redact before returning them (crates/capto-cli/src/client.rs).
REQUEST_ID_HEADER (x-request-id) is defined in crates/capto-ipc/src/redact.rs. The CLI generates a v4 UUID per request and sends it as that header. On the desktop, the telemetry_layer axum middleware in apps/desktop/src-tauri/src/cli_server.rs reads or generates it, echoes it back in the response, records it on metrics counters and breadcrumbs, and includes it in a structured log line with only method, path, status, and duration. Bodies, query strings, and auth material never reach the log.
-
apps/desktop/src-tauri/src/cli_server.rsbuilds the axum router, wraps every response inEnvelope, rejects bad auth with an error envelope, writes/clears the lockfile on control-plane start and stop, and runs the request-id telemetry middleware. -
apps/desktop/src-tauri/src/session_svc.rsis called by those handlers to turnRecordStartRequestandShotRequestinto real session work. -
crates/capto-cli/src/client.rsparses envelopes, maps error codes toExitCode, reads the lockfile for discovery, sendsx-request-id, and appliesredactto returned messages.crates/capto-cli/src/main.rsturns the resultingExitCodeinto the process exit status. - The IPC request types mirror the desktop's own internal start and shot arguments in
apps/desktop/src-tauri/src/lib.rs:RecordStartRequestandShotRequestincrates/capto-ipc/src/types.rscarry the samesource/displayId/windowId/regionintent that the UI sends, andRecordStartRequestmirrorsRecordRequestincrates/capto-core/src/ffmpeg_args.rs(shared types come fromcrates/capto-core/andcrates/capto-encode/).
- Add an endpoint field: extend the matching type in
crates/capto-ipc/src/types.rs, use it in the handler inapps/desktop/src-tauri/src/cli_server.rs, and pass it throughcrates/capto-cli/src/client.rsplus the CLI command. Keep#[serde(default)]on any new optional field so older clients and servers stay wire-compatible. - Change the envelope: since
docs/CLI.mddocuments the contract, any change must keepok/data/errorshape and the errorcodestrings backwards compatible; old desktops must still be drivable by a newer CLI and vice versa. - Add a redaction pattern: extend
SECRET_QUERY_KEYSand add amask_*step incrates/capto-ipc/src/redact.rs, with tests alongside it. - Bump the lockfile: change
LOCK_VERSIONand add a migration path inread_server_lockso an old lock cannot be mistaken for a live control plane.
| File | Role |
|---|---|
crates/capto-ipc/src/lib.rs |
Re-exports, API_PREFIX
|
crates/capto-ipc/src/types.rs |
Request and info types |
crates/capto-ipc/src/envelope.rs |
Envelope<T>, ApiError, ExitCode
|
crates/capto-ipc/src/lockfile.rs |
ServerLock, lock lifecycle, is_pid_alive, LOCK_VERSION
|
crates/capto-ipc/src/redact.rs |
redact scrubbing and REQUEST_ID_HEADER
|
redact is used beyond the crate: the capto_* log scrubbing it powers is documented in docs/PRIVACY.md, and the CLI applies it to surfaced error strings in crates/capto-cli/src/client.rs.
- Control-plane API, the endpoint contract built on these types
- capto CLI, the client consuming exit codes and the envelope
-
Desktop app, the control-plane server (
cli_server.rs) -
capto-core,
RecordStartRequestmirrorsRecordRequest - Security, token storage and redaction rationale