# Portable paid evidence — draft-3 profiles

17 September 2026. Implemented in the working source under milestone M2, with the complete Python regression suite and focused actual TLS tests. This is a developer-preview component, not full candidate sign-off or independently verified settlement.

## Explicit profiles and interfaces

`profiles/paid_evidence.py` extends the same closed inventory and inert archive infrastructure as the [free archive](PORTABILITY.md). The public free entry points still reject paid material. There are four explicit paid selections:

| Profile | Agreement and dependency selection | Schema |
| --- | --- | --- |
| `odexa-paid-evidence-snapshot-1` | Ordinary fixed-quote agreement | [paid-portable](../profiles/paid-portable.schema.json) |
| `odexa-storage-paid-evidence-snapshot-1` | Signed `storage_sessions_v1` agreement and exact copy-parent closure | [storage-paid-portable](../profiles/storage-paid-portable.schema.json) |
| `odexa-assets-paid-evidence-snapshot-1` | Ordinary paid agreement plus native asset/version/derivation closure | [assets-paid-portable](../profiles/assets-paid-portable.schema.json) |
| `odexa-assets-storage-paid-evidence-snapshot-1` | Both selected storage and native asset closure | [assets-storage-paid-portable](../profiles/assets-storage-paid-portable.schema.json) |

All have `protocol_version:"1.2.0-draft.3"`. Their `source_scope` is respectively `all_retained_paid_material_for_one_agreement_at_sqlite_snapshot`, `all_retained_storage_paid_material_for_one_agreement_at_sqlite_snapshot`, `all_retained_assets_paid_material_for_one_agreement_at_sqlite_snapshot` or `all_retained_assets_storage_paid_material_for_one_agreement_at_sqlite_snapshot`. An import does not infer or downgrade this selection.

```python
from profiles.paid_evidence import (
    build_bundle, verify_bundle, PaidExternalTrust, PaidEvidenceArchive,
)

trust = PaidExternalTrust(
    current_snapshot=current_origin_snapshot,
    pinned_documents=independently_retained_origin_documents,
    client_keys=independently_configured_agent_and_gateway_public_keys,
    payer_keys=independently_configured_payer_public_keys,
    # asset_trust=independent_asset_trust,  # required for assets=True
)

bundle = build_bundle(
    paid_store, agreement_id=agreement_id,
    snapshot=current_origin_snapshot, history=authority_history,
    export_operation=export_operation,
    private_key=export_private_key, key_id=export_key_id,
    now=trusted_clock,
    storage=False, assets=False,
)
report = verify_bundle(bundle, trust=trust, now=trusted_clock)
archive = PaidEvidenceArchive("/private/archive/evidence.sqlite3")
archive.import_bundle(bundle, trust=trust, now=trusted_clock)
```

For selected storage, pass `storage=True` consistently and use a `StoragePaidStore`. For native assets, pass `assets=True` consistently, `asset_bundle_bytes` and `asset_trust` to the builder, and independent `asset_trust` in `PaidExternalTrust`. Ordinary paid storage format is `paid-agreement-2`; selected storage is `storage-paid-agreement-1`. Imports use an inert archive database, not either operational store.

The caller authenticates/authorises the export request. These are in-process APIs, not an unauthenticated HTTP exporter. No network request, debit, token issuance, new agreement or credential transfer occurs during archive verification/import.

## Exact retained payment artifacts

The [ordinary archive](PORTABILITY.md) defines the outer closed envelope, signed inventory, exact blobs, export authority, independent origin/reporter trust and base artifact kinds. Paid profiles additionally require:

