CapOwn — User Guide¶
Configuration¶
All CapOwn components use a unified TOML format. A single TOML file serves
both as a deployment enrollment config (fed to deploy.py install worker <source> or deploy.py install client <source>) and
as the runtime config read by the component at startup, with no format
translation step needed. Worker and Client config commands can also fetch a
temporary enrollment URL and save the returned TOML into the local runtime
config.
Every config file starts with a role key ("master", "worker",
or "client"). Common keys — tokens, URLs, node identity — live at the
top level. Component-specific settings live under a section named after
the role ([master], [worker], [client]). Deployment-only
settings (mirrors, workspace presets) live under [deploy] and are
silently ignored at runtime.
The parser is a minimal built-in TOML subset — no third-party dependency required on any component, including the client.
Load order:
Unified format reference¶
| Top-level key | Env override | Used by | Description |
|---|---|---|---|
role |
— | all | "master", "worker", or "client" |
enrollment_token |
ENROLLMENT_TOKEN |
Master, Worker | One-time token used for Ed25519-based worker enrollment |
client_token |
CLIENT_TOKEN |
Master, Client | Token for client/agent authentication |
master_url |
MASTER_URL |
Worker, Client | Master API base URL |
worker_name |
WORKER_NAME |
Worker | Unique identifier for this worker |
Master (master/config.toml)¶
role = "master"
[master]
host = "0.0.0.0"
port = 9210
heartbeat_timeout = 60
db_path = "/app/data/registry.db"
User tokens and enrollment credentials are managed on the Master host after
deployment using capown-master commands — see the
Multi-User Model section.
[master] key |
Env override | Default | Description |
|---|---|---|---|
host |
MASTER_HOST |
0.0.0.0 |
Bind address |
port |
MASTER_PORT |
9210 |
Listen port |
heartbeat_timeout |
HEARTBEAT_TIMEOUT |
60 |
Seconds before marking node offline |
db_path |
MASTER_DB |
/app/data/registry.db |
SQLite database path (container-side) |
The Master container reads /etc/capown/master.toml at runtime. The
Compose file mounts the host config directory ~/.capown/master/ into
the container.
Worker (worker/config.toml)¶
role = "worker"
master_url = "https://<master-domain>:9210"
worker_name = "<unique-worker-name>"
enrollment_token = "cown_enroll_xxx"
[worker]
execution_mode = "container"
container_name = "capown-worker-exec"
workspace = "/workspace"
max_runtime = 86400
idle_timeout = 600
max_output_size = 200000
max_input_size = 200000
reconnect_interval = 5
store_command_history = true
command_preview_size = 300
max_command_history_size = 65536
[worker] key |
Env override | Default | Description |
|---|---|---|---|
execution_mode |
EXECUTION_MODE |
container |
Task execution backend: "host" or "container" |
workspace |
WORKSPACE_DIR |
/workspace |
Workspace path (container: /workspace; host: ~/.capown/workspace) |
container_name |
CONTAINER_NAME |
capown-worker-exec |
Execution container name |
max_runtime |
MAX_RUNTIME |
86400 |
Maximum wall-clock time for a task in seconds |
idle_timeout |
IDLE_TIMEOUT |
600 |
Seconds without output before killing a task |
max_output_size |
MAX_OUTPUT_SIZE |
200000 |
Maximum result output bytes before truncation |
max_input_size |
MAX_INPUT_SIZE |
200000 |
Maximum input content bytes accepted |
reconnect_interval |
RECONNECT_INTERVAL |
5 |
Seconds between reconnect attempts |
store_command_history |
STORE_COMMAND_HISTORY |
true |
Enable shell command history storage |
command_preview_size |
COMMAND_PREVIEW_SIZE |
300 |
Max UTF-8 bytes for command previews in task summaries |
max_command_history_size |
MAX_COMMAND_HISTORY_SIZE |
65536 |
Max UTF-8 bytes for stored full command text |
Config file locations (resolved in order of priority):
$CAPOWN_CONFIGor$CAPOWN_WORKER_CONFIGenvironment variable~/.capown/worker/config.toml/etc/capown/worker.tomlworker/config.toml(dev mode fallback)
The Worker control process always runs on the host as a native OS service,
regardless of execution mode. In container execution backend mode, the
deploy script additionally sets up a managed Docker execution container.
The worker daemon uses docker exec internally to run tasks inside that
container. In host execution backend mode, tasks run directly on the host.
The deploy script installs a stable application copy under
~/.capown/worker/app and runs it through a uv-managed virtual environment
at ~/.capown/worker/venv with Python 3.12.
Client (client/config.toml)¶
role = "client"
master_url = "https://<your-domain>/v1"
client_token = "<your-client-token>"
[client]
soft_timeout = 30
[client] key |
Env override | Default | Description |
|---|---|---|---|
soft_timeout |
CLIENT_SOFT_TIMEOUT |
30 |
Synchronous task soft timeout in seconds |
The client intentionally has no default worker. Run capown workers and
pass <worker> on every worker operation.
Client config discovery (searched in order):
$CAPOWN_CLIENT_CONFIGenvironment variableclient/config.toml(alongside the script)./client/config.toml(current working directory)~/.config/capown/client.toml~/.capown/client/config.ini(legacy INI fallback)
The client parser is a minimal built-in TOML implementation — no
tomllib or tomli dependency needed. Legacy config.ini files
are still accepted for backward compatibility.
Config-Driven Deployment¶
For the curl-based four-step install path, see Getting Started.
CapOwn supports non-interactive deployment via config files. Because the runtime and deployment formats are the same, the Master operator can generate a TOML config or a temporary enrollment URL, hand it to a target machine operator, and it works without any format conversion.
Generate Enrollment Credentials¶
Enrollment credentials are generated on the Master host after it is installed.
On a fresh Master installation, the deployment script automatically creates
the starter user chiral and one bundle enrollment token. The generated
install commands appear in the deploy output.
The simplest manual flow uses the configured public_url and creates the user
if it does not exist yet:
If you haven't set a public URL yet, or need an explicit override:
The default tokens create <username> is equivalent to tokens create <username> --type bundle, using public_url from the Master config when --master-url is omitted.
tokens create --type bundle prints ready-to-run install commands:
Worker: python3 deploy.py install worker https://master.example.com/api/enroll/cown_tmp_xxxxx
Client: python3 deploy.py install client https://master.example.com/api/enroll/cown_tmp_xxxxx
Files: ~/.capown/master/enrollments/alice
The same temporary URL can also be used by capown-worker config and
capown config when the software is already installed; the CLI sends its role
to the Master and receives the matching TOML. No --role flag is needed.
By default, tokens create --type bundle also writes Worker and Client TOML files under
~/.capown/master/enrollments/<username>/ for file-based workflows. Use
--url-only to skip file generation.
Useful bundle options:
capown-master tokens create alice --type bundle --master-url https://master.example.com --ttl 86400
capown-master tokens create alice --type bundle --master-url https://master.example.com --client-uses 1 --worker-uses 3
capown-master tokens create alice --type bundle --master-url https://master.example.com --max-uses 2
capown-master tokens list alice --type enrollment
capown-master tokens revoke <token-id>
Treat temporary URLs and generated TOML files as secrets because they contain or grant bearer credentials.
For full user management workflow, see the Multi-User Model section.
Deploy from a Source¶
The deploy.py install command accepts a unified <source> argument that can
be a local TOML file, a TOML URL, or a bundle enrollment URL:
# One-step deployment from a bundle enrollment URL (recommended)
python3 deploy.py install worker https://master.example.com/api/enroll/cown_tmp_xxxxx
python3 deploy.py install client https://master.example.com/api/enroll/cown_tmp_xxxxx
# Deploy from a local TOML file
python3 deploy.py install worker capown-worker.toml
python3 deploy.py install worker capown-worker.toml --mirror-cn # China mirrors
# Deploy from a TOML URL
python3 deploy.py install worker https://master.example.com/enrollments/alice-worker.toml
If CapOwn is already installed and you only need to reconfigure identity, use the
two-step flow with the config subcommand:
# Already installed -- reconfigure identity only
capown-worker config https://master.example.com/api/enroll/cown_tmp_xxxxx
capown config https://master.example.com/api/enroll/cown_tmp_xxxxx
# Or from a local TOML file
capown-worker config capown-worker.toml
capown config capown-client.toml
The same bundle URL works for both roles; the CLI sends its role to the
Master and receives the matching TOML. No --role flag is needed.
When install is used with a source the deployment is fully automated:
- Reads the
rolefield from the config — no role selection prompt. - Uses mirror from config,
--mirror-cnflag, or defaults to international mirrors — no mirror prompt. - Resolves local defaults (e.g.,
workspace_preset = "user_home"resolves to$HOME/.capown/workspace). - Shows a parameter review panel, then proceeds without confirmation.
- Auto-stops any existing running services — no stop/restart prompts.
Re-running an install command without a source updates software and preserves
existing local config. Running deploy.py install worker <source> or
deploy.py install client <source> explicitly applies that source and
replaces the local identity. Use capown-worker config <source> or
capown config <source> when the software is already installed and you only
want to replace identity. Use deploy.py uninstall <component> --purge only
when you want to remove config and data.
Agent Interfaces¶
CapOwn supports two agent-facing interfaces over the same Master and Worker control plane:
- MCP Streamable HTTP at
/mcpfor MCP-capable agent hosts. It uses the existing client bearer token and exposes Worker, shell, file, system, and task tools. capownCLI plus the agent skill for hosts without MCP support, scripts, and restrictive networks where short HTTPS requests and a stdlib-only client are preferable.
Both interfaces enforce the same user ownership and Worker workspace rules.
See the MCP Guide for MCP configuration and skills/capown-client/SKILL.md for
the CLI workflow.
CLI Usage¶
The capown CLI is the recommended interface. It is installed by
deploy.py into ~/.capown/bin/ or can be run directly via
python -m client.cli.
Structured results use JSON. Raw command and file outputs stay raw.
CapOwn-layer errors use error[code]: message.
# List registered workers
capown workers
# Run a shell command (synchronous, waits for result)
capown run worker-1 "uname -a"
# Read a file
capown read worker-1 /etc/hostname
# Write a file (content from stdin)
printf '%s' "hello world" | capown write worker-1 /workspace/hello.txt --stdin
# Write a file (content from local file)
capown write worker-1 /workspace/hello.txt --file local.txt
# Edit a file (old/new text from local files)
capown edit worker-1 /workspace/file.txt --old-file old.txt --new-file new.txt
# Edit a file (old/new text as JSON on stdin)
printf '{"old": "foo", "new": "bar"}' | capown edit worker-1 /workspace/file.txt --json-stdin
# List directory contents
capown ls worker-1 /tmp
# List directory contents with limit
capown ls worker-1 /tmp --limit 20
# Find files by glob pattern
capown find worker-1 "*.log"
capown find worker-1 "**/*.py"
# Search file content (with optional --regex)
capown search worker-1 "TODO"
capown search worker-1 "error_\w+" --regex
# Get system info
capown info worker-1
# Dispatch a long-running command (async, returns task_id)
capown dispatch worker-1 "sleep 30 && echo done"
# Check task status by task_id (worker-local ID)
capown task <worker> <task_id>
# Wait for a task to finish
capown wait worker-1 <task_id>
# Retrieve the stored full shell command for a completed shell task
capown task-command worker-1 <task_id>
# List recent shell command previews (default: 10 shell tasks)
capown history worker-1
capown history worker-1 --limit 20
capown history worker-1 --all-statuses --preview-bytes 500
# Cancel a running task
capown cancel worker-1 <task_id>
# List pending/running tasks across workers
capown pending
capown pending --worker worker-1
The script-based invocation python client/capown_client.py still works for
backward compatibility but the capown command is recommended for new
setups.
Direct API Usage¶
Tasks can also be dispatched directly via curl:
# Via nginx proxy (external clients):
curl -X POST https://<master-domain>/v1/api/tasks/dispatch \
-H "Authorization: Bearer <client-token>" \
-H "Content-Type: application/json" \
-d '{"target_worker": "worker-1", "payload": {"task_type": "shell", "params": {"command": "uname -a"}}}'
# Or direct to Master port (local):
curl -X POST http://127.0.0.1:9210/api/tasks/dispatch \
-H "Authorization: Bearer <client-token>" \
-H "Content-Type: application/json" \
-d '{"target_worker": "worker-1", "payload": {"task_type": "shell", "params": {"command": "uname -a"}}}'
For a complete list of all Master HTTP endpoints with request/response shapes, authentication modes, and error codes, see the Master API Reference.
Worker Identity¶
Each Worker installation has two identities:
| Identity | Scope | Description |
|---|---|---|
worker_name |
User-facing, renameable | The name used to address the worker in capown commands |
worker_id |
Internal, stable | Format wrk_ followed by 12 hex characters, generated once per installation |
worker_name rules:
- ASCII only, lowercase
- 3 to 48 characters
- Must start and end with a lowercase letter or digit
- May contain lowercase letters, digits, dots (.), hyphens (-), underscores (_)
- Must not start with wrk_ (reserved for worker_id)
- Must not be a reserved word (master, admin, all, none, etc.)
- Must be unique per owner user
Enrollment configs do not contain a worker_id. The worker obtains its
identity during daemon enrollment/registration, then stores worker_id in
~/.capown/worker/identity.toml so it persists across re-deployments. When a
worker's worker_name is changed, the Master recognises it as the same worker
through worker_id and updates the registry entry instead of creating a
duplicate offline node.
The capown workers listing always shows the current worker_name. The
worker_id is visible in the API response for diagnostic purposes.
Do not manually edit the identity.toml file. Deleting it causes a new
worker_id to be generated, making the worker appear as a new node to
the Master.
Worker Rename Flow¶
A worker can be renamed after deployment. The rename preserves worker_id
and tracks the previous name for auditing.
# From the worker host (validates name and updates Master + local files):
capown-worker rename <new-worker-name>
# From the master host (direct database access):
capown-master workers rename <worker_id> <new-worker-name>
Rename requirements:
- The new name must follow worker_name validation rules
- The name must not be in use by another worker owned by the same user
- A rename will not cause task disruption — the worker_id used for
internal routing remains stable
Metadata Refresh on Restart¶
When the worker daemon restarts, it automatically reports its current runtime metadata to the Master after re-authenticating. This includes:
- Hostname: the machine's network name (
platform.node()) - Operating system: e.g.
linux,windows,darwin - Execution mode:
containerorhost - Enabled capabilities: the set of task types the worker supports
- Workspace path: the directory where file operations occur
No CLI action is needed — the metadata is refreshed as part of the daemon's startup sequence. If the reconnect call fails temporarily, the worker retries with the next connection cycle.
Worker Local Administration¶
The capown-worker command manages the Worker service locally. It is
installed by deploy.py into ~/.capown/bin/ during deployment.
Status and Diagnostics¶
.. code-block:: bash
capown-worker status # Show Worker status, identity, and paths
capown-worker doctor # Run lightweight health checks
status shows the Worker service status, configured identity, execution
mode, workspace paths, and container status (when in container mode).
Workspace Management¶
.. code-block:: bash
capown-worker workspace show # Show current workspace
capown-worker workspace set /srv/new-project # Change workspace (with confirmation)
capown-worker workspace set /srv/new-project --yes # Change workspace (non-interactive)
workspace set validates that the path is absolute and already exists as a
directory, displays current and new settings, requests confirmation (unless
--yes), updates the config file, and automatically restarts the Worker
service.
In container mode, workspace set updates the host-side mount path and
recreates the execution container with the new -v host:container mount,
so workspace changes take effect immediately. The container-side workspace
path (/workspace) remains unchanged.
Service Lifecycle¶
.. code-block:: bash
capown-worker start # Validate config, then start service
capown-worker stop # Stop service
capown-worker restart # Validate config, then restart service
capown-worker logs [-f] # Show service logs
start and restart validate the Worker configuration before changing
the running service. If the configuration is invalid or missing, the action is
aborted with an actionable error message. stop intentionally does not
validate config, so it can still stop a local Worker when the config file is
broken.
Validation checks: valid execution_mode (host/container), absolute
workspace path, Docker availability in container mode, and execution
container readiness. Identity warnings are printed but do not block lifecycle
actions.
In container mode, the execution container is recreated (docker rm -f
+ docker run) rather than simply restarted, so mount path or other
container definition changes take effect when you run capown-worker
restart.
Configuration¶
.. code-block:: bash
capown-worker config # Show config location and masked tokens
capown-worker config <source> # Enroll or reconfigure from source
Master Management CLI¶
The capown-master command manages the Master service from the host
machine. It is installed by deploy.py into ~/.capown/bin/ alongside
the Master deployment.
# Show container status and configured paths
capown-master status
# Start / stop / restart the Master service
capown-master start
capown-master stop
capown-master restart
# View container logs (add -f to follow; add --tail <n> for last N lines)
capown-master logs
capown-master logs -f
capown-master logs --tail 50
# Check Master health endpoint
capown-master health
# Show config and data locations
capown-master config
# Set Master public URL (required for default bundle enrollment)
capown-master config set public-url https://master.example.com
# Show version information
capown-master version
# --- Multi-User Management ---
# Create users explicitly when you need to manage them before issuing tokens.
# Otherwise, tokens create <username> auto-creates a missing user.
capown-master users add alice
capown-master users add bob
# Rename a user by stable uid
capown-master users rename <uid> alice-new
# Set a password for a user (optional, for future dashboard login)
capown-master users set-password <uid>
# Change a user's role after creation
capown-master users set-role <uid> admin
# List and inspect users
capown-master users list
capown-master users show alice
# Disable / re-enable users
capown-master users disable alice
capown-master users enable alice
# Default bundle enrollment (uses configured public_url)
capown-master tokens create alice
# With explicit URL override
capown-master tokens create alice --type bundle --master-url https://master.example.com
# List and revoke tokens
capown-master tokens list alice
capown-master tokens revoke <token-id>
# Worker- or Client-only enrollment
capown-master tokens create alice --type worker --master-url https://master.example.com
capown-master tokens create alice --type client --master-url https://master.example.com
# Admin tokens (current-API-only, pre-existing; superseded by future dashboard auth)
capown-master tokens create alice --type admin
capown-master tokens list alice --type enrollment
capown-master tokens show <token-id>
capown-master tokens revoke <token-id>
The capown-master command communicates with the Master Docker container
and requires Docker to be available on the host.
User management and enrollment commands access the Master SQLite database directly from the host and do not require the Docker container to be running.
Multi-User Model¶
The Master supports multiple users with per-user tokens and node ownership. This allows a single Master instance to serve a small trusted group where each member has their own workers.
Concepts¶
- User: A named account on the Master. Each user has their own client and worker tokens.
- Token: A bearer token tied to a specific user. Tokens are stored as SHA-256 hashes — plaintext values are only shown at creation time.
- Ownership: When a worker registers with a user's worker token, that worker becomes owned by that user. A user's client token can only dispatch tasks to workers they own.
Workflow¶
- The Master operator sets the Master public URL during install with
deploy.py install master --public-url <url>or later withcapown-master config set public-url <url>. - On a fresh install, deploy.py automatically creates the starter user
chiraland a bundle enrollment token. To create additional users:capown-master tokens create <username>. Missing users are created automatically. - Worker and Client install commands are printed. Distribute the temporary URL or generated files securely to each user.
- Each user installs their worker/client and runs
capown-worker config <source>orcapown config <source>, or usesdeploy.py install worker <source>ordeploy.py install client <source>with the enrollment source. - Workers register with their per-user worker token and are bound to that user.
- Clients dispatch tasks with their per-user client token and can only operate their own workers.
Enrollment URLs and Files¶
capown-master tokens create --type bundle generates a short-lived URL and, by default,
local TOML files under ~/.capown/master/enrollments/<username>/. Only one
active bundle URL is allowed per user -- generating a new one automatically
revokes any previous active bundle URL for the same user. Treat both
forms as secrets:
- Transfer them through a secure channel.
- Revoke unused temporary URLs with
capown-master tokens revoke <token-id>. - Delete generated TOML files from the Master host after distribution if not needed.
- Do not commit them to version control.
Temporary bundle URLs are consumed by capown-worker config <source> and
capown config <source>. Generated TOML files are compatible with
deploy.py install worker <source> or deploy.py install client <source>.
Data Retention¶
Task status queries are forwarded to the target worker via dispatch_sync. The Master does not persist task history — it only routes tasks and tracks node liveness. Use capown task <worker> <task_id> to query status from the worker.
Shell command history is stored only on the Worker, in memory (TTL 86400s, max 1000 entries). The Master does not persist command history. Use capown history <worker> to list recent shell command previews, or capown task-command <worker> <task_id> to retrieve the stored full shell command for a specific task. Command history can be disabled per Worker via the store_command_history config option.
Structured Error Codes¶
Task results include machine-readable error codes (worker_offline, auth_denied, schema_invalid, etc.). The authoritative list is defined in shared/protocol.py. The error_code field is null/absent when the task succeeds. The truncated boolean flag is true when output was capped due to size limits.
Capability Vocabulary¶
Workers advertise their capabilities as compact strings (e.g. shell.run, file.read, task.status). The authoritative capability-to-task-type mapping is defined in shared/protocol.py.
Capabilities appear in the capown workers listing and are used by Agents to select an appropriate worker for a task.