Metadata-Version: 2.4
Name: primust-verify
Version: 2.0.0
Summary: Offline verifier for Primust VPECs (Ed25519 + post-quantum ML-DSA-65). Free forever. No account required.
Project-URL: Homepage, https://primust.com
Project-URL: Documentation, https://docs.primust.com
Project-URL: Verify, https://verify.primust.com
Author-email: "Primust, Inc." <eng@primust.com>
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.11
Requires-Dist: cryptography>=49
Requires-Dist: httpx>=0.27.0
Requires-Dist: primust-artifact-core>=1.3.0
Requires-Dist: pypdf>=4.0.0
Requires-Dist: pyyaml>=6.0
Description-Content-Type: text/markdown

# primust-verify

**Free forever. No account required. Apache-2.0.**

Offline verifier for Primust VPECs (Verifiable Process Execution Credentials).

```bash
pip install primust-verify
```

## Verify a VPEC

```python
from primust_verify import verify

result = verify(vpec_json)
assert result.valid
```

Or from the command line:

```bash
primust-verify vpec.json
```

Exit codes:

- `0` — valid
- `1` — invalid (signature mismatch, tampered, or failed checks)
- `2` — system error

## Zero-network verification

Use `--trust-root` to verify without any network calls:

```bash
primust-verify vpec.json --trust-root ./primust-pubkey.pem
```

This fetches no external resources. The public key PEM is the only trust anchor needed.

Download the Primust public key from:
`https://primust.com/.well-known/primust-pubkey.pem`

## Deferred math-tier proofs (PACs)

A governance run's ZK (Noir/UltraHonk) proof is computed asynchronously and
isn't ready at the instant the VPEC is sealed, so the sealed envelope marks that
proof artifact `unresolved_at_seal`. The envelope alone therefore can't
self-prove the math-tier claim — by default the verifier reports it as
unresolved and tells you to consult the live API.

To verify the math tier **fully offline**, supply the **PAC** (Proof Attestation
Certificate) — a separately-signed certificate that carries the completed proof
bundle and binds it to the VPEC:

```bash
# Fetch the PAC(s) for a VPEC (public, no auth):
curl https://api.primust.com/api/v1/vpecs/<vpec_id>/pacs > pacs.json

primust-verify vpec.json --trust-root ./primust-pubkey.pem --pacs pacs.json
```

The verifier resolves a deferred proof at one of two trust levels:

- **trustless** — the ZK proof is **re-verified locally** against the circuit's
  pinned verification key and its public input is bound to the VPEC's record
  root. Zero trust in Primust. Requires the Barretenberg `bb` CLI (see below).
- **attested** — only the PAC's issuer signature is checked (e.g. when `bb`
  isn't installed). Same trust basis as the live-API consult, but offline. The
  verifier emits an SI-13 disclosure noting the proof was not independently
  re-verified.

Pass `--require-reverified-proofs` to refuse the attested fallback — a deferred
proof then only clears if it is genuinely re-verified (trustless).

### Enabling trustless re-verification (`bb`)

Trustless re-verification shells out to the Barretenberg `bb` CLI. Install it
with [`bbup`](https://github.com/AztecProtocol/aztec-packages) (installs to
`~/.bb/bb`, which the verifier finds automatically) or point the verifier at a
specific binary with `PRIMUST_BB=/path/to/bb`. Without `bb`, the verifier
degrades to the attested path (or fails the artifact under
`--require-reverified-proofs`).

## You don't need a Primust account to verify a VPEC

This tool is free, open source (Apache-2.0), and works offline. Anyone can verify a VPEC — regulators, auditors, counterparties — without creating an account or contacting Primust.

## Verification paths

- `primust-verify` CLI — canonical local/offline verifier
- Evidence Pack `verify.html` — bundled local browser verifier
- `verify.primust.com` — hosted convenience verifier

The hosted site is useful for shared links and quick review, but it is not the
canonical zero-network / trust-minimized path.

## Options

| Flag                            | Description                                                                                                                      |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `--production`                  | Reject VPECs issued with test keys                                                                                               |
| `--skip-network`                | Fully offline: skip Rekor AND the default revocation check (result discloses `revocation_status=unknown_offline`)                |
| `--no-revocation-check`         | Opt out of the default online revocation check (disclosed as `revocation_status=unknown_offline`, never silently clean)          |
| `--revocation-check-url <base>` | Explicit revocation endpoint base URL (overrides the endpoint derived from the issuer identity; runs even with `--skip-network`) |
| `--revocation-snapshot <path>`  | Action Clearance only: caller-authenticated, time-bounded revocation snapshot JSON                                               |
| `--trust-root <path>`           | Use a local PEM file as trust anchor (zero-network mode)                                                                         |
| `--pacs <path>`                 | Supply Proof Attestation Certificate(s) to resolve deferred math-tier proofs offline                                             |
| `--require-reverified-proofs`   | Refuse the attested fallback: a deferred proof clears only if re-verified (trustless, needs `bb`)                                |
| `--json`                        | Output structured JSON instead of human-readable text                                                                            |

## primust-rely — evaluate a Clearance Requirement (relying-party side)

