Operate

AgentRun CLI

anvil-agentctl is the standalone command-line client for Anvil Agents. Its name is intentionally distinct from Anvil Primaris anvilctl: it imports only the APIs in this repository and does not require Anvil Primaris or Anvil Hub. Built-in runner images ship the same binary for in-pod status reporting; binary presence grants no Kubernetes authority.

The first client transport talks directly to Kubernetes using the caller's normal kubeconfig loading rules and RBAC. Build it from a checkout with:

go build -o ./anvil-agentctl ./cmd/anvil-agentctl

Set KUBECONFIG normally, or put global overrides before the command:

anvil-agentctl --kubeconfig ./cluster.yaml --context agents-prod run list -A

The optional OIDC AgentRun API remains read-only. A future client transport can use it for list, get, and logs, but it must not turn that read-only process into a run-creation or policy-broker service.

The caller needs only the Kubernetes verbs used by each command:

CommandKubernetes access
run createcreate on agentruns
run listlist on agentruns in the selected scope
run getget on the selected agentrun
run logsget on the run, referenced Job and Pod, plus get on pods/log
run debugget on the run, referenced Job and Pod, plus list on Events
auth codex|openai diagnoseget on secrets and agentdatavolumes; list on agentauthsessions
auth codex|openai reauthcreate staging secrets and agentauthsessions; get on volumes/secrets/sessions
auth codex|openai logoutcreate/get agentauthsessions; get agentdatavolumes
auth grok|xai diagnosesame as codex diagnose for xAI/Grok durable homes
auth grok|xai reauthsame as codex reauth; staging key is GROK_AUTH_JSON
auth grok|xai logoutsame as codex logout for Grok durable homes
volume copycreate an append-only AgentDataVolumeCopy (stream source claim to a new volume on a target node)
self reportnone (writes local status JSONL / pod log only)

Create An Append-Only Run

Create a run from a same-namespace profile and one-off prompt:

anvil-agentctl run create \
  --namespace agents \
  --generate-name repository-review- \
  --profile repository-review \
  --source-kind ManualRequest \
  --source-name issue-42 \
  --intent observe \
  --prompt "Review issue 42 against the current repository and report evidence."

Without --dry-run, the command submits exactly one Kubernetes create request. It never applies, patches, or replaces an existing AgentRun. A name collision fails and must be resolved by choosing a new name, preserving the append-only execution record.

Use --name for an exact identity or --generate-name for a unique server-generated suffix. Exactly one is required. --namespace, --profile, --source-name, and either --prompt or --prompt-file are also required. --source-kind defaults to ManualRequest; source API version, namespace, UID, and generation flags remain opaque audit metadata.

Client dry-run needs no cluster connection and can render either YAML or JSON:

anvil-agentctl run create \
  -n agents \
  --name repository-review-001 \
  --profile repository-review \
  --source-name issue-42 \
  --prompt-file ./prompt.md \
  --dry-run=client \
  -o yaml

printf '%s\n' "Inspect the failing release gate." | \
  anvil-agentctl run create \
    -n agents \
    --generate-name release-gate- \
    --profile release-review \
    --source-name manual-release-check \
    --prompt-file - \
    --dry-run=client \
    -o json

--purpose accepts manual, adverseSituation, or scheduledHealthCheck. --intent is an optional explicit override accepting observe, fixTransient, proposeChange, or cleanup; omit it to retain the profile's declared intent.

List And Inspect Runs

anvil-agentctl run list -n agents
anvil-agentctl run list -A
anvil-agentctl run list -n agents -o json

anvil-agentctl run get -n agents repository-review-abc12
anvil-agentctl run get -n agents repository-review-abc12 -o yaml

When --namespace is omitted, these commands use the namespace selected by the current kubeconfig context. run get summarizes phase, backend, intent, source, child references recorded in status, conditions, structured reports, composition digests, and terminal errors. Use YAML or JSON output when the bounded .status.output field is needed without the fuller debug view.

Read Verified Logs

anvil-agentctl run logs -n agents repository-review-abc12
anvil-agentctl run logs -n agents repository-review-abc12 --tail 1000 --follow
anvil-agentctl run logs -n agents repository-review-abc12 --follow --pod-timeout 3m

The command does not select an arbitrary Pod or container. It resolves the Pod recorded in AgentRun status, verifies the same-namespace AgentRun-to-Job-to-Pod controller-owner chain and labels, then reads the fixed agent container. The default tail is 200 lines. Use --tail=-1 for all logs still retained by Kubernetes. --follow waits up to two minutes by default for the controller to publish a runner Pod and for that Pod's logs to become available; --pod-timeout changes that bound. Only pending or not-yet-created Pod/log states are retried. Ownership failures return immediately.

