provin
← Back to home

IMPLEMENTATION SPECIFICATIONS

Technical specifications, traced to the implementation

This page reads the provin OSS code, dependencies, and defaults as an implementation snapshot—not a roadmap. It states not only what is selected, but what each choice guarantees at runtime and what remains outside that guarantee.

main @ 793c27923f57 · 2026-07-17 · 0.x / PoC source ↗

SYSTEM SHAPE

Two planes and portable evidence

A standalone node composes a control plane for registry and coordination with a data plane for event processing from one configuration. Evidence crossing both can be taken away from the live node and verified independently. Note: the standalone layout is as of this snapshot — it has since been retired; current main splits the planes into the cmd/network + cmd/pipeline binaries (see CHANGELOG).

01

Control plane

DID, key, schema, chain-relationship, and audit capabilities are exposed as ConnectRPC services. State lives in YAML and files, and a node may run as a pure registry with no pipeline loops.

source ↗

02

Data plane

Source, Chained, Sink, and Custom processes pass events over NATS and repeat verify, transform, and sign. Each process is a peer following the same contract.

source ↗

03

Evidence path

Credentials, audit verdicts, transparency-log entries, and receipts are retained append-only and can be exported as a bundle for offline verification. Transport trust is distinct from provenance verification.

source ↗

SELECTION CATALOG

Technical choices by responsibility

Versions come from go.mod and generator configuration at this snapshot. Rationale is limited to responsibilities and behavior that can be confirmed in the code.

AreaSelectionImplementation effectCurrent boundary
Runtime Go 1.25.5 The standalone node and provin operator CLI build from one module, sharing concurrency, standard-library TLS, and built-in testing.

Artifacts are dist/standalone and dist/provin (as of the snapshot — standalone has since been retired in favor of cmd/network + cmd/pipeline). No database or second language runtime is mandatory.

source ↗
Service API Protobuf + Buf v2 + ConnectRPC Service contracts live in dplaax.*.v1 protobuf packages, generating Go types and Connect handlers. Buf uses STANDARD lint and FILE-level breaking checks.

Services share one HTTP listener. API compatibility is managed through v1 packages and breaking checks.

source ↗
Event transport Core NATS · server 2.12.6 / Go client 1.50.0 Subject-based pub/sub decouples processes; operator/account JWTs grant cross-organization subjects. A FlushTimeout after publish confirms broker receipt.

This is not JetStream. There is no durable queue, redelivery, or exactly-once behavior; the residual delivery guarantee is at-most-once.

source ↗
Transformation JSONata 1.5.4 + JSON Schema 2020-12 Configuration compiles filters and a converter, while schemas validate input and output boundaries. JSONata expressions compile at startup.

Chained processing is stateless one-in/one-out. Stateful aggregation belongs on the Source/aggregate side.

source ↗
Credential proof W3C VC Data Integrity + Ed25519 A signed credential records content hashes, process, time, and predecessor rather than embedding the payload. eddsa-jcs-2022 is the default suite.

Ed25519 is the only signing key type. P-256 and P-384 are outside the current PoC.

source ↗
Canonicalization RFC 8785 JCS + URDNA2015 Equivalent JSON representations become the same signing input. JCS is default; the RDFC suite uses embedded JSON-LD contexts and URDNA2015.

JSON-LD contexts are never fetched at runtime. Integers over 2^53 in RDFC @json values must be represented as strings.

source ↗
Identity & keys did:dplaax + delegation credentials An Owner → Pipeline → Process hierarchy resolves issuers, authentication keys, and delegated authority. Process signing keys remain in registry custody.

The current keystore is file/YAML based. Vault, HSM, and cloud KMS are interface targets, not shipped integrations.

source ↗
Configuration & state HOCON + YAML / files reference.conf, application.conf, and an environment overlay are layered in order. Reference stores keep control state and evidence in YAML/files; storage and PDP remain interface boundaries.

