1.2.0-rc.1 / Portability and clients

Free evidence archives

Export a closed archive and verify it using independent trust pins.

The native profile odexa-free-evidence-snapshot-1 exports a closed, signed inventory for one free agreement and imports it into a separate inert archive. The wire version is explicitly 1.2.0-draft.3; the original draft 1 policy documents remain unchanged. No draft 2 envelope is relabelled.

This implements the bounded free-evidence archive gate. It does not import live agreements, appoint a replacement provider, transfer rights, create credentials, reactivate access or prove that every event in the world was recorded. Paid agreements and asset-manifest closures outside this profile fail closed.

Interfaces

bundle_bytes = build_bundle(
    store,
    agreement_id=agreement_id,
    snapshot=current_origin_snapshot,
    history=authority_history,
    export_operation=operation,
    private_key=export_private_key,
    key_id=export_key_id,
    now=trusted_utc_clock,
)

trust = ExternalTrust(
    current_snapshot=independently_acquired_current_origin_snapshot,
    pinned_documents=independently_retained_authority_and_policy_bytes,
    client_keys=independently_configured_public_client_registry,
)
report = verify_bundle(bundle_bytes, trust=trust, now=trusted_utc_clock)
archive = EvidenceArchive("/private/archive-directory/evidence.sqlite3")
receipt = archive.import_bundle(bundle_bytes, trust=trust, now=trusted_utc_clock)

The implementation is profiles/portable_evidence.py; machine structure is in profiles/portable.schema.json. The caller authenticates the export requester and authorizes access to that agreement. The builder is an in-process API, not a public endpoint or ownership-checking HTTP handler. It performs no network calls.

The operation is the native resolver’s export_evidence kind with an explicit agreement ID, capability export_evidence, signing role odexa-evidence-export+jws, endpoint exactly the appointed service base URL plus exports, zero offer/access/use durations and payment_mode:"none". Its request equals the retained offer’s exact resource/actions/purposes/obligation-support request. An ordinary receipt or collector signing role is insufficient.

The private signing key must match the active origin-appointed public export key. The builder observes current origin authority, checks the local history high-water mark, evaluates the full operation and repeats those checks after signing/serialization. The global crypto type registry remains unchanged; the new signature role is explicitly allowed only where used.

Closed inventory and source cut

The wire envelope has exactly protocol_version, profile, manifest_jws and blobs. Each blob is exact bytes in canonical base64url, addressed by its SHA-256 digest. The signed manifest contains the agreement/service binding, bundle UUID, creation time, export operation and authority/policy digests, declared source scope, cut and inventory.

Each inventory entry specifies kind, logical identifier, exact-byte digest, byte length and media type. The verifier rejects missing, changed, duplicated, unlisted or unrelated material. Shared exact bytes can use one blob, while logical artifacts remain separately identified. The implementation caps the bundle at 16 MiB, each artifact at 1 MiB and inventory/blob lists at 2,048 entries. Exceeding a limit fails; it does not produce a truncated export.

One SQLite read transaction captures:

  • The indexed agreement state, exact offer and explicit acceptance payload/JWS, and original signed receipt.
  • The first retained status statement per lifecycle version and all retained current-status attestations, preserving distinct signatures at an unchanged lifecycle version.
  • All collector intakes indexed to the selected agreement, including their original signed report payloads.
  • Every retained gateway admission for the agreement, including unresolved admissions and terminal-event binding.
  • Retained authorization decisions indexed to the agreement, with their exact operation, decision, origin authority, policy and observation bytes.
  • Exact offer contexts and the public client/gateway key descriptors needed by those artifacts.

The cut records scoped record count/max sequence, status-history references, attestation count and admission/decision/key counts. It names all_retained_free_material_for_one_agreement_at_sqlite_snapshot and expressly sets global_completeness_claimed:false. The SQL snapshot is taken before signing; current authority is a separate later observation.

This proves closure against the signed inventory and the stated source selection. It cannot reveal events never recorded, deleted before the source was retained, unavailable in another system, or dishonestly omitted by an authorized exporter that also controls its source declaration. It does not claim global chronological completeness. Decisions for offer/accept operations that were never indexed to an agreement are not asserted to be part of that scoped decision table cut; the pinned offer/assent/receipt chain is still independently checked.

Related-event references must resolve to exact report bytes inside the selected snapshot. A cross-agreement reference that cannot be closed causes failure. This first profile rejects non-null asset references and derivation references because a complete native asset-manifest import chain is not implemented here. It never drops such references and calls the remainder complete.

Independent trust and signature verification

ExternalTrust is trusted local configuration, never a field of the bundle:

  • current_snapshot is exact authority/policy bytes and trusted transport testimony obtained independently from the origin. The resolver checks its binding, freshness and current export appointment.
  • pinned_documents maps SHA-256 digests to exact independently retained historical origin authority/policy bytes. Current snapshot bytes also act as current pins. Exporter-carried documents do not populate this map.
  • client_keys maps a key ID to an independently configured descriptor: client ID, role, principal list, reporter ID, key ID and strict public P-256 JWK, plus revoked:boolean. The carried descriptor must match. An exporter’s declaration that a client key is trustworthy does not establish trust.

For example, an operator can supply its existing origin-history store and provisioning registry through a separately authorized adapter. A receiver without those anchors fails verification; it must not silently substitute the exporter’s own copies. No human account credentials, client secrets or private keys are required for verification.

The verifier checks the signed manifest’s historical and current export authority, exact signer key material and all bound scope. It verifies acceptance with the independently pinned agent key and principal assignment, then receipt signature and exact offer/acceptance digests, terms, nonce, binding and access/use windows.

