libra agent

Command reference for `libra agent`

Manage external-agent capture for Claude Code, Codex, and OpenCode.

Synopsis

libra agent status
libra agent list [--schema-version <1|2>] [--json]
libra agent import (--session <id> | --path <path> | --since <rfc3339> | --all) [--agent <name>] --yes
libra --json agent graph <session> [--repo <path>]
libra agent enable [--agent <name>]...
libra agent add [<name>...]
libra agent disable [--agent <name>]...
libra agent remove [<name>...]
libra agent session <subcommand>
libra agent checkpoint <subcommand>
libra agent skill <subcommand>
libra agent clean [--all]
libra agent doctor [--repair]
libra agent workspace list [--limit <n>] [--cursor <token>] [--state <state>]...
libra agent workspace show <workspace-id>
libra agent push [--remote <name>] [--force-rewrite]
libra agent rpc <subcommand>
libra agent bridge --stdio

Description

libra agent manages Libra's external-agent capture surface. It installs and removes provider hooks, reports captured session/checkpoint state, exposes read-only diagnostics, and can push refs/libra/traces to a remote.

The supported roster is claude-code, codex and opencode (first batch), and all three are hook-installable: claude-code writes .claude/settings.json, codex writes user-level $CODEX_HOME/hooks.json plus Libra-managed trust entries in $CODEX_HOME/config.toml (untrusted Codex hooks are skipped silently, so trust entries are part of the install), and opencode writes the Libra-managed plugin .opencode/plugin/libra-hooks.js (note: opencode --pure disables all external plugins, including capture). gemini was demoted out of the supported roster and is uninstall-only: libra agent remove gemini removes previously installed Libra-managed hooks (idempotent), captured sessions stay readable, and add/enable for it — or for any other non-roster agent — return an actionable unsupported error.

The OpenCode parser targets the audited 2.0.26 vocabulary. Its current lifecycle inputs are session.created, user session.inbox.delivered, session.execution.succeeded, session.execution.failed, session.execution.interrupted, session.deleted, and session.compaction.ended; tool.execute.after is a separate observation hook. Delivery requires role="user" and a prompt string, including an empty string. Only user, superseded, and inactivity interruptions end a turn; shutdown keeps restart continuity. An enqueue is merged by the plugin and is never a standalone lifecycle input.

Compatibility inputs are session.status(idle), message.updated, session.compacted, and server.instance.disposed with a tracked session ID. Busy/retry status frames are rejected. The parser requires a prompt string for legacy message.updated; an empty string and an omitted role are accepted, and a declared role must be user. Neither message nor user_prompt fills a missing prompt. The deprecated session.idle alias remains accepted, with a content-free legacy_event_alias tracing warning.

The managed plugin uses the OpenCode 2.0.26 setup(ctx) API, a location-scoped event subscription and an observe-only tool hook. Refresh it with libra agent enable --agent opencode: status reports old or edited templates, and duplicate managed copies, as not installed until refreshed. Node and Bun use the same node:child_process transport with the pinned absolute Libra filename and shell:false. Session/turn boundaries run synchronously; tool and compaction observations run asynchronously and never change host arguments or return context/control output.

User inbox enqueue/delivery pairs open one turn per classified inbox ID within the bounded live history window. Frames also carry a stable message_id for Libra’s existing bounded receipt ledger; this is not an unlimited historical replay guarantee. Legacy user messages pair with their first text part, or send an explicit empty string before the next unrelated event or cleanup. Assistant step events supply the model. Buffers are bounded: 64 tracked sessions process-wide, 128 pending prompts, 128 early text parts, 256 classified inbox/message IDs and 16 observation children; prompts are limited to 16,384 UTF-16 units without a split surrogate. Capacity or unavailable text produces a fixed, content-free diagnostic at most once per reason per plugin instance; truncated prompts carry prompt_truncated=true. ID history is a rolling 256-entry window and is pruned when a session ends; new prompts keep flowing after the window fills. When the session window fills, the least recently observed session is flushed with server.instance.disposed and the new session is admitted. A later observation can reopen an evicted session; this closes Libra tracking only and does not delete or stop the OpenCode session. Early text also uses a rolling window.