The provin node can run without a database. A database-backed registry is not shipped; the selected auth provider, PDP, or adapter may have its own database requirements.

source ↗
Observability OpenTelemetry 1.44 + Prometheus Emit, verification, audit-verdict, and related counters use OTel and can be exposed in Prometheus format at /metrics.

The metrics endpoint is disabled by default. No tracing backend or dashboard is embedded as a product dependency.

source ↗

PIPELINE RUNTIME

One Chained-process execution

The order from reception to onward publish is the execution contract that preserves provenance continuity and payload identity. An input that becomes unverifiable does not produce the next credential.

  1. 01

    Verify the adjacent credential

    Evaluate predecessor Data Integrity, signer authenticity, and chain consistency.

  2. 02

    Persist ingress credential

    Synchronously retain evidence required for later verification; fail when it cannot be stored.

  3. 03

    Resolve the payload

    Obtain inline or by-reference payload bytes.

  4. 04

    Check hash binding

    Compare payload SHA-256 with the content hash declared by the credential.

  5. 05

    Validate input schema

    When configured, validate the structure before transformation.

  6. 06

    Evaluate filters

    Run JSONata filters in order; a mismatch is recorded as an intentional drop.

  7. 07

    Run converter

    Use JSONata to transform one input into one output.

  8. 08

    Validate output schema

    When configured, validate the transformed structure.

  9. 09

    Strict-decode JSON

    Reject duplicate keys and trailing data while retaining numbers as json.Number.

  10. 10

    Hash input and output

    Fix both sides of the processing step as content-addressed values.

  11. 11

    Sign a new credential

    Carry exactly one previousCredential and extend a linear chain.

  12. 12

    Observe and publish

    Notify the observer, publish to the primary subject, then append the emission log after success.

Confirm the execution order in code and README ↗ Confirm sequence and delivery boundaries in code ↗

Verdict and delivery semantics

Verified only

Only ConfidenceVerified advances on a producing process. Failed and indeterminate verdicts close the path as errors.

At-most-once

Core NATS publish plus flush confirms broker receipt, but it does not provide consumer redelivery or persistence.

Known crash window

A stop after publish but before log append leaves a delivered-but-unlogged gap. File log records durable intent first, preventing number reuse; a flush timeout after broker acceptance can still look failed and permit reuse.

CONCRETE SAMPLE

One input becomes a credential and a verdict

An E2E walkthrough proves that the whole deployment runs. This fixed sample lets a reviewer inspect the wire shape and result without running the stack. Hashes and proofValue are abbreviated for display.

01 / INPUT

POST /ingest/src/push
{"sensor":"temp-01","celsius":21.5}

02 / FIRSTDROP CREDENTIAL — EXCERPT

{
  "issuer": "did:dplaax:…:pipeline:readings:process:s1",
  "credentialSubject": {
    "pipelineId": "readings",
    "processId": "s1",
    "transformationClaim": "provin:convert",
    "inputHash": "sha256:3b8…",
    "outputHash": "sha256:3b8…"
  },
  "proof": {
    "type": "DataIntegrityProof",
    "cryptosuite": "eddsa-jcs-2022",
    "proofValue": "z…"
  }
}

03 / VERIFICATION RESULT — EXCERPT

{
  "confidence": "CONFIDENCE_VERIFIED",
  "axes": {
    "dataIntegrity": "CONFIDENCE_VERIFIED",
    "signerAuthenticity": "CONFIDENCE_VERIFIED",
    "chainConsistency": "CONFIDENCE_VERIFIED"
  }
}

A FirstDrop issued by a Source Process has no previousCredential. The next Chained Process adds previousCredential and extends a line satisfying outputHash[n] == inputHash[n+1].

What transformationClaim means — dPLaaX owns the grammar, the provin profile owns the meaning

