# Cross-language parity matrix

Kind

reference

For

developer · governance

Verified against

@veritio/core@0.4.7

The Veritio event protocol is language-neutral, but the three SDKs that implement it are not equally complete. Some capabilities exist in all three and are pinned byte-for-byte by shared fixtures; some exist in one language only; and a few are present everywhere under different names or different call signatures. This page states exactly which is which at the pinned revision, so you can decide whether a workflow you prototyped in TypeScript can be ported without writing protocol code yourself.

## How to read the matrix

[Section titled “How to read the matrix”](#how-to-read-the-matrix)

Three separate questions get conflated when people ask “is this supported in Go?”. Keep them apart.

```text
1. Does the symbol exist?          → the capability matrix below
2. Is its output byte-identical?   → the conformance-fixture table
3. Does it have the same shape?    → the API-shape section
```

A capability can exist in all three languages and still be unpinned, which means nothing in CI would notice if one implementation drifted. It can also be pinned and still differ in call signature, return convention, or exported name — those differences are visible to your code but invisible to the hash.

## Capability matrix

[Section titled “Capability matrix”](#capability-matrix)

Capability

TypeScript

Python

Go

Pinned by fixture

Event creation

`createAuditEvent`

`create_audit_event`

`CreateAuditEvent`

yes

Canonical JSON

`canonicalJson`

`canonical_json`

`CanonicalJSON`

yes

Event and record hashing

`hashAuditEvent`, `hashAuditRecord`

`hash_audit_event`, `hash_audit_record`

`HashAuditEvent`, `HashAuditRecord`

yes

Idempotency-key hashing

`hashIdempotencyKey`

`hash_idempotency_key`

`HashIdempotencyKey`

yes

Metadata redaction

applied inside creation

applied inside creation

applied inside creation

yes

Audit templates

`templates.ts` builders

`templates.py` builders

`templates.go` builders

**no**

Governed change and action drafts

`createGovernedActionDraft`

`create_governed_action_draft`

`CreateGovernedActionDraft`

yes

Revision-id derivation

`governedRevisionId`

`governed_revision_id`

`GovernedRevisionID`

yes

Risk scoring

`scoreRiskSignals`

`score_risk_signals`

`ScoreRiskSignals`

yes

Episode rollup

`rollupEpisodeRisk`

`rollup_episode_risk`

`RollupEpisodeRisk`

yes

Risk policy derivation

`riskPolicy`

`risk_policy`

`RiskPolicy`

yes

`security.risk` assertions

`createSecurityRiskAssertion`

`create_security_risk_assertion`

`CreateSecurityRiskAssertion`

yes

Assertion hashing

`hashAssertionRecord`

`hash_assertion_record`

`HashAssertionRecord`

yes

Evidence edges

`createEvidenceEdge`

`create_evidence_edge`

`CreateEvidenceEdge`

yes

Evidence commits

`createEvidenceCommit`

`create_evidence_commit`

`CreateEvidenceCommit`

yes

Commit verifier

`verifyEvidenceCommits`

`verify_evidence_commits`

`VerifyEvidenceCommits`

yes

Audit-record verifier

`verifyAuditRecords`

none

none

TypeScript only

Edge-record verifier

`verifyEvidenceEdgeRecords`

none

none

TypeScript only

Provenance recorder

`createProvenanceRecorder`

none

none

TypeScript only

Export bundles (`vevb-1`)

`buildExportBundle`, `verifyExportBundle`

none

none

TypeScript only

Store implementations

`MemoryAuditStore`, `@veritio/storage`

none

none

TypeScript only

AI gateway

`@veritio/gateway`

none

none

TypeScript only

Everything in the last six rows is a TypeScript package boundary, not a protocol gap. A Python or Go service can still participate in a Veritio deployment by producing events and records and shipping them to a store that a TypeScript process owns — what it cannot do today is verify a chain, assemble an export bundle, or own authoritative ordering in-process.

## The verifier asymmetry

[Section titled “The verifier asymmetry”](#the-verifier-asymmetry)

This is the divergence most likely to bite. Veritio has three independent chains — audit records, evidence-edge records, and evidence commits — and only one of the three verifiers exists in all three languages.

```text
audit record chain   →  verifyAuditRecords          TS only
edge record chain    →  verifyEvidenceEdgeRecords   TS only
commit ledger        →  verifyEvidenceCommits       TS + Python + Go
```

`verifyEvidenceCommits` is deliberately the narrowest of the three. It proves the commit ledger’s internal consistency: per-stream sequence continuity, previous-hash linkage, member manifest normalization, the `veritio-merkle-v1` records root, and the commit hash. It does **not** reconcile member record hashes against independently verified records. A fabricated commit chain built over fabricated record hashes verifies clean in isolation.

The Go doc comment on `VerifyEvidenceCommits` tells you to compose it with `VerifyAuditRecords` and `VerifyEvidenceEdgeRecords` for end-to-end verification. Those functions do not exist in the Go module. The advice describes the correct protocol composition, not an available Go API. Until a Go or Python record verifier ships, end-to-end verification has to run in a TypeScript process, or in your own independent implementation of the record-chain rules described in [Hash chain](/docs/concepts/hash-chain/).

Treat a Go-side or Python-side `VerifyEvidenceCommits` pass as a statement about the ledger only. It is not evidence that the underlying records were unchanged.

## API-shape asymmetries

[Section titled “API-shape asymmetries”](#api-shape-asymmetries)

These do not change any hashed byte. They change the code you write.

```text
TypeScript  scoreRiskSignals(signals, policy = DEFAULT_RISK_POLICY): RiskAssessment
Python      score_risk_signals(signals, policy=DEFAULT_RISK_POLICY) -> dict
Go          ScoreRiskSignals(signals, policy) (RiskAssessment, error)
```

**Go requires the policy argument and returns an error.** There are no default parameters in Go, so `ScoreRiskSignals`, `RollupEpisodeRisk`, and `RiskPolicy` all take an explicit policy or options value, and all return `(value, error)` where TypeScript and Python throw or raise. Fail-closed behavior is identical; the control flow is not. A Go caller that ignores the error and prints the zero-valued assessment will document `score=0` for a signal set the SDK actually rejected — the opposite of the contract.

**Go exports only `BandOf` among the scoring primitives.** `clamp01`, `round4`, and `sat` are unexported in the Go package. If you need to reproduce a per-component contribution outside `ScoreRiskSignals`, you have to reimplement those three functions in your own code.

**Python’s `_sat` is private.** `clamp01` and `round4` are public module-level functions in `veritio.risk`, but `_sat` is underscore-prefixed and not part of the supported surface.

**Python does not re-export `clamp01` or `round4` from the package root.** They are absent from the `veritio` package’s `__all__`, so `from veritio import round4` fails while `from veritio.risk import round4` works. `band_of` is re-exported from the root.

**One template is named differently.** TypeScript exports `episodeStartedTemplate` and Go exports `EpisodeStartedTemplate`, but Python exports `activity_episode_started_template`. The emitted event is the same; a mechanical name translation will miss it.

**Go supplies a pointer helper for optional overrides.** `Float64` exists in the Go risk-policy module purely to build the optional-float fields that TypeScript and Python express with an absent key.

## What conformance fixtures pin

[Section titled “What conformance fixtures pin”](#what-conformance-fixtures-pin)

`spec/conformance` holds the shared JSON fixtures each SDK’s test suite loads and byte-compares against. At the pinned revision, eighteen fixtures are exercised by all three SDKs, covering canonical JSON, event and edge creation, redaction, every hashing surface, evidence commits, governed action drafts, revision-id derivation, risk normalization, default-policy scoring, episode rollup, frequency rules, policy temperature, and `security.risk` assertions.

Two fixture families are loaded by the TypeScript suite alone, because the behavior they pin exists only there: `provenance-ids.json` and the four `export-bundle-*.json` bundles.

The gap worth naming explicitly is **templates**. There is no template conformance fixture in any language. The builders exist in all three SDKs and their tests are written per-language, so nothing byte-compares the action strings, metadata defaults, or classifier stamping across TypeScript, Python, and Go. Templates are the most likely place for silent drift. If your deployment mixes languages and depends on templates producing identical events, pin the emitted events yourself in your own test suite. See [Template catalogue](/docs/reference/template-catalogue/) for the per-language builder list.

## Verified scoring parity

[Section titled “Verified scoring parity”](#verified-scoring-parity)

The risk parity fixtures score the same three pinned signal sets under `veritio.reference.v1` in Python and in Go, rendering every number through each language’s canonical-JSON writer so the printed digits are the digits a hash would see. Both outputs are byte-identical to each other and to the TypeScript twin.

Python — verified output

```text
policyVersion=veritio.reference.v1
bands={"critical":0.75,"high":0.5,"low":0.05,"medium":0.25}

scenario=read-config-lookup
  signals={"operationType":"read"}
  normalized={"dataVolume":0,"envCriticality":"production","fanOut":0,"operationType":"read","referenceCount":0,"reversibility":"recoverable"}
  score=0.05
  level=low
  policyVersion=veritio.reference.v1
  factors=operationType:0.05 dataVolume:0 fanOut:0 referenceCount:0 reversibility:1 envCriticality:1

scenario=bulk-export-staging
  signals={"dataVolume":5000,"envCriticality":"staging","fanOut":3,"operationType":"bulk","referenceCount":12,"reversibility":"reversible"}
  normalized={"dataVolume":5000,"envCriticality":"staging","fanOut":3,"operationType":"bulk","referenceCount":12,"reversibility":"reversible"}
  score=0.3718
  level=medium
  policyVersion=veritio.reference.v1
  factors=operationType:0.55 dataVolume:0.1961 fanOut:0.0129 referenceCount:0.0155 reversibility:0.6 envCriticality:0.8

scenario=destructive-drop-production
  signals={"dataVolume":250000,"envCriticality":"production","fanOut":40,"operationType":"destructive","referenceCount":180,"reversibility":"irreversible"}
  normalized={"dataVolume":250000,"envCriticality":"production","fanOut":40,"operationType":"destructive","referenceCount":180,"reversibility":"irreversible"}
  score=1
  level=critical
  policyVersion=veritio.reference.v1
  factors=operationType:0.85 dataVolume:0.1999 fanOut:0.0738 referenceCount:0.0626 reversibility:1.3 envCriticality:1
```

Go — verified output

```text
policyVersion=veritio.reference.v1
bands={"critical":0.75,"high":0.5,"low":0.05,"medium":0.25}

scenario=read-config-lookup
  signals={"operationType":"read"}
  normalized={"dataVolume":0,"envCriticality":"production","fanOut":0,"operationType":"read","referenceCount":0,"reversibility":"recoverable"}
  score=0.05
  level=low
  policyVersion=veritio.reference.v1
  factors=operationType:0.05 dataVolume:0 fanOut:0 referenceCount:0 reversibility:1 envCriticality:1

scenario=bulk-export-staging
  signals={"dataVolume":5000,"envCriticality":"staging","fanOut":3,"operationType":"bulk","referenceCount":12,"reversibility":"reversible"}
  normalized={"dataVolume":5000,"envCriticality":"staging","fanOut":3,"operationType":"bulk","referenceCount":12,"reversibility":"reversible"}
  score=0.3718
  level=medium
  policyVersion=veritio.reference.v1
  factors=operationType:0.55 dataVolume:0.1961 fanOut:0.0129 referenceCount:0.0155 reversibility:0.6 envCriticality:0.8

scenario=destructive-drop-production
  signals={"dataVolume":250000,"envCriticality":"production","fanOut":40,"operationType":"destructive","referenceCount":180,"reversibility":"irreversible"}
  normalized={"dataVolume":250000,"envCriticality":"production","fanOut":40,"operationType":"destructive","referenceCount":180,"reversibility":"irreversible"}
  score=1
  level=critical
  policyVersion=veritio.reference.v1
  factors=operationType:0.85 dataVolume:0.1999 fanOut:0.0738 referenceCount:0.0626 reversibility:1.3 envCriticality:1
```

The sparse first scenario matters: `{"operationType":"read"}` supplies none of the optional signals, so the printed `normalized` line shows the fail-closed defaults each language applies — `recoverable`, `production`, magnitude `0`. If one SDK’s defaults drifted, the score on that line would move and the two files would stop agreeing.

## Verified governed-draft parity

[Section titled “Verified governed-draft parity”](#verified-governed-draft-parity)

`create_governed_action_draft` and `CreateGovernedActionDraft` derive the same change id, activity id, revision id, tenant-scoped idempotency seed, changed paths, state commitment, event actions, and edge relations.

Python — verified output

```json
{
  "derivedIds": {
    "changeId": "chg_subscription_sub_9f31_312ca4bde590b545",
    "activityId": "act_subscription_sub_9f31_312ca4bde590b545",
    "entityId": "sub_9f31",
    "revisionId": "rev_subscription_sub_9f31_a71e2768812c_fe34f6d0",
    "idSeed": "312ca4bde590b545",
    "changeAndActivityShareOneSeed": true
  },
  "changedPaths": [
    "/plan",
    "/seatCount"
  ],
  "stateCommitment": {
    "algorithm": "sha256",
    "canonicalization": "veritio-json-v1",
    "schemaRef": "acme://schemas/subscription@3",
    "fieldSetRef": "acme://fieldsets/subscription-governed@1",
    "digest": "sha256:a71e2768812cca4e3609ce7e24460967d253e1ddab13d9a8898f4fd31a3564cb",
    "committedFields": [
      "accountEmail",
      "id",
      "plan",
      "seatCount",
      "status"
    ],
    "omittedFieldPresent": false,
    "accountEmailCommitment": {
      "captureMode": "content_digest",
      "digest": "sha256:aa69b0bc0b21a0ca7b5b5d1beb7556eb7d598f21d638db3eb9770ddd4fb30e45"
    }
  },
  "eventActions": [
    "change.declared",
    "activity.recorded",
    "entity.revision.created"
  ],
  "edgeRelations": [
    "has_activity",
    "has_output",
    "performed_by",
    "generated"
  ],
  "draftIsInputOnly": {
    "note": "create_governed_action_draft returns evidence INPUTS, not persisted records. Append draft['outboxEntry'] through a conforming AuditStore inside the same mutation to make it evidence.",
    "eventsCarryStoreAssignedSequence": false,
    "eventsCarryRecordHash": false,
    "outboxMutationBinding": "same_transaction",
    "outboxSchemaVersion": "2026-06-23"
  }
}
```

Go — verified output

```json
{
  "derivedIds": {
    "changeId": "chg_subscription_sub_9f31_312ca4bde590b545",
    "activityId": "act_subscription_sub_9f31_312ca4bde590b545",
    "entityId": "sub_9f31",
    "revisionId": "rev_subscription_sub_9f31_a71e2768812c_fe34f6d0",
    "idSeed": "312ca4bde590b545",
    "changeAndActivityShareOneSeed": true
  },
  "changedPaths": [
    "/plan",
    "/seatCount"
  ],
  "stateCommitment": {
    "algorithm": "sha256",
    "canonicalization": "veritio-json-v1",
    "schemaRef": "acme://schemas/subscription@3",
    "fieldSetRef": "acme://fieldsets/subscription-governed@1",
    "digest": "sha256:a71e2768812cca4e3609ce7e24460967d253e1ddab13d9a8898f4fd31a3564cb",
    "committedFields": [
      "accountEmail",
      "id",
      "plan",
      "seatCount",
      "status"
    ],
    "omittedFieldPresent": false,
    "accountEmailCommitment": {
      "captureMode": "content_digest",
      "digest": "sha256:aa69b0bc0b21a0ca7b5b5d1beb7556eb7d598f21d638db3eb9770ddd4fb30e45"
    }
  },
  "eventActions": [
    "change.declared",
    "activity.recorded",
    "entity.revision.created"
  ],
  "edgeRelations": [
    "has_activity",
    "has_output",
    "performed_by",
    "generated"
  ],
  "draftIsInputOnly": {
    "note": "CreateGovernedActionDraft returns evidence INPUTS, not persisted records. Append draft.OutboxEntry through a conforming AuditStore inside the same mutation to make it evidence.",
    "eventsCarryStoreAssignedSequence": false,
    "eventsCarryRecordHash": false,
    "outboxMutationBinding": "same_transaction",
    "outboxSchemaVersion": "2026-06-23"
  }
}
```

Every derived value matches. The only difference between the two files is the prose inside `draftIsInputOnly.note`, which names each language’s own call and field spelling. That note also carries the invariant both languages share: a draft is evidence **input**, not persisted evidence. Nothing in it carries a store-assigned sequence or a record hash until you append `outboxEntry` through a conforming store inside the same transaction, as described in [Governed changes](/docs/concepts/governed-changes/).

## Documented parity TODOs

[Section titled “Documented parity TODOs”](#documented-parity-todos)

Two recorder-level behaviors are normative contract but currently implemented in TypeScript only. They are recorded as parity obligations in the repo’s SDK parity rules, not as accidental gaps.

**The capture risk-signal classifier.** `bashRiskSignals`, `fileChangeRiskSignals`, and `envCriticalityOf` in the Claude Code adapter stamp `metadata.riskSignals` _before_ hashing, which makes the command-to-class mapping hash-affecting capture contract rather than an implementation detail. A Python or Go capture adapter must reproduce the patterns, the precedence order, and the unmatched-means-no-signal rule byte-identically. See [Risk signals](/docs/ai/risk-signals/).

**The `sessionId` and `activityEpisodeId` stamps.** Every event a provenance session emits carries `metadata.sessionId` and `metadata.activityEpisodeId`, applied _after_ caller-supplied metadata so a caller cannot shadow them. Read models depend on those keys to attribute downstream change, review, CI, deploy, and runtime events back to one session and one episode. Both keys are non-PII and must not match the redaction key pattern. Any future Python or Go provenance recorder has to apply the same post-merge stamping order.

## What this matrix does and does not prove

[Section titled “What this matrix does and does not prove”](#what-this-matrix-does-and-does-not-prove)

A row marked pinned means the three SDKs produced identical bytes for the fixture cases in CI at the pinned revision. It does not mean the implementations agree on inputs the fixtures do not cover, and it does not mean a capability behaves identically under error conditions — Go’s error returns are a separate surface from TypeScript’s thrown exceptions.

A row marked “TypeScript only” is a statement about `@veritio/core@0.4.7` and its sibling packages at this revision, not a permanent protocol boundary. Check the SDK reference for your language before assuming a gap still exists.

Continue with the [Python SDK reference](/docs/sdks/python/) or the [Go SDK reference](/docs/sdks/go/) for the per-language surface, or the [Verifier reference](/docs/reference/verifier/) to interpret each verification result you can actually obtain in your language.

[Edit page](https://github.com/getveritio/veritio-website/edit/main/src/content/docs/docs/reference/parity-matrix.mdx)

Last updated: Aug 23, 2026

[Previous  
Evidence vocabulary](/docs/reference/evidence-vocabulary/)[Next  
Verifier](/docs/reference/verifier/)

Veritio provides evidence support, not legal advice or automatic compliance.

This site uses cookieless, anonymous analytics (Umami) by default. With your consent, we also enable Google Analytics, which sets cookies and sends usage data to Google. [Privacy Policy](/legal/privacy/)
