Skip to content

Mission Control

Mission Control is HAR’s open-source local dashboard. It combines run records and slot state from registered repositories without replacing the repository-owned harness.

Terminal window
har control up

The CLI pulls the Mission Control image matching its own version and starts the app plus PostgreSQL with Docker Compose. Build the image from a source checkout instead:

Terminal window
har control up --build

Stop it with har control down.

Onboarding a repository remembers it for synchronization. You can also register one explicitly:

Terminal window
har control register --repo /path/to/project
har control sync --repo /path/to/project

Sync also runs automatically at every activity edge — login, launch, verify, commit, complete — so registered repositories stay current without a manual sync or a background watcher. --dry-run previews registration or sync, and sync --json produces structured output.

Remove a repository from Mission Control (and stop auto-sync) from the CLI or the repo detail page (Unregister):

Terminal window
har control unregister --repo /path/to/project
# non-interactive:
har control unregister --repo /path/to/project --yes --delete-worktrees

Unregister deletes synced runs/slots/telemetry for that path and records a blocklist entry so background sync does not immediately re-add it. The CLI proposes deleting session worktrees; confirm interactively or pass --delete-worktrees. The dashboard checkbox does the same, but the packaged Docker image often cannot see host worktree paths — use the CLI when host cleanup is required. Re-register with har control register.

To wipe the entire Mission Control database (and optionally scrub local harness history so a later register/sync starts empty), use Settings → Danger zone in the dashboard, or the CLI:

Mission Control Settings danger zone with clear-all confirmation sheet

Terminal window
har control reset
# non-interactive:
har control reset --yes

Reset deletes every repository row (runs, slots, validations, telemetry cascade) and clears the unregister blocklist. Cloud / portal credentials are kept. With local scrub enabled (default), it also removes .har/runs, .har/validations, .har/commit-bindings, .har/state, and .har/slots under each registered repository, and clears ~/.har/repos.json. Packaged Docker Mission Control may not see host paths — prefer the CLI when host scrubbing is required. Re-register afterward with har control register.

Factory is the default Mission Control experience. It answers four questions:

  1. What durable piece of work is this?
  2. Which isolated attempt executed it?
  3. What ran, how long did it take, and what did the agent cost?
  4. Which exact Git tree passed full verification?

Mission Control Factory overview showing completed and active work units

Launch with a short work identifier plus source metadata to create that evidence chain:

Terminal window
har env launch 1 \
--work-id "widget-123" \
--work-source github \
--work-url "https://github.com/acme/widget/issues/123" \
--work-title "Add saved filters"