The grammar of a claim like "provin:convert" in this sample (a single token) is fixed by dPLaaX; what asserting it warrants is normatively defined by the provin wire profile. The core is closure: a closed claim warrants that the declared inputs are the output’s complete information source, which is what licenses exclusion inferences ("this lot cannot be in that output").

provin:filter closed — declared inputs are the complete information source
provin:convert closed
provin:filter-convert closed
provin:aggregate closed fold over the declared input set
provin:enrich conformant-closed — exclusion holds for conformant flows only
provin:generate open — information beyond the declared inputs is acknowledged
provin:sink-receipt identity — a receipt, not a transformation

PROOF & CANONICALIZATION

Turn signing scope into reproducible bytes

The credential body is the single source of truth. Proof configuration and document are canonicalized and hashed separately, then concatenated. Unknown signed-scope fields remain in the body map, so re-encoding does not silently discard them.

Data Integrity signing input

hashData = SHA-256(canon(proofConfig)) ‖ SHA-256(canon(document))
proofValue = base58btc(Ed25519.sign(hashData))
source ↗

eddsa-jcs-2022

implemented · default

Canonicalizes JSON with RFC 8785 JCS and signs with Ed25519. This is the Phase 1 required suite.

eddsa-rdfc-2022

implemented · optional

RDFC suite using embedded contexts and URDNA2015. External context fetching is forbidden at runtime.

Strict JSON decoder

all signed paths

Avoids duplicate keys, trailing data, and implicit float64 conversion. Lint constrains signed JSON to one decode path.

Linear credential chain

implemented

previousCredential is singular. Payloads are not embedded; input and output hashes bind content to the record.

verification confidence

The verifier evaluates data integrity, signer authenticity, and chain consistency. The weakest result on the failed < indeterminate < verified lattice becomes the overall verdict.

TRUST BOUNDARIES

Separate service authorization from provenance verification

Whether a request may enter, whether a peer is authentic, and whether the data provenance is valid are different questions. provin handles them as three layers with different evidence and failure conditions.

LayerProtected boundaryMechanismFailure behavior
L1 · Service authorization Administration and registry APIs Bearer token → PDP, enforced by protobuf method-policy annotations and PEP interceptors. The default o3co backend verifies JWTs. Missing configuration stops startup. An empty static allow-list means deny all.
L2 · Peer wire proof ChainPeer and Payload services Ed25519 signs a JCS view of signerDID, operation, nonce, issuedAt, and fields; the authentication key resolves from DID. Checks time window, one-time nonce, and restart epoch. There is no auth-off mode.
L3 · Provenance evidence The data itself Credential chain, content hashes, transparency log, and audit verdicts verify provenance independently of transport trust. A failed or unresolved axis closes the producing path.

PDP backends do not imply equal authentication

o3co verifies JWTs. OPA relies on policy to perform authentication; Cedar receives a raw bearer principal unless policy adds validation; static checks token presence or an allow-list only. Backend labels must not be treated as equivalent assurance.

source ↗

Current threat-model coverage

Detected conditions

Post-signature record changes, payload/content-hash mismatches, declared-chain discontinuity, and unauthorized signers or requests fail closed at their respective boundaries.

Trust dependencies

Issuer key custody, DID registry and resolution, verifier trust policy, transport confidentiality, clocks, and nonce operation support parts of the guarantee.

Outside the guarantee

Correctly signed false data, unrecorded processing, a compromised authorized key, and guessing low-entropy payload hashes are not prevented by the chain alone. A repository-wide threat model remains post-public-release work.

Security policy and trust boundaries ↗

CONFIGURATION & EVIDENCE

Use file-backed defaults and separate state from evidence

The provin node can run on YAML and files alone, while storage and PDP boundaries remain replaceable interfaces. Configuration, control state, evidence, and ephemeral state stay distinct; external requirements follow the selected auth provider, PDP, and adapter.