| Artifact kind / logical identifier | Exact content |
| --- | --- |
| `payment_quote` / original quote UUID | Original accepted `payment.quote` JSON bytes |
| `payment_accepted` / agreement UUID | Original `payment.accepted_binding` bytes in the paid ledger |
| `payment_state` / agreement UUID | Current retained scalar state, excluding separately carried quote, accepted binding and mandate bytes |
| `payer_proof` / agreement UUID, only when a mandate exists | Closed `{payer_client_id,mandate_jws,payload_b64url,public_identity_b64url}` |
| `payment_audit` / canonical decimal source sequence | Closed `{sequence,kind,recorded_at,state_digest,input_b64url,context_b64url}` |

The decoded public payer identity is exactly `{payer_client_id,payer_id,key_id,public_jwk}`. It contains no secret, secret hash or account access credential. `mandate_jws` uses `odexa-payment-mandate+jws`; the retained payload must match its decoded bytes exactly. The decoded scalar state is exactly `payment_state,access_state,checks,last_sequence,last_effective_at,provider_reference`. Each `checks` entry is the original check UUID and exact provider-response payload digest in retained order.

The signed source cut adds `payment_audit_count`, `payment_audit_max_seq` and `payer_proofs_count`. One SQLite read transaction selects these artifacts together with the agreement, status attestations, reports, admissions and decisions. The original immutable quote registry must match the retained state. Payment-verifier authority/policy bytes come from the separately retained origin-history store, matching the digests in each original verification context; they are not fetched from an exporter-selected URL.

The reference bound is 256 distinct payment checks and at most 260 changing audit entries, within the outer archive's 2,048 items and 16 MiB. Payment artifacts remain at most 1 MiB each. Selected nested asset evidence retains its separate 8 MiB limit. A larger history fails explicitly; it is never silently truncated. Source sequence gaps are allowed because other agreements can occupy the intervening database rows. Reordering uses the numeric retained sequence, not lexical sorting of its string identifier.

## Independent payer and verifier trust

`payer_keys` is independent trusted configuration mapping key ID to `{payer_client_id,payer_id,key_id,public_jwk,revoked}`. Its public fields must equal the carried descriptor and `revoked` must be false. A valid signature from an exporter-carried key alone is insufficient. Known revoked public material cannot be rescued through another key alias. Payer authorisation remains separate from the agent's agreement assent.

The original quote must match the paid offer's exact quote digest, and the ledger's accepted binding must match the exact offer, principal, request and original receipt's recording/access/use times. Selected storage uses its original signed bytes; an internal projection is never substituted for the accepted digest. A paid context must also have explicit paid operational authority.

For each provider verification, the importer reconstructs the exact canonical payment-check request from the retained quote, acceptance and mandate. It reconstructs the scoped `verify_payment` operation and recomputes authority from independently pinned historical origin/policy documents at the retained observation time. The result's authority fields must match the original context exactly. The original `odexa-payment-status+jws` must verify with that appointed key and preserve the exact response payload.

The paid state machine checks the response's agreement, check, quote, payer, amount/currency, provider, mandate, accepted resource scope, sequence, freshness and transition. A correct signature does not excuse a wrong amount or scope. Current/independently known historical compromise causes rejection. A retired historical verifier can remain valid for historical evidence; its service/key identity cannot be rebound to a different issuer/public key. Historical validity does not restore current verification authority.

The export signer must remain currently authorised at the beginning and end of verification. Archive import repeats verification after its write lock, using a fresh clock. These are bounded local checks, not global instantaneous revocation consensus.

## Ordered replay and access correlation

Audit kinds are exactly `accepted`, `payer_mandate`, `verification`, `expired` and `revoked`. The first entry must reproduce the accepted binding. A mandate step must preserve the original separately authenticated payer proof. A verification step must preserve the original signed provider response and exact authority context. Expiry/revocation steps carry no invented payment input. Non-changing duplicate audit steps, unknown kinds, regressing/future recorded times and mismatching identities are rejected.

After every step, the importer recomputes the canonical scalar state and checks its exact SHA-256 against `state_digest`. The final replay must equal the complete retained scalar bytes, including every retained check ID/digest. This detects missing/reordered/substituted entries against the retained state even if an authorised exporter repairs and re-signs the outer inventory. It cannot detect an exporter that falsifies an entire self-consistent source selection before making its declaration; the existing no-global-completeness qualification still applies.