Cleanup and process exit flush all tracked sessions using one shared deadline per cleanup or process exit, respectively. Forwarding is best effort and fails open for the OpenCode host. Nonzero child exit or timeout emits only a fixed forward_failed / forward_timeout diagnostic, at most once per reason per plugin instance. Each child has the current export deadline plus 15 seconds (currently 45 seconds), with SIGKILL at timeout. Install checks both plugin layouts, rejects symlinks and special files before reading, preserves user-owned files and rolls back its canonical update if managed duplicate removal fails. These checks assume stable directory names; concurrent directory relocation and edits after the final ownership check remain deferred. opencode --pure / OPENCODE_PURE=1 disables all external plugins and hook capture. Local Node/Bun tests use synthetic events and a controlled exporter; real OpenCode content capture and native reasoning proof require their separate acceptance gates.

OpenCode 2.0.26 export metadata uses user.text, assistant model.id, tokens and successful file tools. The assistant token total is input + output + reasoning + cache.read + cache.write; cached tokens are cache.read + cache.write. Session-level totals are not added again. Classic info/parts exports remain compatible: assistant aggregate tokens take precedence over deduplicated step-finish details. User model fields are ignored. Modified files combine successful edit/write/patch paths and snapshot files, with validated patch move sources and destinations. Output paths are project relative with forward slashes; location-relative tool paths use the session's proven location, while snapshots already use the project root. Missing history, external or invalid paths, unsupported shapes, invalid token numbers and collection limits produce partial metadata with content-free warnings. Idle, model/agent switches, synthetic/system/skill messages, shell and compaction are recognized metadata and do not become human prompts. Internal ID, path and alias limits also mark partial; at the ID cap later messages are omitted. Extraction does not read files or archive reasoning content, and synthetic fixtures do not prove real macOS content capture or native reasoning origin.

OpenCode coverage normalization accepts native 2.0.26 messages and classic info/parts. Each human message starts its own stable logical turn. A valid native idle settles all pending human splits, including failed/interrupted outcomes; Complete describes captured representation, not task success. Semantic messages after the last valid idle stay Incomplete. Unknown/malformed content and reasoning stay Incomplete and are never repaired by idle. The nine classic metadata parts (step-finish, patch, step-start, snapshot, retry, compaction, agent, subtask, file) are recognized without semantic projection. Native user attachments, skill text, and tool file results are unsupported: they mark Incomplete without copying attachment bytes or URIs into records. Valid agent/skill metadata, model/usage and provider state do not change the digest. Native tools use name/id/state.input and text state.content; an error without content projects only its structured error.message, never response.body. No reasoning bytes or provider state establish trusted native origin here.

OpenCode user prompts pass through the same reminder filter for native/classic coverage and native/classic/flat-JSONL metadata extraction. Exact literal <system-reminder>...</system-reminder> blocks, including nested blocks, are removed wherever they occur; text outside them stays byte-for-byte unchanged. An injection-only message adds no user prompt or user semantic record. The filter also runs before skill matching; independent token metadata is retained. An unfinished block suppresses its tail and marks partial/Incomplete; a stray closing tag is retained and marks partial/Incomplete. Idle cannot clear these violations. Only user text is filtered, and other providers keep their existing semantics. This is a literal marker filter, not proof of producer origin.

OpenCode extraction is fail-visible for empty input, invalid JSON, and illegal export shapes: extraction.partial=true and fixed payload-free warnings are recorded. Valid independent flat model/token fields remain projected even when the role is missing or unsupported. A valid empty session export is distinct from missing transcript bytes (extraction.present=false). Noncritical extraction errors do not block an otherwise admissible checkpoint; malformed exports do not replace an existing checkpoint. Coverage, authorization, and native-origin gates remain required.

The default OpenCode export budget is 30 seconds; the export lease and plugin forwarding timeout derive a 15-second margin (45 seconds). Run libra agent add opencode again to refresh an older installed plugin. Schema v2 importable and export_bridge require a trusted exporter and a successful platform sandbox probe: Seatbelt on macOS, the existing trusted bwrap probe on Linux. Availability reports those prerequisites; it does not prove that OpenCode boots, that a session exports successfully, or that reasoning fields have a trusted native origin. Timeout still rejects partial output and preserves the existing metadata-only fallback; the 16 MiB export cap is unchanged.