Configuration precedence

  1. 01

    reference.conf

    Complete embedded defaults. There are no hidden Go-side defaults.

  2. 02

    config/application.conf

    Optional node-specific settings override reference values when present.

  3. 03

    environment overlay

    The selected environment overlay is applied last before decoding into typed configuration.

source ↗

Where state lives

YAML control state

Registry state for DID, pipeline/process, schemas, and chain configuration.

File-backed evidence

Credential variants, resolution pool, audit queue/verdicts, receipts, and sink rejects. Credentials are content-addressed and append-only.

In-memory PoC state

Wire-auth nonces and related ephemeral state. A restart-epoch barrier closes cross-restart replay, but this is not a nonce store shared across nodes.

offline verification

provin bundle export collects credentials and dependent evidence; provin bundle verify validates them with no network access. JSON-LD contexts are embedded in the binary. Evidence is rotated to cold archive rather than deleted or mutated.

source ↗

Current transparency-log scope

The current implementation replays a hash-chained file log to detect modification. A CT-style log with Merkle inclusion and consistency proofs is staged work, not part of the present guarantee.

source ↗

OPERATIONS

Defaults and operational surfaces

Defaults lean toward local and bounded operation, while the table also identifies PoC behavior that operators must account for.

ItemDefault / implementationOperational meaning
Listen address 127.0.0.1:8443 Loopback by default; the node is not implicitly exposed.
HTTP mode cleartext h2c on loopback Non-loopback requires node-native TLS or explicit allow-cleartext behind an isolated TLS terminator.
TLS minimum 1.2 Go standard-library secure cipher defaults. Certificate rotation requires restart; there is no hot reload.
Health /healthz · /readyz Liveness and readiness are separate.
Metrics /metrics · default off Exposes OpenTelemetry counters in Prometheus format when enabled.
Data-plane dependency Core NATS Process-event transport. JetStream durable queues and redelivery are not used.
Authorization dependency external PDP or static o3co, OPA, and Cedar use an external PDP. Static is an in-process allow-list, not authentication. Auth-provider storage follows the selected implementation.
Durable state YAML / files · unbounded by default Back up the data directory and monitor disk. Relationship evidence can be rotated to cold archive while the node is stopped.
Resource sizing not published Minimum CPU, memory, disk, throughput, and latency recommendations are not published. Small container-limit E2E runs establish a lower bound; repeatable benchmarks are separately required for steady-state performance.
Credential / push limit 1 MiB / 1 MiB Credential and request-body sizes are bounded.
Resolver worker 30s · batch 64 · retry 5 · depth 1024 Re-evaluates unresolved chains in bounded batches with retry and depth limits.
Audit runner 30s · batch 64 · attempts 10 Processes the audit queue in bounded batches.

operator CLI

Commands cover owner init, pipeline/process create, schema registration, chain-relationship management, organization verify/diagnose, bundle export/verify, and evidence rotation. Owner private keys are RFC 8037 OKP JWK files written with mode 0600.

IMPLEMENTATION BOUNDARY

What exists in the pinned snapshot, and what must not be assumed yet

Only code confirmed in the 0.x / PoC snapshot is labeled implemented. Staged work expresses direction, not an availability promise.

Implemented

  • Source, Chained, Sink, and Custom process contracts with the NATS runtime
  • ConnectRPC registry, chain, payload, schema, signer, tlog, audit, and resolver services
  • Ed25519 Data Integrity, JCS, RDFC, and strict signed-JSON decoding
  • Three trust boundaries and fail-closed configuration validation
  • File-backed evidence, bundle export/offline verify, and evidence rotation
  • Health/readiness, optional Prometheus metrics, and standalone/operator binaries
  • DID login defaults to LEGACY_DID_LOGIN@1 — relationship-blind (no DID Document authentication / assertionMethod check; any controller-matched key passes). The OWNER contracts (relationship check, three-way kid match, audience required) are wired on main — not in the current v0.2.1 release; they ship with the next one

