Skip to content

Machine Configuration

/etc/wardcell/config.toml holds settings for wardcell itself — as opposed to the policy it enforces. It sits beside the policy root:

/etc/wardcell/
├── config.toml these settings
└── policies/ the policy root

The installer writes it, with every setting at its default and a comment on each, when there is none. It never replaces an existing one, so your edits survive upgrades. No file at all means the defaults.

/etc/wardcell/config.toml
[policy_root]
install_bundled = true
allow_override = true
[workspace_policy]
mode = "extend"
[logs]
file_min_level = "debug"
keep_days = 14
max_runs = 200
[telemetry]
service_name = "wardcell"
timeout_ms = 2000
min_level = "info"

Default true. Whether installing or upgrading wardcell writes its bundled policy tree into /etc/wardcell/policies/.

Set it to false when your organisation manages that directory — through configuration management, MDM, or a package of its own. An upgrade then leaves the policy root exactly as it is, and only the app and CLI change.

With it true, an upgrade replaces the fragments wardcell ships and removes any it no longer ships, but never touches files it did not install: your own fragments and zones.toml survive either way.

Default true. Whether --policy-root and $WARDCELL_POLICY_ROOT may point at a policy root other than /etc/wardcell/policies/.

Set it to false so the installed CLI only builds sandboxes from your organisation’s policy. Naming another root is then refused before anything runs:

error: --policy-root is disabled on this machine: /etc/wardcell/config.toml sets
`policy_root.allow_override = false`, so sandboxes are built only from
/etc/wardcell/policies

Naming /etc/wardcell/policies itself is still allowed.

Default "extend". Whether a workspace’s .wardcell.sbx — the one in the directory a command runs in — is applied:

Mode
"extend"it may add paths, hosts and variables a profile is missing — never reopen anything base.sbx denies, never carry native rules
"off"it is not read

extend makes a workspace policy an escape hatch — a way to keep working while a profile is fixed. Its cost is that anything running as the user outside a sandbox can edit that file, and so can widen the next sandbox in that directory as far as base.sbx allows. Organisations that rely on profiles, not just base.sbx, to keep credentials or networks out of reach should choose off. See Workspace Policies.

Every run writes one JSONL log file, under ~/Library/Logs/wardcell/runs/ on macOS and $XDG_STATE_HOME/wardcell/runs/ (or ~/.local/state/wardcell/runs/) on Linux. These settings govern what is written and how long it is kept. See Viewing Logs.

[logs]
file_min_level = "debug" # least-severe level written to the run file
keep_days = 14 # prune a run's log after this many days
max_runs = 200 # keep at most this many runs
Setting
file_min_levelthe least-severe level written to the run file. Default "debug". "trace" opts into the per-request MITM firehose, which is large
keep_dayshow many days a run’s log is kept. Default 14
max_runsthe most runs retained. Default 200

Retention runs on each run start and on wardcell logs prune, applying keep_days and max_runs together — whichever is stricter. A run still running is never pruned.

Off until telemetry.endpoint names a collector. Set it to an OTLP/HTTP base URL and every run reports itself there — a span per run and the full event stream at min_level and above:

[telemetry]
endpoint = "https://collector.example.com:4318"
service_name = "acme-fleet" # default "wardcell"
timeout_ms = 2000 # how long one export may take
min_level = "info" # least-severe level exported
headers = { "x-api-key" = "..." }

min_level defaults to "info" — the least-severe level exported to the collector. The run file keeps more (down to logs.file_min_level); the export is the quieter cut of the same stream.

A collector that is down, slow or refusing the data never affects a run; it costs timeout_ms and one line on stderr. See Telemetry for every attribute and what is not covered.

headers values are secrets, which is another reason this file is root-owned.

[plugin]
# The program a run asks what wardcell cannot answer itself: the identity an
# outbound request carries, whether an entry into a zone happens, and where a
# service in a registry lives. Started once per run, asked over one socket, and
# killed when the run ends.
path = "/usr/local/libexec/wardcell/wardcell-plugin"

Naming a program here is what starts it, for every run — one plugin per machine, whatever the policy asks of it. What it can answer is discovered at startup, not configured here; zones.toml says what it must support, not whether one runs. Unset, no plugin starts, and a policy root that needed one (an authenticated zone, or a zone requiring approval) then refuses to run rather than proceeding without the identity or approval it promised. See Writing Plugins for the model.

What a zone requires. authenticated and requires_approval live in zones.toml, the sibling of this file in /etc/wardcell. That file says which zones exist and what each one needs; config.toml here says which program answers for them. See Configuring Zones.

Terminal window
wardcell config show # every setting, and the file it came from
wardcell config show --output json
wardcell config get workspace_policy.mode

For the same reason as the policy root: a setting that pins the policy root, or turns workspace policies off, is only a setting if the user it applies to cannot edit it away. wardcell refuses to start if config.toml is not owned by root or is writable by anyone else:

error: /etc/wardcell/config.toml must be owned by root and writable by nobody
else; a setting the user can edit is not one an organisation can rely on.

A misspelt setting is an error too, rather than being ignored — a typo that silently left allow_override at true would leave you believing a restriction is in force.

Sandboxes cannot read the file at all: base.sbx denies /etc/wardcell to everything that runs inside one.

These settings govern the installed wardcell CLI, which is what a managed machine runs. They are not a boundary against a user who compiles their own copy of wardcell, or who does not use wardcell at all — that is what device management and endpoint controls are for.