Kubernetes opens the Pod log subresource by name and offers no UID precondition. The CLI verifies the Pod UID and owner chain immediately before opening logs, but a subject able to replace controller-owned Pods can race that check and inject log content. Treat Pod replacement authority as log-injection authority.

Aggregate Debug Evidence

anvil-agentctl run debug -n agents repository-review-abc12

run debug combines:

  • AgentRun phase, status conditions, structured reports, composition digests, terminal error, and bounded status output;
  • the referenced Job only after its namespace, label, controller kind, name, and UID match the AgentRun;
  • the referenced Pod only after it belongs to that verified Job and contains the fixed agent container;
  • Kubernetes Events for the AgentRun and only those child UIDs that passed verification; and
  • one likely-cause summary selected from explicit run errors, false conditions, container state, Job failure, and warning Events.

This command makes current Kubernetes evidence easier to inspect but is not a durable transcript. AgentRun status retains bounded output and reports, and Kubernetes can expire Pod logs and Events. Send logs to a durable external store when exact historical model and tool-call investigation is required. Table, summary, and debug views escape terminal control characters from untrusted status and Event text. run logs deliberately preserves the raw log stream; redirect or inspect it in a non-terminal parser when the runner is not trusted.

Provider Auth Maintenance (OpenAI Codex and xAI Grok)

Durable provider homes keep OAuth auth.json on an AgentDataVolume PVC. The runner only seeds from the bootstrap Secret when the file is missing or the operator deliberately changes the opaque seed id. Refreshing a Secret alone does not overwrite a stale durable login, which is why manager Jobs can keep dying with 401 after an ExternalSecret or vault update.

Provider CLIAliasesStaging / seed keyDurable path on the volume
auth codexopenaiCODEX_AUTH_JSON + CODEX_AUTH_SEED_ID$CODEX_HOME/auth.json (usually /codex-home/auth.json)
auth grokxai, grokBuildGROK_AUTH_JSON + GROK_AUTH_SEED_ID$GROK_HOME/auth.json under the Grok home (usually /opt/anvil/grok-build/.grok/auth.json)

OpenAI Codex

codex login --device-auth

anvil-agentctl auth codex diagnose \
  -n agents \
  --data-volume hazy-trade-codex-home \
  --bootstrap-secret codex-credentials-seed \
  --auth-file ~/.codex/auth.json

anvil-agentctl auth codex reauth \
  -n agents \
  --data-volume hazy-trade-codex-home \
  --bootstrap-secret codex-credentials-seed \
  --auth-file ~/.codex/auth.json

xAI Grok Build

# Complete local Grok / xAI OAuth so ~/.grok/auth.json is fresh.
anvil-agentctl auth grok diagnose \
  -n agents \
  --data-volume hazy-trade-grok-home \
  --bootstrap-secret grok-credentials-seed \
  --auth-file ~/.grok/auth.json

anvil-agentctl auth xai reauth \
  -n agents \
  --data-volume hazy-trade-grok-home \
  --bootstrap-secret grok-credentials-seed \
  --auth-file ~/.grok/auth.json

For pure apiKey mode, mount XAI_API_KEY (Grok/Pi/OpenClaw) or provider keys through envSecretRefs. Durable OAuth reauth is only required when the harness uses the durable auth.json home. Diagnose still reports whether an API key key is present on the bootstrap Secret.

reauth refuses bootstrap Secrets that look ExternalSecret-managed so the CLI does not race ESO. Point --bootstrap-secret at a CLI-owned seed Secret, or omit it and only rewrite the durable home. Credential bytes never appear in the AgentAuthSession spec, status, or CLI args.

Logout clears durable auth.json, writes a logout tombstone that blocks secret reseeding, and does not claim remote session revocation:

anvil-agentctl auth codex logout \
  -n agents \
  --data-volume hazy-trade-codex-home \
  --confirm-volume hazy-trade-codex-home

anvil-agentctl auth grok logout \
  -n agents \
  --data-volume hazy-trade-grok-home \
  --confirm-volume hazy-trade-grok-home

While a non-terminal AgentAuthSession targets a volume, new AgentRuns that mount it stay Pending with reason AuthSessionActive. Sessions never kill active Jobs.

Exit code 3 means diagnose finished and found an unhealthy state. Exit code 2 is usage; 1 is operational failure.

In-Pod Status Reporting

Inside a runner Job the same binary can record structured status without any kubeconfig:

anvil-agentctl self report progress --stage tool-setup --summary "Tools ready."
anvil-agentctl self report needsHuman --stage harness-auth --summary "Re-auth required."

This writes the existing JSONL status file and ANVIL_AGENT_RUN_STATUS_JSON= log lines. It never patches AgentRun/status. The historical anvil-agent-status shell wrapper remains for compatibility.