Skip to content

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:

environment variables > configuration file > defaults

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):

  1. $CAPOWN_CONFIG or $CAPOWN_WORKER_CONFIG environment variable
  2. ~/.capown/worker/config.toml
  3. /etc/capown/worker.toml
  4. worker/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):

  1. $CAPOWN_CLIENT_CONFIG environment variable
  2. client/config.toml (alongside the script)
  3. ./client/config.toml (current working directory)
  4. ~/.config/capown/client.toml
  5. ~/.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:

# Quick default bundle enrollment (uses configured public_url)
capown-master tokens create alice

If you haven't set a public URL yet, or need an explicit override:

capown-master tokens create alice --type bundle --master-url https://master.example.com

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:

  1. Reads the role field from the config — no role selection prompt.
  2. Uses mirror from config, --mirror-cn flag, or defaults to international mirrors — no mirror prompt.
  3. Resolves local defaults (e.g., workspace_preset = "user_home" resolves to $HOME/.capown/workspace).
  4. Shows a parameter review panel, then proceeds without confirmation.
  5. 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 /mcp for MCP-capable agent hosts. It uses the existing client bearer token and exposes Worker, shell, file, system, and task tools.
  • capown CLI 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: container or host
  • 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

  1. The Master operator sets the Master public URL during install with deploy.py install master --public-url <url> or later with capown-master config set public-url <url>.
  2. On a fresh install, deploy.py automatically creates the starter user chiral and a bundle enrollment token. To create additional users: capown-master tokens create <username>. Missing users are created automatically.
  3. Worker and Client install commands are printed. Distribute the temporary URL or generated files securely to each user.
  4. Each user installs their worker/client and runs capown-worker config <source> or capown config <source>, or uses deploy.py install worker <source> or deploy.py install client <source> with the enrollment source.
  5. Workers register with their per-user worker token and are bound to that user.
  6. 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.