Bind on launch when the task names a tracker issue or ticket. Use a repo-scoped --work-id (not a provider-prefixed composite such as github:owner/repo#123); pass the provider in --work-source and the canonical link in --work-url. Skip binding for ad-hoc work with no tracker identity.

Every fresh launch creates an immutable attempt ID. Runs and telemetry inherit the work and attempt correlation. Full verification produces exact-tree proof, and har env complete 1 records the completed outcome before teardown.

External issue → Work unit → Attempt → Slot/worktree → Runs → Exact-tree validation

Factory derives active, failed, and verified state from that evidence. It stores explicit state only for business outcomes such as completed or abandoned. A plain teardown is operational cleanup and never claims success.

Historical repositories remain compatible: runs, validations, and slots without work metadata continue to appear under Operations and repository detail. They are not guessed into work units.

Repository detail links to Factory lines (/repos/<id>/lines): a board over the factory lines installed in that repository. It shows stations in order, the cumulative gate and which of its stages are green, the workstations currently in use, and the skills, MCP servers and plugins each line declares.

The board reads the repository’s own .har/lines.json and program — the same source har line status reads — so the dashboard and the CLI always agree. It is read-only: installing or adapting a line is har line add plus the adaptation prompt that install writes, and running a gate is har line gate <station>.

Line gate stages are absent from verificationStages by design, so the board never lists them among verification stages. It says so on the card, and flags the reverse case — a line stage that has leaked onto the verify plan — as an error, matching har env doctor.

A repository with a harness and no line bundle gets an empty state naming the CLI, not an install button.

The repository overview summarizes registered projects, total runs, active slots, and harness profiles. Repository detail provides:

  • active slots, worktrees, branches, preview URLs, dirtiness, and drift;
  • a run timeline with stage, trigger, duration, and status;
  • a History graph of content snapshots and the commits that share them, laid out by branch. Selecting a node opens the record of the attempt that produced it: verification stages, the attempt timeline, agent trajectory and the bound work unit. Slots are live data and are not linked from the record;
  • expected validation stages and their latest results;
  • exact-tree content snapshots (formerly listed only as change batches) and associated commits;
  • files under .har/artifacts/;
  • verification trends and pass rates.

The global Operations page (/worktrees) lists session worktrees across repositories (active and idle), with an On disk indicator, row selection, select-all, and Delete selected to remove worktree directories and clear the matching Mission Control slot rows (repository registration is kept). It also highlights dirty/missing paths and shows token and cost columns when agent telemetry is enabled (see below). Click a row for the slot leaf (LLM usage + Verify pipeline + session timeline). Packaged Docker Mission Control may not see host paths — use har env teardown <id> on the host when deletion fails. The repository catalog lives at /repos; cross-repo usage rollup at /usage.

Agent usage telemetry (Cursor / Claude / Codex)

Section titled “Agent usage telemetry (Cursor / Claude / Codex)”

HAR attributes Cursor, Claude Code, and Codex activity to each worktree/session via @osfactory/otel-hook and shows it in Mission Control. Preference shape:

{ "enabled": true, "signals": { "metrics": true, "logs": true, "prompts": true, "traces": true } }

Default: full telemetry on (traces + logs + derived metrics + prompts). Install (npm install -g @osfactory/har) and the first har / MCP invocation persist this preference to ~/.har/telemetry.json when the file is missing. Prompt text also fills the Mission Control purpose column from the first captured user prompt.

Terminal window
har telemetry status
har telemetry on # full telemetry + Mission Control + @osfactory/otel-hook
har telemetry on --no-prompts # keep traces/logs/metrics; disable prompt text capture
har telemetry install-hooks # re-run Cursor / Claude / Codex hook registration
har telemetry off # clear hooks OTLP export + stop MC auto-start; keeps historical rows

Preference is stored in ~/.har/telemetry.json. Override with HAR_TELEMETRY=0|1.

Hooks fire on session start, every prompt, before and after every tool call, and on stop — six or more times per turn. Telemetry that blocks would therefore be paid several times over on every single turn, so the registration HAR writes is bounded on both sides:

  • Each generated hook entry carries an explicit timeout (10s), instead of the agent’s much longer default.
  • The wrapper caps the export itself (1.5s) and the flush (0.5s), and always exits 0. A dropped telemetry event must never fail a tool call.

The practical consequence: if Mission Control is not running, a turn costs a fraction of a second per hook rather than stalling. har telemetry status warns when hooks are installed and the collector is unreachable, since that combination is pure cost.

Hooks config lives at ~/.har/otel-hooks/otel_config.json (HAR_OTEL_HOOKS_HOME to override). The package itself is installed under ~/.har/otel-hooks/node_modules (pinned @osfactory/otel-hook).

Run har telemetry install-hooks after upgrading HAR. HAR installs the TypeScript package in its managed hooks directory, replaces existing HAR hook registrations, and then removes the legacy opentelemetry-hooks tool when it was installed with uv or pipx. The command name remains otel-hook, but HAR registrations use ~/.har/otel-hooks/run-otel-hook.sh; the old Python executable does not need to remain on PATH. Python is no longer required for HAR telemetry.

For a standalone, user-wide otel-hook command outside HAR, install it separately with npm install -g @osfactory/otel-hook.

When telemetry is on:

  1. har env launch auto-starts Mission Control if it is not reachable (har control up).
  2. har telemetry on (and launch) install @osfactory/otel-hook and register Cursor / Claude / Codex hooks to export OTLP http/protobuf to {HAR_CONTROL_API_URL}/api/otel.
  3. Launch writes session attribution into .env.agent.<id> (HAR_SESSION_KEY, resource attrs). Mission Control matches sessions by har.session_key or by workspace/cwd → slot work dir.
  4. Token usage is derived from span gen_ai.usage.* attributes. har control sync also harvests local Claude/Codex session files as a fallback when hooks telemetry is missing.

Privacy: prompt text is included by default and stays in local Mission Control (SQLite). Opt out with har telemetry on --no-prompts or disable everything with har telemetry off.

No agent content? Check the prompts signal

Section titled “No agent content? Check the prompts signal”

Prompt capture stays on by default — that is a product choice, not an oversight: without it Mission Control and a hosted portal have no session content to show, and a surface with no turns is indistinguishable from a session that had none. Turning it off is the deliberate opt-out, and it is silent by design:

  • With prompts off (har telemetry on --no-prompts, or har telemetry off), the Claude transcript harvest contributes zero events — and it is the only event harvest there is, since the Codex one covers usage alone. Anything that still appears is mirrored from otel spans, so turn counts read 0 for sessions that really had turns.
  • har telemetry status prints the effective signals; har control sync --full warns when prompt capture is the reason no content is leaving the machine.
  • A harvested stream longer than 1000 events keeps the most recent 1000 and marks the oldest surviving event with har.harvest.truncated, har.harvest.dropped_events and har.harvest.total_events, so a clipped session is labelled as clipped rather than looking complete.

Harvested events cover every transcript the slot owns, and fall back to the main checkout’s only when the slot has none — the same primary/fallback tier the token harvest uses, so content and usage always describe the same session.

The slot Trajectory tab is assembled from an append-only ledger, not from the usage/event tables. OTLP ingest and harvest write canonical facts first; event, span, and usage rows are projections of those facts.

Concern Behavior
Order Producer sequence, then event timestamp, then source / event id / content key. Ingestion order is not canonical.
Duplicates The same (source, sourceEventId, contentKey) is stored once. Replayed OTLP does not create a second row.
Late arrival Out-of-order facts keep their sequence. The live viewer repairs gaps after reconnect instead of appending blindly.
Partial content contentDisclosure records full, redacted, masked, truncated, withheld, or metadata_only. Withheld and metadata-only bodies are not rendered.
Secrets Attribute leaves such as authorization or api_key are stored as [redacted] and never used as prompt/response text.
Size Bodies over HAR_TRAJECTORY_MAX_PAYLOAD_BYTES (default 64 KiB) are truncated. Binary tool payloads are omitted.
Retention HAR_TRAJECTORY_RETENTION_DAYS=0 (default) keeps rows. A positive value expires ledger, event, and span rows older than that. Usage totals stay.
Export / delete The slot viewer can download the session as local JSONL or delete that session’s trajectory. Factory reset still wipes the whole database.
Local vs remote Default Mission Control is local SQLite. Export is a local download. A hosted portal receives these bodies only after you opt in (see below).

har control sync pushes runs, slots and token counts to a hosted portal, but the ledger’s prompts, tool arguments and tool results stay local until you say otherwise:

Terminal window
har control trajectory # show the current setting
har control trajectory on # forward prompts / tool payloads
har control trajectory off # back to counts and events only (default)

HAR_PORTAL_TRAJECTORY=on|off overrides the stored choice for one sync, and telemetry must be on either way. Use har control trajectory on --target … when this checkout is attached to more than one HQ workspace — forwarding is stored per connection so a private dev workspace can receive prompts without enabling them for production. What is forwarded goes through the same policy as what is stored: bodies are capped at HAR_TRAJECTORY_MAX_PAYLOAD_BYTES, secret attributes are [redacted], and contentDisclosure travels with each record so a truncated or withheld body is labelled as such remotely instead of arriving as apparently-complete text. The policy is applied again on the way out, so tightening the cap also bounds records stored under a looser one.

Forwarding is idempotent on (source, sourceEventId, contentKey) and watermarked independently of the event and run batches — enabling it later still sends the backlog rather than starting from that moment.

Projected spans ride along with telemetry rather than with this opt-in: they carry call structure and timing, and their attributes were already redacted when the span was projected.

Mission Control’s source has its own harness:

Terminal window
cd control
har env launch 1

Use this path for development, manual testing, and screenshots. Do not run it on the same port as the packaged har control up instance. Harness preflight detects the conflict and can select another port in the slot lane.

har hq connect opens browser SSO (or stores --api-key) and attaches the current repository to the workspace you pick in the portal. Other locally registered repositories are listed with a count so you can send all of them to one workspace, or assign leftover paths per workspace. Unattached checkouts are not ingested into the only leftover connection. Saved connections live in ~/.har/portal-targets.json; har hq list / har hq disconnect manage them. Workspace choices are never committed into .har/. har control login still works as a deprecated alias.

har control sync --cloud targets HAR Cloud. Hosted coordination is separate from the local open-source dashboard and portable .har/ contract.