Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@ All notable changes to Agent Manifest are documented here. Format follows [Keep

## [Unreleased]

## [0.6.0] — 2026-07-26

Fixes the CLI that every document described but that nobody could run, and closes a signature-downgrade gap in the verifier.

### Fixed

**[SDK]** **The documented CLI invocation now works.** Every command was nested under a redundant second `manifest` group, so the real invocation was `manifest manifest verify signed.json` while the README, the docs site, the PyPI description, and the CLI's own module docstring all printed `manifest verify signed.json`. Running the published quickstart failed at the first step with `Error: No such command 'keygen'`. `tests/test_cli.py` used the nested form, so CI never caught it. Commands are now attached to the top-level group as documented; the nested spelling still works, is hidden from `--help`, and prints a deprecation warning (removal in 1.0). `test_documented_commands_are_top_level` guards the surface.

**[SDK]** `verify_manifest()` now cross-checks the signed `crypto_profile` against `signature.algorithm` and fails closed on a downgrade (spec 4.2). `crypto_profile` is inside the signing pre-image, but the whole `signature` block is excluded from it (spec 3.6), so the algorithm identifier could previously be rewritten without disturbing the signed bytes: a manifest declaring the post-quantum profile verified `VALID` on a classical-only Ed25519 signature. The check runs independently of `trusted_keys` (the downgrade is a property of the manifest, not of the verifier's key material) and is one-directional: it rejects a signature weaker than the declared profile requires and permits a stronger one, so an issuer dual-signing ahead of the profile flip is not flagged. New conformance vector `AM-VEC-020`.

### Documentation

**[SDK]** `docs/getting-started.md`: new "Watch it catch a change" step. Shows the two ways verification fails and how they differ — an edit to the signed record (signature fails, `signature_verified: false`) versus runtime drift against an intact signature (declared-vs-actual binding fails, `system_prompt: MISMATCH`) — and names the boot-time boundary, pointing at `attest_runtime_state()` for freshness. All printed values are copied from real runs.

**[SDK]** `docs/api-reference/cli.md` is now generated from the CLI by `scripts/gen_cli_reference.py` instead of hand-written. The hand-written page had drifted into fiction: it documented `manifest keygen --out/--print-pub`, `manifest verify --revocation-url/--min-slsa-level`, `manifest create --agent-id/--issuer/--model/--ttl-hours`, and `manifest revoke --crl-file/--key-file`, none of which exist, while omitting the options that do. `test_cli_reference.py` fails if the committed page drifts from the CLI again.

**[SDK]** CLI help output is readable: `\b` markers keep the example blocks from being rewrapped into one line, and `-o/--output`, `--enforce-hitl`, and `--enforce-attestation` have help text instead of appearing bare.

**[SDK]** Corrected CLI examples that could not have worked: `docs/operations/key-rotation.md` used the non-existent `manifest keygen --out ... --print-pub`, and `docs/index.md` plus `python/README.md` printed `manifest verify` without `--public-key`, which fails closed as `UNVERIFIABLE` and exits 1 rather than the `VALID` shown.

**[SDK]** `LIMITATIONS.md`: document that **Azure TDX is not supported for offline attestation** (hardware-confirmed). Azure runs TDX behind the Hyper-V paravisor, so the guest gets no signed DCAP quote — only a MAC'd `TDREPORT` via the vTPM — and rooting that as genuine silicon needs a networked service (Azure MAA). Offline TDX attestation is supported on non-paravisor guests (e.g. GCP C3); on Azure use SEV-SNP (`AzureCVMProvider`). Azure-MAA TDX support is tracked as a follow-up.

## [0.5.0] — 2026-07-21
Expand Down
143 changes: 94 additions & 49 deletions docs/api-reference/cli.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- Generated by scripts/gen_cli_reference.py. Do not edit by hand. -->

# CLI reference

The `manifest` command is installed with the `cli` extra.
Expand All @@ -6,99 +8,142 @@ The `manifest` command is installed with the `cli` extra.
pip install "agent-manifest[cli]"
```

Every command is invoked directly: `manifest verify signed.json`. Releases up to
0.5.0 nested them under a second `manifest` group; that spelling still works and
prints a deprecation warning.

## Commands

### manifest create

Create and sign a new agent manifest.
Create a draft manifest from a JSON config file.

```
manifest create [OPTIONS]
Usage: manifest create [OPTIONS] CONFIG

Create a draft manifest from a JSON config file.

CONFIG must be a JSON file with at minimum: agent_id, issuer, issued_at, expires_at,
and an artifacts block.

Example:
manifest create config.json -o draft.json

Options:
--agent-id TEXT SPIFFE URI identifying this agent role [required]
--issuer TEXT SPIFFE URI of the signing authority [required]
--model TEXT Model identifier (e.g. gpt-4o-2024-08-06)
--prompt-file PATH Path to the system prompt file (hashed, not stored)
--ttl-hours INTEGER Manifest validity window in hours [default: 8]
--crypto-profile TEXT standard | post-quantum | hybrid [default: standard]
--out PATH Output path for the signed manifest JSON
--help Show this message and exit.
-o, --output TEXT Write output to file (default: stdout)
--help Show this message and exit.
```

### manifest sign

Sign an existing manifest JSON with a key loaded from a file or environment variable.
Sign a draft manifest with Ed25519.

```
manifest sign [OPTIONS] MANIFEST_FILE
Usage: manifest sign [OPTIONS] MANIFEST_FILE

Sign a draft manifest with Ed25519.

KEY must be a file containing the 64-hex-character (32-byte) Ed25519 private key seed.

Example:
manifest sign draft.json --key private.hex -o signed.json

Options:
--key-file PATH Path to Ed25519 private key (base64url, no padding)
--key-env TEXT Environment variable holding the private key
--out PATH Output path (defaults to overwriting MANIFEST_FILE)
--help Show this message and exit.
-k, --key TEXT Path to raw 32-byte Ed25519 private key (hex file) [required]
-o, --output TEXT Write output to file (default: stdout)
--help Show this message and exit.
```

### manifest verify
### manifest keygen

Verify a manifest against its signature and optional runtime context.
Generate a new Ed25519 key pair for manifest signing.

```
manifest verify [OPTIONS] MANIFEST_FILE
Usage: manifest keygen [OPTIONS]

Generate a new Ed25519 key pair for manifest signing.

Writes:
private.hex - 64-hex private key seed (keep secret, mode 0600)
public.hex - 64-hex public key bytes

Example:
manifest keygen -d ./keys/

Options:
--revocation-url TEXT CRL endpoint to check for revocation
--public-key PATH Path to a trusted raw Ed25519 public key hex file
--enforce-hitl Fail if no valid HITL approval is present
--enforce-attestation Fail if no attestation report is present
--min-slsa-level INT Minimum SLSA level required [default: 0]
--help Show this message and exit.
-d, --output-dir TEXT Directory to write key files
--help Show this message and exit.
```

For local signature verification, pass the public key generated by
`manifest keygen`. Without a trusted public key, signed manifests fail closed as
`UNVERIFIABLE`.

### manifest revoke
### manifest attest

Append a signed revocation record to a local CRL file.
Extend the manifest hash into hardware and append the attestation block.

```
manifest revoke [OPTIONS] MANIFEST_ID
Usage: manifest attest [OPTIONS] MANIFEST_FILE

Extend the manifest hash into hardware and append the attestation block.

For TPM: requires tpm2-tools (apt-get install tpm2-tools). For swtpm in CI: set
TPM2TOOLS_TCTI=swtpm: before running.

Example:
manifest attest signed.json --provider tpm --level 1 -o attested.json

Options:
--crl-file PATH Path to the JSON-Lines CRL file [required]
--reason TEXT Revocation reason [required]
--revoked-by TEXT SPIFFE URI or email of the revoking authority [required]
--key-file PATH Path to the signing key [required]
--help Show this message and exit.
-p, --provider [auto|azure-cvm|tpm|sev-snp|tdx|opaque|software]
Attestation provider (default: auto)
--level INTEGER Minimum conformance level (0-3)
-o, --output TEXT Write output to file (default: stdout)
--help Show this message and exit.
```

### manifest keygen
### manifest verify

Generate a new Ed25519 key pair and write the private key to a file.
Verify a manifest against the local verification engine.

```
manifest keygen [OPTIONS]
Usage: manifest verify [OPTIONS] MANIFEST_FILE

Verify a manifest against the local verification engine.

Prints the VerificationResult as JSON. Exits with code 0 on VALID, 1 on any other
result.

Use --crl-path to load a revocation list and check for revoked manifests.

Example:
manifest verify attested.json --crl-path revocations.jsonl

Options:
--out PATH Output path for the private key (base64url, no padding)
--print-pub Print the public key to stdout after generation
--help Show this message and exit.
--enforce-hitl Fail unless a required HITL approval is present and unexpired
--enforce-attestation Fail unless the attestation report matches the manifest hash
--crl-path TEXT Path to a FileCRL JSON-Lines file for revocation checks
--public-key TEXT Path to a trusted raw Ed25519 public key hex file
-o, --output TEXT Write output to file (default: stdout)
--help Show this message and exit.
```

### manifest attest
### manifest revoke

Run the auto-provider and print the attestation report as JSON.
Generate a revocation record for a manifest ID.

```
manifest attest [OPTIONS]
Usage: manifest revoke [OPTIONS] MANIFEST_ID

Generate a revocation record for a manifest ID.

The record JSON can be submitted to your revocation registry or passed to a
RevocationStore instance in the verification endpoint.

Example:
manifest revoke 018f4a3b-... --reason "key compromise" --revoked-by security@example.com

Options:
--provider TEXT tpm | sev-snp | tdx | opaque | auto [default: auto]
--manifest-file PATH Manifest to extend into the attestation register
--help Show this message and exit.
-r, --reason TEXT Reason for revocation [required]
--revoked-by TEXT Identity of revoking authority (DID or email) [required]
-o, --output TEXT Write output to file (default: stdout)
--help Show this message and exit.
```

## Source
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pip install "agent-manifest[cli]"
manifest keygen -d ./keys/
manifest create config.json -o draft.json
manifest sign draft.json --key keys/private.hex -o signed.json
manifest verify signed.json # VALID
manifest verify signed.json --public-key keys/public.hex # VALID
```

## The agent attestation gap
Expand Down
13 changes: 8 additions & 5 deletions docs/operations/key-rotation.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ Before starting:
## Step 1 - Generate a new key pair

```bash
# Generate a new Ed25519 key pair
manifest keygen --out /path/to/new-signing-key.b64url --print-pub

# The printed public key goes into your trust anchor configuration
# The private key stays in the secrets manager - never in source control
# Generate a new Ed25519 key pair into a directory
manifest keygen -d /path/to/new-keys/

# Writes new-keys/private.hex (mode 0600) and new-keys/public.hex, and prints
# the new key_id to stderr.
#
# The public key goes into your trust anchor configuration
# The private key moves to the secrets manager - never into source control
```

Or in Python:
Expand Down
5 changes: 4 additions & 1 deletion python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,13 @@ manifest keygen -d ./keys/
manifest create config.json -o draft.json
manifest sign draft.json --key keys/private.hex -o signed.json
manifest attest signed.json --provider auto --level 1 -o attested.json
manifest verify attested.json
manifest verify attested.json --public-key keys/public.hex
manifest revoke <manifest-id> --reason "key compromise" --revoked-by security@example.com
```

Without `--public-key` the verifier has no trusted issuer key, so a signed
manifest fails closed as `UNVERIFIABLE` and the command exits 1.

## Cryptography

- **Standard profile**: Ed25519 (RFC 8032), SHA-256, RFC 8785 canonical JSON
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "agent-manifest"
version = "0.5.0"
version = "0.6.0"
description = "Agent Manifest SDK — cryptographically anchor all 10 artifacts defining an AI agent at deployment"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
Loading
Loading