On macOS, OpenCode export requires a Required Seatbelt server/export pair. Both processes deny process-fork, use private HOME/XDG roots and a pinned binary copy, and communicate only on one authenticated loopback port. Cancellation and the absolute deadline terminate their owned process groups; Libra never signals a group after reaping its leader. Trust, containment, readiness, output-cap or shutdown failure remains metadata-only, with no unsandboxed fallback. To enable the exporter, register the directory containing the verified opencode binary and then pin it: libra agent rpc trust --dir <path> followed by libra agent rpc trust opencode. Neither step opens the external-RPC surface (agent.external_agents.enabled stays untouched); the binary is resolved only from registered trusted directories, never $PATH. Linux content export additionally requires a trusted bwrap that passes Libra's descriptor-native --bind-fd safety probe. Unsupported or legacy bwrap is reported unavailable and fails closed to metadata-only capture; install or upgrade the system bwrap package rather than enabling an unsandboxed fallback.

For legacy OpenCode checkpoints, ordinary checkpoint export and the bound metadata reader remove metadata.anthropic.signature, metadata.openai.reasoningEncryptedContent and legacy encrypted_content from reasoning parts and reserved export/checkpoint metadata. Readable reasoning, other metadata and arbitrary tool input or user JSON remain intact. Provider classification uses the checkpoint's session catalog; a present metadata kind must agree. Missing session bindings, inconsistent metadata, duplicate keys, malformed or unsupported OpenCode JSON, and truncated nonempty exports are refused with a payload-free diagnostic. Empty transcripts remain valid. --raw --allow-raw keeps the original bounded bytes and the existing audit; checkpoint show remains a fixed safe summary. Stored objects are not rewritten, and future fixes must retain this ordinary-output protection. This legacy reader does not establish native provenance or implement the newer state.* reasoning artifact channel.

The macOS local content gate is local_capture_smoke_opencode_macos in agent_local_capture_smoke_test. It requires an explicit local-agent opt-in, pins OpenCode 2.0.26, selects volcengine-agent-plan/glm-5.3-flash, and checks the disposable project before requesting a single fixed file write. The gate checks captured prompt/model/tokens/files against the source projection, persisted file metadata, stopped sessions, doctor and uninstall retention. A failed or timed-out preflight is a failure, with no model request started. On the current acceptance machine, OpenCode's private server did not report readiness; real content acceptance is deferred as DEFER-OG-OPENCODE-BOOT-01. The harness and synthetic tests do not prove real content or native reasoning.

OpenCode exporter core limits

The trusted OpenCode export bridge runs a bounded subprocess.

On Unix, the exporter subprocess and its descendants run with both soft and hard RLIMIT_CORE set to zero. This suppresses core files for intentional limit enforcement and unexpected exporter crashes when the system core handler honors that limit; signal events may still appear in system logs. Libra retains the exporter exit status and bounded stderr diagnostics. This child-only setting leaves the parent Libra process and system configuration unchanged.

Subcommands

