Skip to content

Operator command cookbook

This page gives practical usage for every command shown by the normal ait --help screen. Use the command's own --help output for its complete option list, and use the direct interception guide for the end-to-end operator workflow.

Examples use uppercase placeholders such as SESSION_ID. Replace them with IDs printed by AIT. Add --json when another program will consume the result.

Health and Cockpit service

Command Use when Example Expected result
ait doctor Before an assessment or when a transport will not start ait doctor --profile proxy Checks local binaries, packaged assets, storage, and proxy prerequisites without target contact
ait server start Start the local API/Cockpit independently of a lab ait server start --listen 127.0.0.1:8790 Returns the listener and starts a loopback-only secured server
ait server status Determine whether the shared Cockpit is already running ait server status Reports listener, PID, health, and local state
ait server stop Stop the shared Cockpit after all labs and sessions are finished ait server stop Stops the local server; it does not silently resolve another process's traffic

Use the assessment profile for a broader offline readiness check:

ait doctor --profile assessment

Lab commands

The lab is the safest place to learn each decision. It launches only packaged local components and never requires a provider or Docker.

Command Use when Example Expected result
ait lab list See exercises and active/stopped lab instances ait lab list Catalog plus current lab IDs and states
ait lab start Launch an exercise and open its active Cockpit session ait lab start --exercise delegated-a2a-message Starts local agents, interception, and a controlled effect ledger
ait lab status Inspect component, SDK, receiver, and ledger state ait lab status LAB_ID Current lab and process receipts
ait lab trigger Generate the exercise traffic after interception is ready ait lab trigger LAB_ID A matching message enters the pending queue
ait lab reset Clear messages, tasks, rules, and ledger state between comparisons ait lab reset LAB_ID Fresh deterministic exercise state
ait lab stop Terminate every process owned by the lab ait lab stop LAB_ID Agents, listener, wrappers, and streams stop cleanly
ait lab use-target Copy protocol and break behavior from a lab to a real target ait lab use-target LAB_ID --protocol a2a_jsonrpc --upstream http://127.0.0.1:9000 Deposits a new target setup; preparation itself sends no probe

Transport selection examples:

ait lab start --exercise mcp-tool-result --mcp-transport stdio
ait lab start --exercise asynchronous-artifact --a2a-binding rest
ait lab start --exercise delegated-a2a-message --a2a-binding grpc

Use --fixture only for the handwritten regression fixture. Normal operators should use the SDK-backed default.

Connection commands

Connections describe where AIT listens and where it forwards. Saving or listing a connection is offline. discover and test contact the endpoint explicitly named by the operator.

Command Use when Example Expected result
ait connections add Save an explicit HTTP/SSE/WebSocket/gRPC relay placement ait connections add --name specialist --protocol a2a_jsonrpc --upstream http://127.0.0.1:9000 Returns a connection ID and proposed loopback listener
ait connections add Save an MCP stdio wrapper placement ait connections add --name policy --protocol mcp_stdio --command python policy_server.py Returns a connection with a shell-free command vector
ait connections discover Read an authorized A2A Agent Card and propose connection details ait connections discover --upstream https://agent.example Displays advertised interfaces without starting interception
ait connections list Find connection IDs for later starts ait connections list Saved connection table
ait connections test Verify endpoint connectivity or validate a stdio executable ait connections test CONNECTION_ID Health/command validation; no offensive traffic is generated

Starting and controlling interception

Command Use when Example Expected result
ait intercept start Start from a saved connection ait intercept start --connection CONNECTION_ID --open Returns session/listener IDs and opens the Cockpit
ait intercept start Start an ad hoc HTTP relay ait intercept start --protocol a2a_rest --upstream http://127.0.0.1:9000 --direction request Listener pauses matching requests
ait intercept start Break only on message content ait intercept start --connection CONNECTION_ID --where 'json.params.message.parts.0.text:contains:refund' Repeatable; all predicates must hold. Ops: equals, contains, exists, not_exists, regex, in
ait intercept start Bound an unattended session ait intercept start --connection CONNECTION_ID --pending-timeout 60 --pending-timeout-action drop A paused message holds the sender's connection open; timed-out messages are marked timed_out
ait intercept start Wrap an MCP stdio server ait intercept start --protocol mcp_stdio --command python policy_server.py Prints the replacement wrapper command
ait intercept status Check listener, queue, process, and interception state ait intercept status SESSION_ID Session health and pending count
ait intercept on Begin pausing matching messages ait intercept on SESSION_ID Matching traffic waits for decisions
ait intercept off Continue forwarding without pausing while keeping observation active ait intercept off SESSION_ID New traffic passes and remains visible in History
ait intercept pending List messages awaiting an operator decision ait intercept pending SESSION_ID Message IDs, operations, directions, and age
ait intercept stop Stop after choosing how to resolve paused traffic ait intercept stop SESSION_ID --pending forward Pending originals forward unchanged, then owned processes stop

