The record format, walked through
SRF is an open, Apache-2.0 specification for a decision record: one verifiable account of what an AI system saw, did, and who allowed it, keyed by the business subject it affected. The format is not the moat; it exists so any system can emit it and any auditor can verify it without trusting Stratl. This page is the implementer’s walkthrough; the normative text is spec/SPEC.md and the schema spec/srf-v0.1.schema.json in the backend repository.
The object
Section titled “The object”Decision├─ srf_version: "0.1"├─ decision_id: "DEC-" + ULID├─ subject { type, id, display? }├─ agent { id, name?, version?, owner? }├─ actors[] { kind: human|system|agent, id, identity_provider?, role? }├─ events[] (at least one; see below)├─ provenance { source, source_type, source_record_ids?, connector_version?, ingested_at? }├─ assurance: observed | attested | enforced├─ custody { storage: stratl|customer, key: stratl|customer, region?, key_id_url? }├─ retention? { policy_id?, hold_until?, legal_hold? }└─ integrity (added by the custodian; never part of the hash)subject, agent, each actor, custody and retention reject unknown members. Events accept extra type-specific members, which is how connectors carry facts such as truncated, token counts, or content: not_captured.
Event fields
Section titled “Event fields”Every event has seq (1-based, in order), type and at (RFC 3339 UTC, Z). Then, by type:
| type | fields |
|---|---|
input |
hash, ref? |
retrieval |
source, doc_refs[] |
model_call |
provider, model, version?, config_hash?, input_hash, output_hash, output_ref? |
tool_call |
name, args_hash, args_ref?, result_hash, result_ref?, status |
policy_eval |
policy_id, version, result, rule? |
approval |
required, requested_at?, approver?, identity_provider?, authority?, decision?, comment? |
action |
name, target, params_hash?, executed_at, result |
outcome |
status, adverse, category?, effect_on_subject? |
Hashes are sha256: followed by 64 lowercase hex characters. *_ref fields are opaque references a source may attach (a document id, a storage path); they are not fetched by Stratl.
The wire form, and why it matters
Section titled “The wire form, and why it matters”The wire form is the JSON exactly as transmitted: no null-valued members, no defaults injected by any implementation. Producers omit absent optional fields rather than send null; verifiers strip any null members before hashing. A field that is present is hashed; a field that is absent is not; implementations never add fields during validation.
This rule is what lets Python, TypeScript and Go agree. If one implementation filled in display: null and another omitted it, they would hash different bytes. The conformance fixtures under spec/fixtures/ pin this: each is a record and its expected hash, and make conformance runs all three implementations over all of them on every change.
Assurance and custody are declarations
Section titled “Assurance and custody are declarations”assurance says how the events were captured and is never upgraded by Stratl: an SDK or a tracer produces observed; Stratl’s own workflow endpoints produce enforced because the action could not have happened without them. custody says who holds storage and key. Both are visible in the portal, in the bundle and in the verifier’s output, and a verifier never collapses “signature valid” with “custody: customer”.
Writing a producer
Section titled “Writing a producer”The Python and TypeScript SDKs build conforming records, fingerprint content under shared rules, and submit with originals. Use them unless you have a reason not to. Python, TypeScript.
Build the object above, omit what you do not have, and POST it to /v1/ingest/srf with an ingest key. Requirements: a ULID-based decision_id with the DEC- prefix (any unique id is accepted, but ULIDs sort by time); seq starting at 1 with no gaps; RFC 3339 timestamps with Z; sha256: fingerprints computed over the exact bytes you would upload as originals. The response carries the record_hash the service computed; compare it with your own to confirm your canonicalisation matches. stratl inspect record.json prints the canonical form and hash of any record for the same purpose.
Versioning
Section titled “Versioning”srf_version is a string. Minor versions add optional fields only, and hash inputs never change within a major version. A verifier written for 0.1 will verify 0.x records.
Related formats
Section titled “Related formats”spec/MAPPING-AAT.md maps records from the IETF draft-sharif-agent-audit-trail format to SRF. The GenAI OpenTelemetry semantic conventions map through the OpenTelemetry connector.
Next: how integrity is computed.