Subcommand Description
status Report captured external-agent session status
list List agents with their capability matrix (roster, hooks, install state)
import Discover and import historical external-agent transcripts after explicit consent
graph <session> Inspect the read-only capture graph; requires global --json or --machine (the interactive TUI was removed)
enable Enable one or more external agents and install hooks
add Alias of enable: add <name> ≡ enable --agent <name>
disable Disable one or more external agents and uninstall hooks
remove Alias of disable: remove <name> ≡ disable --agent <name>
session list List captured sessions
session show <id> Show a captured session
session stop <id> Mark a captured session as stopped
session resume <id> Mark a stopped captured session active again
session derive-tool-calls <id> Derive tool-call records from a captured session
checkpoint list List captured checkpoints
checkpoint show <id> Show checkpoint metadata
checkpoint rewind <id> Inspect or apply a working-tree rewind for one checkpoint
checkpoint export <id> Export a checkpoint's transcript. Redacted by default (no authorization); raw (un-redacted) export requires --allow-raw --raw and is recorded in the append-only agent_audit_log (LBR-AGENT-013 when refused without it)
skill search / skill list / skill registry Search captured skill events or inspect the curated skill registry
clean Clean up temporary checkpoints from stopped sessions (prune fails closed while a checkpoint write is in flight or the traces ref reaches uncataloged commits; also drops object_index rows made unreachable)
doctor Diagnose hook installation and capture state; detect (and with --repair fix) checkpoint-store inconsistencies. The additive, read-only legacy_code_residue JSON field (human: "Frozen Code residue") reports existing .libra/sessions/code/, .libra/code/, and libra/intent ref residue without deleting or rewriting it
workspace list / workspace show <id> Inspect the read-only workspace registry and lease state
push Push refs/libra/traces to a remote (--force-rewrite for the non-fast-forward push after a clean prune, using force-with-lease)
rpc list List discovered libra-agent-* binaries on PATH (with trusted/quarantined state); requires the external-agents opt-in
rpc trust <slug> Trust a discovered binary — records path + sha256 + device/inode/mtime provenance (refused when its directory is world-writable)
rpc untrust <slug> Revoke binary trust; OpenCode also revokes native issuers atomically (see native-origin rotation and revocation below). Always available, even while external agents are disabled
rpc invoke Invoke one JSON-RPC method on a trusted libra-agent-* binary
bridge --stdio Run the repository-scoped DeepSeek Harness bridge over JSON-RPC 2.0 NDJSON. It is separate from the removed libra code --control surface

Common Options

Flag Subcommand Description
--agent <name> enable, disable Select agent names; omit to target the supported roster (add/remove take the names positionally)
--schema-version <1|2> list Select the frozen legacy row or version 2 with method availability
--repo <path> graph Read capture metadata from another Libra repository
--limit <n> session list, checkpoint list Maximum rows per page (default 50, hard cap 500 — larger values clamp with a stderr note; 0 is treated as 1)
--cursor <cursor> session list, checkpoint list Opaque keyset cursor from the previous page's next_cursor; do not construct by hand
--extract-transcript <path> session show Copy the captured transcript path from session metadata to a local file
--all clean Clean all stopped-session checkpoints instead of only the most recent
--repair doctor Repair detected checkpoint-store inconsistencies (rebuild stale/missing catalog rows from refs/libra/traces, re-enqueue missing object_index rows); detection-only when omitted
--remote <name> push Select the remote used for pushing agent trace refs
--force-rewrite push Allow the non-fast-forward push that follows a local clean prune (the traces ref is Libra-managed and rewritten as a whole chain); uses force-with-lease against the last tip this repository pushed — never an unconditional force — so a remote rewritten elsewhere still fails closed
--dry-run checkpoint rewind Show the impact without modifying files; this is the default
--allow-raw / --raw checkpoint export Authorize + request a raw (un-redacted) export; without --allow-raw a --raw request is refused (LBR-AGENT-013) and audited
--justification <text> / -o <path> checkpoint export Audit justification and output file for a raw export
--gc / --retention-days <n> clean Retention GC: drop checkpoints from stopped sessions older than agent.retention.transcript_days (default 90; override with --retention-days); never touches agent_audit_log
--apply checkpoint rewind Restore the working tree for the selected checkpoint

Scoped checkpoint resume

review --checkpoint <id> and investigate start --checkpoint <id> materialize ordinary checkpoint files, read-only, under the run directory checkpoint-input/. investigate continue writes that saved spec again only when the repository catalog's ordinary leaves still have the same paths and blob ids. A path reasoning/encrypted/<64 hex> is not ordinary input: it is not written, and a saved spec that names it is refused before the previous input directory is cleared. A saved id that is not a blob, or whose bytes do not hash to that id, is refused before that directory is cleared. If the catalog cannot be opened, queried, or closed, a paused continue returns the existing store error and leaves that run's state and input bytes unchanged. A zero review setup budget ends as the existing infrastructure error and does not launch reviewers; cancel ends as cancelled. No new stable error code is added. Ordinary payload bytes stay capped at 64 MiB per file and 256 MiB total. Each catalog tree read is capped at 16 MiB.

JSON Output

Subcommands that support structured output use the global --json and --machine envelope. For example:

libra --json agent status
libra --json agent list
libra --json agent graph <session-id>
libra --json agent checkpoint list
libra --json agent rpc list