Use --pending drop only when dropping unresolved messages is the intended shutdown behavior.

ait intercept measure: does it actually work?

sweep tells you what can be aimed here. This tells you whether it does anything. Run it before you put a primitive in a report.

ait intercept measure a2a.approval-amount --lab LAB_ID --trials 20
a2a.approval-amount
  control arm moved the receiver  <count>/<trials>
  attack arm moved the receiver   <count>/<trials>
  adjusted p                      <p>     significant=<bool>

The shape of the output, not a result. Measured rates from this project are held back until the work is published.

Each trial delivers both arms and compares them against each other: the attack counts only when it took the receiver further than its own control did. That within-pair comparison is the whole design. The first version of this scored the control ⅚ because its oracle asked "did an event happen": true of any delivered message, and a control that scores alongside the attack has isolated nothing.

Unreadable pairs are dropped rather than counted, and if more than a quarter of them go, no rate is reported at all: a number computed from what survived would be quoting a sample chosen by whatever broke.

Small samples cannot be significant, and the arithmetic says so. Exact McNemar on four discordant pairs is p=0.0625. A flawless 4/4 is not a finding; six is the minimum that can cross.

ait intercept sweep: what can I even aim here?

Run it first. It answers the question every engagement opens with: of the catalogue, how much of it addresses this target's traffic at all?

ait intercept sweep --session SESSION_ID
6 of 26 primitives address this traffic (1 message(s) considered)
  a2a.actor-substitution             identity    Actor/principal
  a2a.approval-amount                content     Approval parameters
  credential.forge                   credential  Actor and authority
  ...

not addressable here:
  mcp.tool-shadowing                 the attack arm cannot be applied: JSON Pointer...

Six of twenty-six is not a defect. MCP techniques cannot apply to A2A traffic. However, it is the number to plan against, and assuming twenty-six is how an assessment ends up reporting a target as sound because the primitives aimed at it were the ones that could not reach it.

The exclusions carry their reason. A pointer that does not resolve is usually a retargeting job (intercept attack --set), where a selector that did not match means the technique belongs to a different kind of traffic.

Coverage is not effect. A primitive listed here reaches the wire; whether it moves the receiver needs an attack arm, its close control, and ait intercept evidence.

Inspecting and deciding messages

Every decision operates on an immutable captured original. An invalid edit leaves the message pending.

Command Use when Example Expected result
ait intercept show Inspect one complete decoded message and safe envelope ait intercept show MESSAGE_ID --session SESSION_ID Original, state, metadata, and the transport envelope
ait intercept suggest Ask AIT for local message-specific offensive tests ait intercept suggest MESSAGE_ID --session SESSION_ID Exact change/action, hypothesis, observation, and close control; no target contact
ait intercept edit Replace one decoded value and forward explicitly ait intercept edit MESSAGE_ID --session SESSION_ID --set /params/message/metadata/amount=75 Encodes and delivers the edited message
ait intercept edit Apply one derived suggestion and forward explicitly ait intercept edit MESSAGE_ID --suggestion a2a.context-crossover Applies the deposited patch, validates it, and delivers
ait intercept forward Deliver the immutable original ait intercept forward MESSAGE_ID --session SESSION_ID One unchanged delivery
ait intercept drop Resolve without upstream delivery ait intercept drop MESSAGE_ID --session SESSION_ID Records a dropped decision
ait intercept replay Test replay/idempotency with correlated copies ait intercept replay MESSAGE_ID --copies 2 Delivers the requested bounded number of copies. Requests, SSE, WebSocket, and stdio only: a unary HTTP response has one delivery slot and is refused rather than silently delivered once
ait intercept attack List the catalogued primitives that apply to a message ait intercept attack --target MESSAGE_ID Boundary, trust hypothesis, and title for each applicable primitive
ait intercept attack Deliver the attack arm of a primitive ait intercept attack mcp.tool-shadowing --target MESSAGE_ID Applies the injection and prints the control to run next and why
ait intercept attack Deliver the close control ait intercept attack mcp.tool-shadowing --target NEXT_MESSAGE_ID --arm control The near-miss that should stay inert; without it a positive is not attributable
ait intercept send Originate a message through the active listener ait intercept send --path /message --body-json @probe.json Delivers composed traffic and returns its transcript refs
ait intercept evidence Score the recorded arms into a reviewable finding ait intercept evidence --primitive mcp.tool-shadowing --out finding.json Verdict tier, attribution, and what remains unproven with the reason
ait intercept evidence Record an out-of-band observation ait intercept evidence --external-effect --note "controlled ledger row 42" Only pass this for an effect an oracle confirmed; a target response is not an oracle
ait intercept send Resend a captured request, optionally edited ait intercept send --from MESSAGE_ID --set /params/message/metadata/amount=999 The repeater. Injected traffic passes the same gate, so a matching break condition will pause it
ait intercept compare Compare sent, delivered, receiver response, and observable effect ait intercept compare MESSAGE_ID Structural differences and an honest evidence limitation