Staged or out of scope

  • JetStream, durable queues, retry/dead-letter, and exactly-once delivery
  • A stronger publisher contract eliminating ambiguous delivery and a distributed nonce store
  • P-256/P-384 signing and HSM, Vault, or cloud-KMS backends
  • A production transparency log with Merkle inclusion and consistency proofs
  • Database-backed registry or multi-node control-state replication
  • Adapters and connectors for external products or regulatory frameworks
  • Multi-key DID Documents on the OWNER path — a document with more than one controller-matched verificationMethod is rejected as ambiguous, fail-closed (can occur mid key-rotation — follow-up)
  • Mapping an HTTP-push ingest handle to the credential head it produced (known gap — public E2E finding E2E-F-030)

Current release, compatibility, and governance state

AreaCurrent stateHow to evaluate it
Public release provin OSS v0.3.0 and public E2E v0.2.0 are available. The technical detail on this page reflects the pinned 2026-07-17 snapshot; the node has since been reorganized into separate cmd/network + cmd/pipeline binaries (see CHANGELOG). Because this is a 0.x PoC, evaluate the public artifact and E2E in the intended environment.
Compatibility The credential Data Integrity wire is frozen. Go APIs, configuration, and other signed views remain mutable in 0.x. The freeze scope and its enforcement are detailed in the "wire freeze" section below. Evaluate wire and API stability separately even though both carry a 0.x version.
Security maintenance After publication, only the latest minor line is assessed and fixed. There is no SLA or bug-bounty program. Do not assume production support or backports.
Governance Maintainer-led (1o1 Co. Ltd.). GOVERNANCE.md defines the next-MAJOR procedure for wire changes and the path to a broader maintainer group. Maintainership does not yet span multiple organizations. See GOVERNANCE.md for the procedure; continuity still requires review.
Operational evidence Conformance vectors, W3C vectors, KATs, and E2E tests exist; production soak and public benchmarks do not. Reproduce the intended topology and workload before adoption.

wire freeze — what is frozen, and how it is enforced

The v0 credential Data Integrity wire — every byte that participates in a credential signature — is frozen. What holds the freeze is not a declaration or a process but tests in the repository: the official W3C vc-di-eddsa vectors, KATs, and sha256-pinned contexts.

Frozen

  • The credential @context set — credentials/v2 pinned to the W3C-published normative sha256, plus the embedded dplaax.dev/vc/v1 and provin.dev/vc/v1
  • The Data Integrity proof algorithm — SHA-256(canon(proofConfig)) ‖ SHA-256(canon(document)) and the base58btc proofValue encoding
  • Both cryptosuites and their canonicalizations — eddsa-jcs-2022 (RFC 8785) and eddsa-rdfc-2022 (URDNA2015), anchored to the official W3C vectors, with URDNA2015 additionally pinned by a KAT
  • The source-commitment form — the RFC 6962 Merkle tree hash over JCS-canonicalized source credentials and the source_root encoding
  • The DID verification-method read contracts — OKP/Ed25519 JWK and Multikey

Changing any of the above breaks proof compatibility with already-issued credentials and is a next-MAJOR change. The procedure is defined in GOVERNANCE.md.

Not frozen (separate contracts)

  • The tlog checkpoint SignedView
  • The chain-manager / payload-resolver wire-auth views
  • The DID-document JCS hash recorded into lifecycle logs

Each is pinned by its own golden tests, and compatibility-relevant changes are called out in the CHANGELOG. Being able to state what is not frozen is what makes the frozen scope credible.

This site’s claim that evidence remains verifiable after the original system is gone rests on this freeze being fixed by tests. Future verifiability cannot be demonstrated in advance — the grounds are that the frozen scope and its enforcement are public.

This boundary prevents “extensible” from being confused with “available now.” Evaluate a pinned commit in the intended environment before making a deployment decision.

Open the pinned commit on GitHub