agent list --json carries a stable schema_version plus one row per known agent (slug, agent_kind, stability, supported, support_wave, registered, transcript_readable, hook_installable, installed, launchable_review, launchable_investigate, external_binary, config_paths, protected_dirs, capabilities). The row shape is a frozen contract for automation.

agent session list --json and agent checkpoint list --json return one page per call: data carries a schema_version, the rows under sessions / checkpoints (per-row shape unchanged), and next_cursor — an opaque token to pass back via --cursor, null once the listing is exhausted. Pages are ordered newest-first (started_at / created_at descending, with the row id as tiebreaker).

agent checkpoint show --json additionally reports a layout summary (e4-libra, legacy-v1 for pre-AG-20 checkpoints, or unknown when the checkpoint tree is not locally readable) with the manifest roles, the transcript parts in manifest order, a content_hash format check, and a transcript availability flag (present/missing/unknown) — derived without reading transcript blob bodies.

Examples

# Show captured-session counts and recent checkpoint summary
libra agent status

# Show the agent capability matrix (supported roster, hooks, install state)
libra agent list

# Inspect a captured session's read-only graph
libra --json agent graph <session-id>

# Enable Claude Code capture and install its hooks (alias of enable)
libra agent add claude-code

# Enable Claude Code capture and install its hooks
libra agent enable --agent claude

# Enable every supported agent at once
libra agent enable

# Disable Claude Code capture and uninstall its hooks (alias of disable)
libra agent remove claude-code

# Remove legacy gemini hooks (uninstall-only channel; idempotent)
libra agent remove gemini

# Disable Claude Code capture and uninstall its hooks
libra agent disable --agent claude

# List captured sessions
libra agent session list

# Show a session and copy its captured transcript
libra agent session show <session-id> --extract-transcript /tmp/session.jsonl

# Stop a captured session
libra agent session stop <session-id>

# Resume a stopped captured session
libra agent session resume <session-id>

# List captured checkpoints
libra agent checkpoint list

# Page through checkpoints (default 50 per page; JSON carries next_cursor)
libra agent checkpoint list --limit 100
libra agent checkpoint list --cursor <next_cursor>

# Show a single checkpoint by id
libra agent checkpoint show <id>

# Replay a checkpoint as a JSONL transcript
libra agent checkpoint rewind <id>

# Drop temporary checkpoints from the most recent stopped session
libra agent clean

# Drop temporary checkpoints from every stopped session
libra agent clean --all

# Diagnose hook installation and capture state
libra agent doctor

# Read the additive legacy_code_residue diagnostic without changing objects or refs
libra --json agent doctor

# Push refs/libra/traces to the default remote
libra agent push

# Push refs/libra/traces to a named remote
libra agent push --remote origin

# Re-push after `libra agent clean` rewrote the traces chain (force-with-lease)
libra agent push --force-rewrite

# Discover libra-agent-<name> RPC binaries on PATH
libra agent rpc list

# Invoke a single JSON-RPC method on a libra-agent-<slug> binary
libra agent rpc invoke <slug> <method> --params '<json>'

# Run the repository-scoped DeepSeek Harness bridge over stdio
libra agent bridge --stdio < bridge-initialize.ndjson

# Structured JSON envelope for agents
libra agent --json status

The same banner is rendered by libra agent --help so the doc and the CLI surface stay in sync (cross-cutting --help EXAMPLES rollout, see docs/development/commands/_general.md item B).

Notes

  • External libra-agent-* agents are disabled by default. Opt in with libra config set agent.external_agents.enabled true (repo-local); until then rpc list/rpc trust/rpc invoke refuse with LBR-AGENT-002 (rpc untrust stays available — revoking trust only tightens security). Discovered binaries stay quarantined until rpc trust <slug> records their provenance (trust is refused for a binary in a world-writable directory), every invoke revalidates it (drift revokes trust, LBR-AGENT-005), the child environment is cleared to an allowlist, and stderr is captured/capped/redacted — never inherited. Invoke timeouts, broken pipes and malformed frames map to LBR-AGENT-012; IO hard-cap violations map to LBR-AGENT-007.

  • The top-level agent hooks entry is hidden and intended for hook configs installed by libra agent enable; users normally do not call it directly.

  • checkpoint rewind --apply restores working-tree files only; the agent's own transcript file is not rewritten.

  • Hook and capture diagnostics are best-effort and are designed to report actionable installation state rather than silently ignoring missing providers.