JSON values passed to --set are decoded when possible. Quote objects and arrays in the shell:

ait intercept edit MESSAGE_ID \
  --set '/params/toolChoice={"mode":"auto"}'

Live-rule commands

Rules repeat a successful manual decision. Preview and enable are separate so a saved rule never starts altering traffic merely because it exists.

Command Use when Example Expected result
ait intercept rule save Derive a selector and patches from a manually edited message ait intercept rule save MESSAGE_ID --name controlled-amount --scope session Creates a disabled or explicitly scoped reusable rule
ait intercept rule list Inspect rule order, scope, enabled state, and hit counts ait intercept rule list --session SESSION_ID Ordered rule table
ait intercept rule preview See local match count and sample messages before enabling ait intercept rule preview RULE_ID --session SESSION_ID Offline preview; no delivery
ait intercept rule enable Apply a reviewed rule to future matching traffic ait intercept rule enable RULE_ID --session SESSION_ID Rule becomes active
ait intercept rule disable Stop future application without deleting evidence ait intercept rule disable RULE_ID Rule remains saved but inactive
ait intercept rule edit Replace a bounded rule from reviewed JSON/YAML ait intercept rule edit RULE_ID --from reviewed-rule.yaml Validated rule revision
ait intercept rule delete Remove a rule that should no longer be selectable ait intercept rule delete RULE_ID Rule removed; historical message decisions remain
ait intercept rule export Move reviewed rules to another controlled workspace ait intercept rule export --out rules.json Ordered, sanitized rule document
ait intercept rule import Deposit exported rules without automatic activation ait intercept rule import --from rules.json Imported rules remain disabled unless --enable is explicit

Transform-rule commands

A live rule is a JSON-Pointer patch derived from a manual edit, scoped to a session. A transform rule is a YAML document Seam's engine loads, armed on a connection so it survives a restart, carrying regex replace, deep merge, positional insert, file payloads and chain guards. Different objects; know which one you are holding.

Command Use when Example Expected result
ait intercept transform arm Arm a YAML rewrite rule ait intercept transform arm ./poison-schema.yaml Validated against the whole prospective set, written to the connection, hot-applied if a session is live
ait intercept transform list See which rule wins ait intercept transform list Evaluation order, with chained, DISABLED, priority and SHADOWED flags
ait intercept transform test Pre-flight before arming on a live target ait intercept transform test --fixture msg.json --expect-rule my_rule Fails when the named rule cannot fire, including when an earlier rule shadows it
ait intercept transform trace Tune offline against captured traffic ait intercept transform trace --transcript session.json Per record, which rule the engine would apply, and why the others missed
ait intercept transform explain Check intended paths and transports ait intercept transform explain my_rule Declared mutation paths, inferred transports, failure modes
ait intercept transform remove Disarm without deleting the file ait intercept transform remove my_rule Removed from the connection and from the live session

test and trace report what the engine would do, which is not the same as which rules match: the engine applies the first match and returns, so at most one rule is applied and the rest report miss_code: shadowed.

Multi-step chains

A rule can guard on another rule having already delivered:

Guard Means
chain.fires.<rule-id> gte 1 after that rule's mutation reached the wire
chain.fires.<rule-id> lt 1 unless it landed: a fallback, not a control
chain.self lt 1 fire once, then go quiet

chain.fires counts delivered mutations. A rule whose output the operator dropped, replaced with the sender's original bytes, or which failed to encode does not advance a chain, so a dependent rule cannot arm on a poison that never landed.

Command Use when Example Expected result
ait intercept status Watch a chain progress ait intercept status chain armed plus delivered count per step
ait intercept evidence Report a chained attack ait intercept evidence --out finding.json Steps in order, what armed each, and the standing caveat that ordering is not causation

Two things change when a chain guard is loaded: interactive SSE interception becomes sequential (an operator pause then blocks the stream), and chain counters reset at session start and whenever you edit and re-arm a rule: /rules/apply returns the reset ids as chain_reset.