The original receipt remains `pending_payment`. Access begins at lifecycle version 1 and changes only when its replayed access state changes. Failed/pending payment observations do not manufacture a new access grant. Confirmation can activate access, and reversal/expiry terminate it; later financial facts cannot restore terminal access. The indexed state/version must correlate with those transitions and every retained signed status.

An explicit owner/client revocation requires its original signed service status. A reversal recorded by the authenticated payment verifier can terminate the access index without a separately emitted status; the importer labels the index as correlated exporter evidence, not a signed status that never existed. Expiry remains fixed to the original receipt's access deadline. An index that has not yet lazily recorded expiry is still reported expired for access at a later snapshot.

Activation/reversal effect times must lie in the retained observation interval. Explicit revocation can be recorded within the service's bounded transaction window after its signed effect time. A paid admission must name a confirmed active lifecycle and a contemporaneously retained confirmed check; an old confirmation alone is insufficient. Whole-second timestamps cannot resolve ordering within the same second as a transition, and the report expressly leaves that ordering unverified.

Recorded audit times and synthesized historical observations are exporter testimony correlated with independently pinned bytes and signed statements. They are not independently witnessed timestamps or proof that every intermediate authority withdrawal was observed.

## Storage, asset closure and assurance

Selected storage additionally preserves exact related-event and copy-parent dependencies and derives qualified storage metrics from the accepted terms. Native asset variants require the exact root set for all selected report asset/derivation references, complete signed dependency closure, independent current knowledge for each source origin and per-report correlation. A paid result does not waive either dependency check. The catalogue cut remains distinct from the agreement SQL cut; changed report roots fail closure rather than claiming a global atomic cut.

The returned `payment` report includes replayed financial/access state, access at the snapshot, quote digest, mandate-authentication flag, verification/audit counts and last provider sequence. It explicitly reports `bank_settlement_verified:false`, `recorded_times_independently_witnessed:false`, `global_payment_history_complete:false` and `reactivates_access:false`. Client use assertions and gateway testimony retain their original source qualifications. Matching asset evidence does not prove model use or receipt by a remote client.

`PaidEvidenceArchive` inherits the private owned database, no operational-table reuse, exact retry/conflict, append-only retention and historical-only retrieval rules. Its read API returns original bytes and the earlier import report; reading it later does not assert current trust or permission. Reimport performs current checks again. No secrets or live tokens are exported.

## Evidence and remaining review

`tests/test_paid_evidence.py` has 18 methods covering actual HTTPS assent/payer/verifier flows, pending without a payer, failed-to-confirmed recovery, exact verification retry, reversal, late confirmation, pending expiry, original signed revocation, protected delivery/intake, independent payer trust, correctly signed wrong amount, altered audit/state/counts/order, retired versus revoked verifier, source/archive restart, credential exclusion, post-lock expiry and all four archive variants. Asset variants exercise transitive-parent omission after the exporter re-signs the inventory. Storage execution itself retains its separately recorded Node evidence; these new storage archive tests authenticate signed claims.

The focused run passes, the relevant 80-method regression slice passes, and the complete Python run passes **571 tests**. AJV validates **56 new structural cases** and **245 combined cases across 15 schemas**. Structural positives are not independent semantic proofs. Full counts/commands are in [the integration report](../verification/REPORT.md).

The implementation is root-authored against the actual paid store/state machine and current shared archive. The interrupted historical `.py.txt` prototype remains excluded. The later [M3 handover procedure](PROVIDER-HANDOVER.md) now preserves original paid evidence while requiring explicit fresh successor agreements. Full independent Node paid/asset conformance, installed operator tooling and completed second/four-perspective review remain M5/M6. This archive component alone does not claim those milestones complete.