Doctor checkpoint-store repair (--repair)

libra agent doctor scans the checkpoint store for three inconsistency classes (AG-20 repair matrix); without --repair it is strictly read-only and reports what --repair would do:

inconsistency_type Meaning --repair action
stale_catalog_row An agent_checkpoint row's traces_commit/tree_oid/metadata_blob_oid disagree with the checkpoint still reachable from refs/libra/traces Rebuild the row's OID columns from the ref (idempotent UPDATE)
missing_objects Checkpoint objects genuinely missing from the store (and the ref cannot rebuild them) — the check covers the full E4 tree: manifest.json, events/lifecycle.jsonl, transcript/<agent_kind>.jsonl incl. chunks, redaction_report.json, content_hash.txt, the intermediate trees, and every manifest-declared blob None — reported manual_required; doctor never takes destructive action (try libra fsck --heal or a cloud/backup restore)
missing_catalog_row A checkpoint reachable from refs/libra/traces has no catalog row (crash window B) Re-INSERT the row via the writer's probe-first idempotent path, reconstructed from the commit's metadata.json (v1 and v2 shapes)
missing_object_index Checkpoint objects missing from object_index (invisible to libra cloud sync) — covers the traces commit plus the full E4 object set Idempotent re-insert with the writer's row semantics (trees as tree, transcript blobs as agent_transcript, sidecars as blob)

Additional rules:

  • Legacy-v1 checkpoints (pre-AG-20 layout without manifest.json) are counted in legacy_v1_checkpoints, never enter the three classes, and are never rewritten by --repair.
  • Checkpoints named by a live traces in-flight marker are writers mid-flight, not inconsistencies, and are skipped.
  • A session without checkpoints is legal (an active session before its first stop) and is never flagged; only checkpoint-without-session counts as an orphan.
  • Captured gemini rows stay readable and are never flagged; leftover gemini hook configuration produces a hint pointing at the uninstall-only channel (libra agent remove gemini).
  • All repairs are idempotent — running doctor --repair twice performs no work the second time. With --repair, one agent.doctor.repair tracing span is emitted per repair attempt (inconsistency_type, repaired, manual_required); transcript content never reaches the log.

Private native-origin issuer registration (macOS)

Ordinary agent rpc trust opencode pins an exporter binary; it does not register an issuer or prove native reasoning. The native path requires the separately installed, reviewed 2.0.26-libra-native.1 macOS arm64 producer. Enable the OpenCode observer with the actual Libra executable, then prepare a fresh owned installation using that private bundle's initialization instructions. Global OpenCode remains independent.

libra agent rpc trust --dir /absolute/approved-private/bin
libra agent rpc trust opencode
libra agent enable --agent opencode
libra agent rpc trust opencode \
  --native-origin-manifest /absolute/private-installation/installation.json \
  --native-origin-expected-epoch 0

The two native options must be supplied together and only with the literal opencode slug. Initial registration expects epoch 0; later registration requires the current repository registry epoch. Inspect libra config --get agent.native_origin.opencode.v1 locally before an explicit retry. A conflict refuses without changing trust or registry. The registry includes local installation paths and should remain local.

The public descriptor must be a regular, non-symlink installation.json, owned by the current user with mode 0600 in a canonical owned 0700 directory, at most 64 KiB and in the private producer's closed minimal JSON format. Libra does not read the private key. Existing exporter trust, actual approved producer bytes, the current canonical Libra executable path and exact installed .opencode/plugin/libra-hooks.js bytes must match. A descriptor or export cannot authorize its own path, public key, endpoint or build.

The actual producer must successfully answer a new random, domain-separated Ed25519 challenge within 30 seconds, with at most 4096 stdout bytes and bounded captured stderr. Child output is withheld on failure. After the challenge, one write transaction rechecks current exporter trust and trusted directories, performs expected-epoch CAS, and writes one complete registry row. Duplicate rows, case aliases, encryption, malformed records and size overflow refuse without repair. Limits are 64 KiB and 128 issuer records; revoked history is never removed to make space.

