Metadata-Version: 2.4
Name: abom-cli
Version: 0.1.20
Summary: ABOM — the Agent Bill of Materials. Scan, sign, and verify what your AI agents are made of.
Project-URL: Homepage, https://abom.ai
Project-URL: Repository, https://github.com/josephassiga/abom-dev
Project-URL: Specification, https://github.com/josephassiga/abom-dev/tree/main/spec
Author: ABOM Contributors
License-Expression: Apache-2.0
Keywords: agent,ai,ai-security,cyclonedx,llm,ml-bom,provenance,sbom,supply-chain
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Requires-Dist: cryptography>=42.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: click<8.2; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: jsonschema>=4.21; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.7; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: httpx>=0.27; extra == 'mcp'
Description-Content-Type: text/markdown

# abom-cli

The reference implementation of [ABOM](../spec/) — the Agent Bill of Materials.
Every agent action becomes a **signed, hash-chained record a third party can
verify without trusting you**: scan a repo into a signed Composition Manifest,
run MCP traffic through the gateway for per-call attestation, settle disputes
from commitments — and opt into inline blocking (`--enforce`) when evidence
alone isn't enough.

```bash
pip install abom-cli        # (until published: pip install -e .)
abom scan .                 # → abom.json (signed with ed25519)
abom verify abom.json       # check signature
abom verify abom.json --policy policy.json   # + enforce a policy (exit 1 on violations)
abom gate abom.json --tool wire_transfer     # ALLOW/DENY one action (deny-by-default)
abom broker abom.json --upstream https://mcp.internal/rpc  # mediate an agent's tool calls
abom gateway run policy.json --upstream https://mcp.internal/rpc  # policy + signed provenance per call
```

## Commands

| Command | What it does |
|---|---|
| `abom scan [PATH]` | Detect agent components (models, prompts, tools, MCP servers, frameworks, vector stores, guardrails) and emit a signed Composition Manifest. `-o -` writes to stdout. |
| `abom verify [FILE]` | Verify the ed25519 signature; with `--policy`, enforce model allowlist / residency / egress / approval rules. Non-zero exit on findings (CI-friendly). `--trusted-key` pins the authorized signer. |
| `abom gate FILE --tool T` | Decide ALLOW/DENY for one action against the signed manifest, deny-by-default, notarized. `--trusted-key` refuses a manifest it can't authenticate. Exit 1 = DENY. |
| `abom broker FILE --upstream URL` | Run the MCP broker: mediate every `tools/call` against the signed manifest before forwarding to an upstream MCP server. Denied calls return a notarized JSON-RPC error and never reach upstream. `--log` for a durable decision log. |
| `abom gateway run POLICY --upstream URL` | Run the MCP gateway (ABOM v0.2): one **signed, hash-chained Action Provenance Record per `tools/call`** — written and fsync'd before forwarding, fail-closed. Default is **inspect-only** (violations recorded as `would_deny`, forwarded); `--enforce` blocks. `--floor` layers an org minimum no template can weaken; `--manifest` binds records to a composition. |
| `abom gateway verify LOG...` | The auditor's check: recompute the hash chain, every ed25519 signature, and — across rotated files — the epoch continuity links. `--trusted-key` pins the signer. Exit 1 at the exact file and seq. |
| `abom witness LOG.mlog` | Cosign a durable transparency log with an independent witness key; refuses a rewrite across runs. Run on a second party's machine for genuine non-equivocation. |
| `abom anchor LOG.mlog` | RFC 3161-timestamp the log root via a TSA (`--tsa`, default freetsa.org) — a third-party existence proof (eIDAS Art. 41 with a qualified TSA). |
| `abom witness-verify COSIG.json` | The auditor's check: verify a witness cosignature with no trust in the operator. |
| `abom keygen` | Show (or create) the local ed25519 signing key (`~/.abom/signing_key.pem`, override with `ABOM_KEY`). |
| `abom version` | Print the tool and spec versions. |

All commands accept **`-v`** (info) / **`-vv`** (debug) / **`-q`** (errors only) / **`--json-logs`** (NDJSON for CI) — logs go to stderr, so the ABOM on stdout stays clean.

### Example

