Docs navigation
Get started
AI agents
Connect
Operate
Day to day
When it breaks
Govern access
Team & account
Access
Identity concepts
Provider guides
Account
Runner CLI
The
emisar
binary runs the daemon that executes gated actions. It is also the local toolbox for
packs, journals, audit verification, and signed dispatch. This page lists each operator
command, its key flags, and whether it changes the host.
Commands, flags, config keys, and environment variables are v1 compatibility surfaces. See Compatibility and deprecation .
$ sudo emisar status # live daemon + connection summary $ sudo emisar doctor # offline preflight when status reports a problem $ sudo emisar events tail -f # the host-side security log, live $ sudo emisar pack list ID VERSION ACTIONS HASH DESCRIPTION debugging 0.2.10 31 sha256:ae3b9a7c8f11 General-purpose Linux diagnostics + remediation… linux-core 0.3.19 34 sha256:8d0ba17ca0e4 Read-only Linux diagnostics + narrow service control… systemd-deep 0.1.10 24 sha256:84f814476179 Deeper systemd state + operator surface…
Running the CLI#
On an installed host the binary lives at
/usr/local/bin/emisar
and every subcommand auto-discovers its configuration, so you rarely pass --config. The config, cached
token, and packs directory are root-owned, so run commands with sudo. Running
emisar
with no arguments prints a Paths footer showing exactly where this host keeps its config,
packs, token, and journal.
-
—
--config <path>— the config.yaml to load. Defaults to$EMISAR_CONFIG, then the/etc/emisar/config.yamlinstall location, then a per-user~/.config/emisar/config.yaml. -
—
--packs-dir <dir>— extra pack search directories, overriding the config'spaths.packs(repeatable). Lets a read command work without a full config. -
—
--json— machine-readable JSON, onaction list, everypackverb exceptinstallanduninstall,status,doctor,audit verify,version, and the signing verbs. A command that emits no JSON rejects the flag rather than sending human text into your pipeline.
The following table shows the installer's default paths. Configuration keys and the install flags below can change them:
| What | Default path |
|---|---|
| Config | /etc/emisar/config.yaml |
| Packs | /etc/emisar/packs |
| Data dir (dispatch log, signing nonces, runtime status) | /var/lib/emisar |
| Cached runner token | /var/lib/emisar/token |
| Events journal | /var/log/emisar/events.jsonl |
The connect daemon#
emisar connect
is the long-running daemon. It dials out over a TLS websocket, advertises this host's
actions, executes incoming dispatches, streams redacted output back, and journals every
attempt locally. There is no inbound listener on the host. On the first connect it trades the
enrollment key (from the env var named by cloud.enrollment_key_env, by
default EMISAR_ENROLLMENT_KEY)
for a cached per-runner token. The key can be unset afterward.
You normally do not run
connect
by hand. The installer registers it as a supervised service
(emisar.service
under systemd, a LaunchDaemon on macOS) running as the
emisar
user. Manage it through the init system;
reload
sends SIGHUP, which re-reads packs, rebuilds the signing verifier, and re-advertises
the catalog on the live connection — in-flight runs keep executing across it.
$ sudo emisar status # daemon, connection, catalog, and in-flight runs $ sudo systemctl status emisar # is the service process up? $ sudo journalctl -u emisar -f # stream its logs $ sudo systemctl reload emisar # re-read packs + signing config (SIGHUP) $ sudo systemctl restart emisar # full restart
On a
--no-service
host, such as a container, CI box, or cloud shell, there is no unit. Run the daemon with emisar connect --config /etc/emisar/config.yaml.
The
runner fleet
guide covers groups, enrollment keys, and pack credentials.
Actions#
Inspect the catalog that this host advertises. Run one action locally while debugging a
pack.
emisar actions
is an accepted alias for the group.
emisar action run
only to debug a pack on a host you control. It bypasses cloud policy, approvals, signed
dispatch, and cloud audit. It still writes the runner's local event.
| Command | What it does | Mode |
|---|---|---|
| emisar action list | Every action this host advertises — id, pack, kind, risk, title. | Reads only |
| emisar action describe <id> | The full JSON definition of one action, including its argument schema. | Reads only |
| emisar action run <id> | Execute one action on this host now, for debugging a pack. | Runs on the host |
Flags on action run:
-
—
--arg key=value— one argument, repeatable. Numbers,true/false, and JSON arrays/objects are decoded. Everything else stays a string. -
—
--reason "…"— free-text reason recorded on the local event. -
—
--timeout 30s— override the action's timeout, clamped to its own min/max. -
—
--stream— stream stdout/stderr live instead of returning buffered JSON.
Packs#
emisar pack, also available as packs, manages this host's action
catalog. The catalog contains the versioned action schemas that the host can execute.
| Command | What it does | Mode |
|---|---|---|
| emisar pack suggest | Recommend packs for what is running on this host, from the public registry or a --catalog URL, file, or pack directory. | Reads only |
| emisar pack list | Installed packs with version, action count, and content hash. | Reads only |
| emisar pack info <id> | A pack's setup. Required binaries, the env vars its actions read, a verify command. | Reads only |
| emisar pack verify [id...] | Run each pack's declared verify action to check it can reach and authenticate to its target. | Reads its target |
| emisar pack validate <path> | Validate a pack directory on disk without loading it into the runner. | Reads only |
| emisar pack diff <id> | The changed lines between an installed pack and the registry's, with risk and redaction changes called out. | Reads only |
| emisar pack install <name|path|url> | Fetch, validate, hash-verify, and install one pack into the packs dir. | Changes the host |
| emisar pack update [id...] | Update installed packs to the registry's current versions. | Changes the host |
| emisar pack uninstall <id> | Remove an installed pack. Aliases are rm, remove, and delete. | Changes the host |
Flags on pack install:
-
—
--hash sha256:…— pin the exact content hash the portal advertises. The install aborts on any mismatch. A tampered or mismatched copy never reaches the runner. -
—
--force— overwrite a pack already installed under the same id. -
—
--registry <url>— install from your own registry (or setEMISAR_PACKS_REGISTRY). Defaults to the public registry atemisar.dev. -
—
--dest <dir>— the packs directory to install into. Defaults to the config's firstpaths.packsentry.
Flags on
pack diff
(which also takes --registry):
-
—
--to <version>— compare against a specific published version instead of the registry's current one. That URL carries no index entry, so the computed hash is printed for you to pin the install with. -
—
--stat— the summary and the callouts without the changed lines.
# install one pack, pinned to the hash the portal advertises $ sudo emisar pack install redis --hash sha256:9f2c… # see what an upgrade would change, line by line, before applying it $ sudo emisar pack diff redis # update every installed pack (or name a few); preview with --dry-run $ sudo emisar pack update $ sudo emisar pack update redis postgres --dry-run # validate a pack you are authoring, before you trust it $ emisar pack validate ./my-pack
install, update, and
uninstall
signal a running daemon to reload — the same SIGHUP as
systemctl reload emisar
— so the runner advertises changed actions without restarting or dropping a run;
when no live daemon is reachable, the CLI prints the manual reload command instead.
Installing a pack does not trust it: a changed hash lands as
pending
in
Packs
until someone trusts it. Names accept
name=version
to pin a specific published version. See
the pack reference
for what goes in a pack.
A pack that installs cleanly can still be missing a credential.
pack verify
runs the low-risk read each pack declares for the purpose, so a wrong environment variable
surfaces on the host instead of at the first dispatch.
pack install
runs it for the pack it just installed.
# probe every installed pack; name packs to probe only those $ sudo emisar pack verify ok docker docker.ps 26ms ok redis redis.info 18ms skipped gcp-dns gcp.dns_zones needs project — probe it with: emisar pack verify gcp-dns --arg project=<value> failed postgres postgres.uptime failed (exit 2): psql: error: connection to server at "db.internal", port 5432 failed: FATAL: password authentication failed for user "emisar" 2 ok · 1 failed · 1 skipped # supply what the host cannot infer $ sudo emisar pack verify gcp-dns --arg project=acme-prod
A skip is not a failure. A pack whose verify action needs a value this host cannot infer — a
project id, a hostname, a pid — is skipped with the command that completes it, as is a pack
your
admission
config refuses. Only a real failure exits non-zero. Read the detail before concluding the
pack is misconfigured: a target that is simply down fails the same way a bad credential
does.
Diagnose and audit#
When a runner will not connect or an action fails, these read-only verbs answer why without opening a cloud session. What each answer means, and which page owns the fix, is in troubleshooting.
| Command | What it does | Mode |
|---|---|---|
| emisar status | Correlate the live daemon lock and PID with its owner-only runtime snapshot. Reports the last successful heartbeat send, advertised catalog, uptime, connection attempts, and in-flight runs. Exits nonzero when the daemon or connection is unhealthy. | Reads only |
| emisar doctor | Offline preflight: config, credential, dispatch log, pack dirs, packs, action binaries, service, and control-plane reachability. Exits nonzero if any check fails. --probe adds the pack verify run. | Reads only |
| emisar state | Print the runner_state JSON this host advertises to the control plane. | Reads only |
| emisar events tail | Print recent journal events. -f follows new events. --lines sets the count and defaults to 50. | Reads only |
| emisar events cat / grep | Dump the whole journal, or filter it by --action, --type, --event-id, or --request-id. --type is the event_type a SIEM alerts on. | Reads only |
| emisar audit verify [path] | Re-derive a journal file's SHA-256 hash chain and report breaks. --all checks rotated siblings. | Reads only |
| emisar version | The runner version and build info. | Reads only |
$ sudo emisar status # running daemon + connection summary $ sudo emisar doctor # offline preflight when status reports a problem $ sudo emisar events tail -f # the local journal, live $ sudo emisar audit verify --all # re-derive the journal hash chain
status
reads a mode-0600 snapshot written by the running daemon and verifies it against the held
process lock, PID, and heartbeat freshness. It proves only the host's latest successful
send; the console is authoritative for current control-plane receipt.
doctor
never opens a cloud session, and executes no action unless you pass --probe, which adds the
pack verify
run above and so costs one real call per pack. One failing check never aborts the
rest, so one run surfaces every problem.
audit verify
exits 0 when the chain is intact and 1 on the first break, printing the line and event
ID;
--all
walks the active file plus every rotated
.N
sibling, verifying each chain independently.
audit verify
verifies continuity only within the retained journal or retained suffix. It cannot prove
that a privileged host operator did not replace or truncate the entire local journal. The
record that cannot be changed from the host is the off-host cloud audit. Carry both into
your SIEM and cross-reference them. See Audit & SIEM.
Signed-dispatch keys#
emisar signing
sets up bridge-attested dispatch. An enforcing runner then requires a CA-signed certificate
on every action. A compromised control plane can relay a dispatch but never mint one.
Everything here is offline: keys are printed to your terminal and are not sent
anywhere.
| Command | What it does | Mode |
|---|---|---|
| emisar signing init | Mint a CA, leaf key, and certificate. Print the runner config, offline CA private key, and two MCP environment variables. | Prints key material |
| emisar signing new-ca | Mint just the offline CA keypair — the root of trust (rarely). | Prints key material |
| emisar signing new-cert | Sign a short-lived operator cert against the offline CA (routinely, as certs expire). | Prints key material |
-
—
--ca-name(initandnew-ca) — the CA's common name, defaulting toemisar-dispatch-ca. -
—
--ttl 24hand--scope group=prod,env=edge(initandnew-cert) — the cert's validity window, which accepts Go durations plus long-form30d/1yand defaults to 24h, and the runners it binds to. Short is better — the only revocation is expiry, so a long TTL keeps a leaked cert usable longer. An empty scope means any runner that trusts the CA. -
—
--ca-keyand--ca-cert(both required onnew-cert) — the offline CA private key and certificate fromnew-ca. Both are read locally and never transmitted; the certificate is required because an X.509 leaf names its issuer exactly. -
—
--key-name(new-cert) and--key ed25519|p256(all three) — the leaf's common name, and the key algorithm. Choosep256when a KMS or HSM in your chain cannot do Ed25519.
# one-shot: prints the runner config block, the offline CA key, and the MCP env vars $ emisar signing init --ca-name acme-2026 --scope group=prod --ttl 24h # later, renew a cert against the offline CA (no runner reload needed) $ emisar signing new-cert --ca-key <offline CA key> --ca-cert <CA certificate> --scope group=prod
The generated
EMISAR_SIGNING_KEY
and
EMISAR_SIGNING_CERT
are environment variables for the MCP client. The CA private key stays offline. Never put it
on a runner or the control plane.
Signed dispatch
explains enforcement, scope, rotation, and revocation.
Install, update, and removal#
Install the binary with install.sh, then use
emisar update
for manual host-local updates. Both paths use the same staged replacement transaction and
preserve config and packs. The installer requires systemd on Linux
(use
--no-service
for hosts without a real init). This section is the flag reference.
Upgrade runners
explains fleet canaries, verification, batches, and rollback.
emisar update
requires the root-owned receipt written by the official installer. It refuses copied,
containerized, development, and infrastructure-managed binaries. Update those from the
system that deployed them.
# update an official installer-managed runner $ sudo emisar update $ sudo emisar update --version X.Y.Z # install or provision a host $ curl -fsSL https://emisar.dev/install.sh | sudo bash # provision unattended with a fixed pack set $ curl -fsSL https://emisar.dev/install.sh | sudo bash -s -- --version runner-vX.Y.Z $ curl -fsSL https://emisar.dev/install.sh | sudo bash -s -- --yes --packs linux-core,postgres
-
—
emisar update --version X.Y.Z— install an exact stable immutable runner release for a canary or rollback. Without the flag, the command selects the newest stable immutable release. -
—
--version TAG— install a specific runner release (runner-vX.Y.Z,vX.Y.Z, or bareX.Y.Z). Default is the latest. -
—
--yes— skip prompts. Requires an explicit--packs(orEMISAR_PACKS) so an unattended run never guesses. -
—
--packs LIST— install exactly these packs, comma- or space-separated, with no host detection (for cloud-init, Packer, Ansible). -
—
--no-start— install and enable the service but leave it stopped.--no-service— binary only, with no service user, unit, or activation. -
—
--uninstallstops the service and removes the binary, unit, and cached token. It keeps config, data, and logs.--purgewith it also deletes config, data, logs, and the service user. -
—
--bin-dir·--etc-dir·--data-dir·--log-dir·--user— override the install locations and the service account.
# remove the binary, service, and cached token — keep config + journal $ curl -fsSL https://emisar.dev/install.sh | sudo bash -s -- --uninstall # …or delete everything, including config, data, and logs $ curl -fsSL https://emisar.dev/install.sh | sudo bash -s -- --uninstall --purge
After uninstalling, delete the runner from Runners — its run history and audit events survive the host. Removing a runner cleanly, and what changing its enrollment key does, is covered in the runner fleet guide.