Historical service signatures must use an origin-appointed key and permitted signature type within the key/authority time window and applicable service/delegation scope. A retired historical signer can remain valid under a different currently authorized export signer. Known revoked public material from the current origin snapshot or independently pinned origin history causes rejection, including when current metadata has removed the old key. Revoked client material is similarly rejected. This is a conservative known-compromise policy, not proof that a key was never compromised or that an external historical timestamp was independently witnessed.

Status statements retain their exact signatures and authority revision/digest. Conflicting meanings at one lifecycle version or terminal-to-active resurrection fail. Collector intakes must match original verified reporter bytes and their separate source category. Gateway reports must match a retained permitted admission and cannot precede it. The importer does not convert client claims into observed use, or signed gateway testimony into independent proof that bytes reached an end client.

The free-store lifecycle has initial active version 1, with the receipt’s recording time and accepted reason, followed by at most one terminal version 2. The indexed reason must be bounded text and its effective time must lie between the receipt and snapshot. A revoked index requires a matching original signed terminal status. The service can lazily persist expiry without emitting a status: this is accepted only at the exact receipt access deadline with access_expired, and the verification report labels it exporter_asserted_lazy_expiry_at_receipt_deadline. It does not invent a signed status. The report separately lists retained signed status versions, indexed state assurance and access state at the snapshot; an initial index left active past its receipt deadline is reported expired for access.

Every admission, including one without a terminal report, must fall between receipt recording and snapshot creation, name active lifecycle version 1, precede its access expiry, and not follow a retained terminal transition. Its authority observation interval and pinned historical introspection scope are checked. Timestamps have one-second resolution; admission or active-status ordering within the same second as revocation cannot be reconstructed and is explicitly not verified. A source with another lifecycle chronology fails this profile rather than silently expanding its meaning.

Historical signature and admission checks synthesize an evaluation observation from independently pinned bytes and the retained claimed time. They establish pinned historical scope/window consistency, not that the document was current at that time, that every intermediate withdrawal was observed, or that HTTPS freshness was independently witnessed then. Current export authority uses the separately acquired current snapshot. The archive’s assurance does not upgrade historical source testimony into current permission or independently witnessed chronology.

Historical operation decisions are recomputed from independently pinned origin/policy documents. Existing decision records contain acquisition time rather than exact execution time; verification establishes that the retained decision is valid at a point in its declared zero-to-five-second observation window. It does not invent a more precise execution timestamp.

Current export authority is checked again after semantic verification. Archive import repeats verification after acquiring its write lock so a delayed lock cannot preserve an expired pre-lock authority check. These are bounded local checks; they do not create global instantaneous revocation consensus.

Archive import and provider changes

EvidenceArchive requires a private owned directory/file (0700/0600 when created) and rejects symlinks, hard links and a destination containing operational tables. Its only application table stores immutable exact bundle bytes, digest, identity and import report. It cannot be constructed over the live free-service database.

Imports serialize through SQLite with FULL synchronization. Exact retries of an origin/bundle UUID return the retained import result. A changed byte stream under the same identity conflicts. Verification failure or a late authority failure leaves no new archive row. Original issuers, signatures, contexts and statement history remain inside the retained bundle.

get(origin,bundle_id) returns those bytes and their earlier import report, marked historical-only with no access reactivation. That report describes verification at import time; reading an archive later does not assert current trust or permission. Reimport performs current checks again before accepting an exact retry.

Operational migration requires a new explicit origin provider appointment, separate credential/key provisioning, and the supported fresh-agreement handover procedure. Transparent agreement assignment is not defined by this edition. Copying this archive into another provider does none of those things. Existing receipt or accepted-use windows are not rewritten.

The separate selected storage free archive now preserves signed storage-session and exact copy-parent evidence under its own profile identifier. This ordinary free profile remains closed and does not automatically accept those envelopes.

The separate native asset archive variants now close exact manifest/representation/derivation dependencies for ordinary and selected-storage free agreements. The ordinary profile described here still rejects asset references; callers must explicitly select the asset variant and supply independent asset trust. The separate paid archive variants now preserve payer/verifier proofs and exact payment audit replay for ordinary, storage and asset-aware paid agreements.

This ordinary free component has no HTTP exporter/importer or implicit paid/asset-profile import. There is no privacy-redaction protocol, key escrow, remote encrypted backup, retention deletion workflow or full storage anti-tamper/rollback system in this component. Public evidence may contain principal or reporter identifiers; authorization and privacy handling remain necessary at the application boundary.

Verification evidence

34 focused Python tests passed using:

PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -p test_portable_evidence.py -v

Cases cover the real export resolver; signed agreement/report closure; gateway admission correlations; historical decision recomputation; status conflicts and multiple attestations; missing/extra/tampered artifacts; independent key pins; known compromise and key rotation; paid/asset-reference rejection; same-ID byte conflicts; concurrent archive retries; restart; private paths; separation from operational databases; and authority expiry during signing, verification and archive-lock waits.

The same test module emits nine explicitly labelled structural cases with --cases; independent AJV 8.20.0 validation matched all nine structural outcomes (five positive, four negative). JSON Schema validates structure only. Exact-byte correlation, signatures, independent trust, lexical integer restrictions, current authority and closure require the semantic verifier. Tests do not establish production deployment or whole-protocol release-candidate status.

Odexa / Protocol explorer

This page. Your terms.

Inspect this website’s published policy and see how a proposed use is evaluated.

Current pagehttps://odexa.io/guides/portability/
Loading policy…

Published JSON
Open JSON

This is a local policy check, not a signed agreement or proof of agent compliance. Other published licences and applicable rights still apply. How policy evaluation works →

Odexa / Get in touch

Start a conversation.

Tell us what you have in mind. We’ll respond where we can.

We use these details to review and respond to your enquiry. Please leave out confidential information. Submitting does not subscribe you to marketing. Privacy policy.