This package also ships `primust-rely`: the relying-party evaluator that
takes YOUR declarative **Clearance Requirement** (YAML or JSON) and a sealed
**Action Clearance** and answers pass/fail — offline, fail-closed. Primust
never enforces: you evaluate, you decide, and the output is your own
auditable **Reliance Decision** record (requirement digest, clearance
digest, per-check results, verdict, reasons).

```bash
primust-rely merge-approval.yaml clearance.json \
  --revocation-snapshot ./ac-revocations.json            # exit 0 = satisfied
primust-rely refund-release.yaml clearance.json \
  --revocation-snapshot ./ac-revocations.json --json     # print the decision
```

A minimal requirement (`requirement_version: 1`; full JSON Schema ships as
`primust_verify/clearance_requirement.schema.json`, examples under
`examples/clearance-requirements/`):

```yaml
requirement_version: 1
requirement_id: merge-approval
min_evidence_tier: operator_confirmed # canonical Source-axis ordering
allowed_results: [cleared]
require_finality: true # settled/final on every record
forbidden_reason_codes: [no_witness]
require_no_unresolved_gaps: true
max_age: 24h
issuer:
  name: Primust
  key_ids: [kid_api, kid_api_eu]
```

Fail-closed guarantees: an unverifiable artifact (bad seal, unknown issuer,
tampered bytes) never satisfies any requirement; unknown requirement keys
are hard errors (no silent ignores); a clearance missing a field a
requirement needs fails that check explicitly — never pass-by-absence.

Revocation is an unconditional reliance invariant, not an optional requirement
field. The snapshot must be independently authenticated and pinned by the
relying party; it is never accepted from the clearance itself or fetched
implicitly. It uses
`schema_version: action-clearance-revocation-snapshot/1`, carries ordered
`generated_at` and `expires_at` RFC3339 timestamps, declares
`coverage_scope: global_per_covered_key`, names its authenticated trust domain
in the required non-empty `covered_key_fingerprints` array, and lists revoked
clearance IDs, seal signer key IDs, and
`sha256:<64 lowercase hex>` fingerprints over the exact raw 32-byte Ed25519
keys. A snapshot producer that marks a snapshot `checked` MUST include every
revocation for every clearance signed by each covered key. Tenant-filtered or
otherwise partial feeds are forbidden by this schema; future scoped feeds need
a new schema bound to a load-bearing clearance scope. A checked snapshot is
usable only when `covered_key_fingerprints` contains the fingerprint of the
exact issuer key that verified the clearance. Another trust root is an
explicit `trusted_snapshot_scope_mismatch`. Only a current, globally complete
`status: checked` snapshot with no matching revocation yields
`checked_not_revoked`. Missing, stale, unavailable, malformed, or non-global
snapshots produce explicit unknown states and can never satisfy
`primust-rely`.
Known matching revocations are monotonic: a matching clearance ID or signer
key remains `revoked` even outside that otherwise well-formed snapshot's time
window; time bounds prevent a stale or future-dated clean claim, not a
previously observed revocation.
Timestamp fractions may carry one through six digits; validity comparisons
use a shared millisecond precision in both verifier implementations.

### Producer and authentication boundary

Version 2.0 ships the consumer contract, not a platform snapshot publisher.
The Primust API in this candidate does not expose a route or signed file that
can be treated as an authenticated
`action-clearance-revocation-snapshot/1`. Do not trust arbitrary JSON merely
because it came from a request, a clearance, or a TLS endpoint. Until a
separately pinned producer or signed wrapper is ratified and provisioned,
`primust-rely` is expected to fail closed with an unknown revocation state.

### 2.0 breaking-change and migration note

`primust-verify` 2.0 introduces the fail-closed revocation contract above.
Callers constructing `action-clearance-revocation-snapshot/1` must include
`coverage_scope: global_per_covered_key` and `covered_key_fingerprints`.
`primust-rely` now emits
`record_type: reliance-decision/2`; version 2 adds the mandatory
`revocation_checked` reliance check and the revocation fields under
`verification`.

This is a major release because a reliance call that previously returned
`satisfied` now returns `unsatisfied` until the caller supplies an independently
authenticated, current snapshot that covers the verified issuer key. The
snapshot argument remains syntactically optional so structural-verification
callers can upgrade without inventing revocation facts; it is semantically
mandatory for a successful `primust-rely` decision. Consumers that parse
`reliance-decision/1` must add `/2` support before upgrading.

`primust-verify` and `verify_action_clearance()` still separate structural
validity from authorization to rely: a correctly signed clearance can return
valid/exit 0 while revocation is `unknown_*`. That output is not authorization.
`primust-rely` is the fail-closed relying-party gate and exits 0 only when
revocation is `checked_not_revoked` and every declared requirement passes.

## Requirements

- Python 3.11+
- Optional: Barretenberg `bb` CLI — only for **trustless** re-verification of
  deferred math-tier proofs (see "Deferred math-tier proofs" above). Not needed
  for signature, schema, surface, gap, or attested-PAC verification.

## License

Apache-2.0

---

[Docs](https://docs.primust.com) | [Verify online](https://verify.primust.com) | [Primust](https://primust.com)