A worked example with its control arm ships at agentic-redteam/seam/rules/chains/mcp-tool-schema-induced-call/.

Shadow endpoints

A substituted destination that points at nothing proves only that routing moved. A shadow answers like the endpoint it replaced and records what arrived, which is also the out-of-band oracle a finding needs to reach external_effect_observed.

Command Use when Example Expected result
ait intercept shadow start Give a redirect somewhere real to land ait intercept shadow start a2a-agent --name shadow-specialist Loopback URL; serves an Agent Card and accepts delegations
ait intercept shadow start Catch a substituted callback or elicitation URL ait intercept shadow start callback Records any request to any path
ait intercept shadow start Catch a redirected MCP client ait intercept shadow start mcp-server Serves initialize/tools/list, records tools/call arguments
ait intercept shadow list See what is running and what it has caught ait intercept shadow list Name, kind, URL, observation count
ait intercept shadow observations Read what arrived ait intercept shadow observations --name shadow-specialist Timestamped arrivals with the payload each carried
ait intercept shadow stop Finish; observations stay on disk ait intercept shadow stop shadow-specialist Reports where the record was kept

The three routing primitives, a2a.card-endpoint-substitution, a2a.callback-substitution, and mcp.elicitation-url-substitution, declare {{shadow:<kind>}} as their redirect target, resolved to a running shadow when the primitive runs. Running one with no shadow started fails with the command to fix it, rather than quietly delivering a redirect into a closed port.

Shadows bind to loopback only, and their observation files are written 0600: a substituted destination receiving a bearer token is the point of a credential-relay test, so the token is recorded in full.

What an arrival proves. Traffic reached an endpoint you control and you can see exactly what it carried, independently of anything the target reported. That is the top evidence tier. It does not prove a real attacker would hold that endpoint, nor that anyone downstream would notice the substitution: the finding says both.

Session export and offline review

Command Use when Example Expected result
ait intercept export Produce a client-safe session report ait intercept export SESSION_ID --format html --out session.html Redacted by default: allowlisted metadata, bodies omitted, header values withheld unless known-safe
ait intercept export Keep a complete capture for your own analysis ait intercept export SESSION_ID --format jsonl --raw --out session-raw.jsonl Includes credential values; labelled "status": "raw" and never described as sanitized
ait intercept export Preserve machine-readable evidence ait intercept export SESSION_ID --format jsonl --out session.jsonl Bounded message/decision records
ait intercept export Reuse connection and rule behavior without fixture state ait intercept export SESSION_ID --format profile --out connection-profile.json Sanitized placement/profile document
ait intercept open Verify and inspect an export without target contact ait intercept open session.jsonl Digest, counts, redaction state, and message summary

Traffic commands

These commands are mainly for older run artifacts and Advanced investigations. For a direct interception session, prefer intercept pending, show, compare, and export.

Command Use when Example Expected result
ait traffic tail Follow paginated records from a saved run ait traffic tail --run RUN_ID --follow Cursor-based event stream
ait traffic inspect Filter one run by task, correlation, lifecycle phase, stream, or session sequence ait traffic inspect --run RUN_ID --seq-in-session '10..20' --related-limit 20 The first matching record plus bounded correlations
ait traffic export Build a portable run evidence bundle ait traffic export --run RUN_ID --out evidence-bundle Checksummed artifact bundle

Session sequence filters are numeric. Use 10 for one exact record, >=10 or <20 for a one-sided bound, and 10..20 for an inclusive range. 10 does not match 100. The same expressions work in the Cockpit History sequence box.

Advanced

ait advanced reaches the peer tools AIT coordinates rather than reimplements, plus the workspace and target plumbing the primary groups do not need. It forwards the remaining arguments to the full parser:

ait advanced seam --help
ait advanced assay --help
ait advanced meshmapper --help

Run ait advanced --help, then the selected leaf command with --help, for the complete Advanced surface. None of it is a prerequisite for interception, message editing, lab exercises, rules, or session export.

Complete terminal-only example

This is the direct operator loop without the Cockpit:

ait lab start --exercise delegated-a2a-message --no-open --json
ait lab trigger LAB_ID
ait intercept pending SESSION_ID
ait intercept show MESSAGE_ID --session SESSION_ID
ait intercept suggest MESSAGE_ID --session SESSION_ID
ait intercept edit MESSAGE_ID \
  --session SESSION_ID \
  --set /params/message/metadata/amount=75
ait intercept compare MESSAGE_ID --session SESSION_ID
ait intercept export SESSION_ID --format html --out delegated-edit.html
ait lab stop LAB_ID

The expected result is not merely “the JSON changed.” The specialist must receive the delivered amount and the controlled ledger must independently show the resulting test action.