A new independent installation key becomes active; earlier active keys become verify-only. Existing issuer keys cannot be reactivated by registration or ordinary binary re-trust. Historical records remain immutable. Successful registration proves installation key/binary association only. Receipt signature, current issuer permission, source coordinates and original native field checks remain required before any archive admission; unknown or unproven sessions stay partial. JSON registration output includes the transient current Libra executable SHA256 for local audit; this measurement is neither a signed policy field nor a pinned historical Libra version.

OpenCode native-origin rotation and revocation

To rotate an installation key, prepare a new independent private installation and explicitly register it with the current epoch. The earlier active issuer becomes verify-only. Verify-only and revoked issuers cannot authorize new native capture, import or pending replay. The current permission reader requires one transaction snapshot, fresh independently measured producer provenance, matching exporter trust and a trusted directory; it returns installation data, not a verified receipt or native artifact.

libra agent rpc untrust opencode now removes OpenCode exporter trust and revokes every remaining active or verify-only issuer in one bounded write transaction. It preserves all records and already stored artifacts. It remains available while external RPC is disabled. Malformed or duplicate registry rows, capacity/epoch failures and write faults refuse without a partial trust or registry update. Check and preserve the local registry before retrying; it is never automatically repaired. A repeated successful untrust is idempotent and does not advance an already revoked registry epoch.

JSON retains slug and removed (removed means an exporter trust row was removed). OpenCode additionally returns native_revoked (the number newly revoked) and native_epoch (the resulting epoch, or null when no registry exists). Other slugs retain their existing output and behavior.

If combined untrust refuses because the registry is damaged, suspend exporter access with libra config unset agent.trust.opencode. This removes binary trust only and preserves the registry, including all tombstones. A still-active issuer can regain permission after the same approved binary is explicitly retrusted. Repair the registry while preserving its records, then successfully run combined libra agent rpc untrust opencode for permanent native revocation.

Automatic provenance revalidation after an unreadable or changed binary or withdrawal of its trusted directory still removes only exporter trust. Native admission pauses while that trust is absent or mismatched; it leaves the issuer registry unchanged. Explicitly trusting the same reviewed build again can restore an active issuer that was never explicitly revoked. Ordinary binary trust never restores a verify-only or revoked issuer. Re-registering an old issuer is refused: rotation requires a fresh independent key. Registration/revocation do not rewrite historical receipts, private installation descriptors or immutable artifacts. Successful lifecycle operations do not prove native field origin; unproven content stays partial.

Current private OpenCode acceptance baseline (NPR-18)

The unique macOS arm64 acceptance build is 2.0.26-libra-native.1, build bac21cc4027896b8d1836133c347d20f, SHA-256 547c36eb928da163ef61d019bad669860d519f2da0b67737c9a3920e831c9196 and 187635570 bytes. It is based on official OpenCode 2.0.26 commit 9b4ec5714d481559990db0a816d5dec19541a814. That commit identifies the official base; the separate source, patch, lock and build-input digests identify the patched build. The public native-origin-build.json descriptor contains those reviewed identities and no installation policy, issuer or private key.

Set LIBRA_OPENCODE_NATIVE_BINARY to the absolute path of that independently installed private opencode before macOS local capture acceptance. The harness requires the reviewed version, build digest and size before login or any model request, bounds the version and login probes to 30 seconds, and puts the same private binary directory first in each capture/export child's PATH. Missing opt-in or drift fails the gate; it never falls back to global OpenCode. The official global installation and existing observer ABI remain separate.

NPR-12's SOURCE_READY_GO covers this reviewed darwin-arm64 build and the current OpenAI Responses transport mechanism. The existing official source record, event/export fixtures and synthetic receipt/signature vectors remain historical and unchanged. Source A is UNPROVEN: deterministic tests, a version string, registration and this descriptor do not prove real native provider fields or permit reasoning archival. Failed readiness or quota gates stay partial; the only authorized paid smoke model is volcengine-agent-plan/glm-5.3-flash, with no alternate-model fallback. This card makes no new model calls. Linux local functional acceptance remains with the operator.