```
$ abom scan .
  ABOM · my-agent @ 1.2.0
  models                  3  gpt-4o-mini, claude-3-5-sonnet, OpenAI (SDK)
  frameworks              2  LangChain, LangGraph
  MCP servers             2  filesystem, github
  tools                   1  lookup_customer
  prompts                 1  prompts/system.txt
  signed: ed25519 · key 5846eabc738b3542
  → wrote abom.json
```

## How detection works

`abom scan` is a static scanner (pure stdlib + `cryptography`):
- **Dependencies** (`requirements*.txt`, `pyproject.toml`, `package.json`) → frameworks, model SDKs, vector stores, guardrails.
- **Source** → concrete model names (`gpt-4o`, `claude-*`, …) and `@tool`-decorated functions.
- **Prompt files** (`*.prompt`, `prompts/*.txt|md`) → hashed.
- **MCP configs** (`mcp.json`, `claude_desktop_config.json`, …) → MCP servers.

Each component records `detected_from` so the manifest is auditable. The output
validates against [`spec/abom-0.1.schema.json`](../spec/abom-0.1.schema.json).

## Signing

`abom scan` signs with **ed25519** (`cryptography`). The key lives at
`~/.abom/signing_key.pem` (override with `ABOM_KEY`); the public key + a short
`key_id` are embedded so `abom verify` is self-contained. Pin the trusted signer
with `--trusted-key <key_id>` on `verify` / `gate` / `broker`: a manifest
re-signed by any other key is then refused, so "signed" authenticates *who* — not
just integrity. `LocalSigner` is dev/CI; a `KMSSigner` seam keeps the private key
in a KMS/HSM for production.

## Dev

```bash
make install          # pip install -e ".[dev]"
make test             # pytest (audit chain, scanner, signing)
make scan && make verify
make build            # wheel + sdist + twine check
python demo/demo.py   # generate → verify → tamper-evidence walkthrough
```

## The MCP broker

`abom broker` moves enforcement **out of the agent's process**. The agent points
its MCP client at the broker instead of the MCP server; the broker checks every
`tools/call` against the signed manifest and only forwards allowed ones. A denied
call is answered with a notarized JSON-RPC error and never reaches the upstream
server — deny-by-default becomes *structural* for brokered tools rather than
cooperative. It does **not** stop out-of-band calls (raw HTTP / subprocess) the
agent makes without transiting the broker; pair it with network egress
containment for that.

The optional `[mcp]` extra (`pip install 'abom-cli[mcp]'`) pulls in `httpx` for
the broker's upstream transport and for `abom scan --mcp`.

## The MCP gateway (ABOM v0.2)

`abom gateway` is the broker's v0.2 evolution ([spec draft](../spec/abom-0.2-draft.md),
ADRs [0001](../docs/adr/0001-abom-gateway-v02.md)–[0006](../docs/adr/0006-inspect-default.md)).
Same choke-point position — agent ⇄ gateway ⇄ upstream MCP server — but the
product is the **evidence**: every `tools/call` (allowed, denied, *and*
would-deny) becomes a signed, hash-chained Action Provenance Record, fsync'd
to an append-only log *before* the call is forwarded. A call the log cannot
record is never forwarded, so a forwarded-but-unrecorded call cannot exist.
Arguments enter the log as **salted-HMAC commitments plus a de-identified
shape** — never content — and `verify_argument_claim` settles disputes about
what a call contained. Log files open with a signed epoch, link across
rotations, and refuse a second writer; degradations are recorded as signed
events, not gaps.

The default mode is **inspect-only** — the signed `would_deny` log is the
deliverable. `--enforce` turns the same policy into inline blocking; `--floor`
layers an org-scoped minimum that no project template can weaken (a
contradicting template is refused with a signed conformance-violation record).

```bash
abom gateway run examples/policies/gateway-baseline.json -u https://mcp.internal/rpc \
    --floor examples/policies/org-floor.json          # inspect-only by default
abom gateway verify gateway.apr.jsonl                 # chain + signatures (+ rotation links)
python demo/gateway_demo.py                           # live end-to-end demo (make gateway-demo)
```

Already running Google's Model Armor? Keep it — and make it auditable:
`--model-armor-template projects/<p>/locations/<l>/templates/<t>` screens each
call via the sanitize API and **seals the verdicts into every signed record's
`detectors[]`** (token via `ABOM_MODEL_ARMOR_TOKEN`). The screen never gates —
policy decides; the screen's word becomes third-party-verifiable evidence.
*They decide, you prove.*
