Document of record — transcribed, not authored
byte-verbatim — sha256 over the exact bytes of Phase34_Slice222_GRRC_IndependentVerificationSpec.md — source bytes (markdown, sha256-pinned) ↗ (14,627 bytes): 6d3cd35cf88dddcf4c4ba6a5109dc7fed940929aa30194a991eee7b7a275756b
The specification below is complete enough that a skeptic can write their own verifier and mirror every input on infrastructure they control (§7). The hosted endpoints at verify.grrc.ai are a convenience over this offline floor — never the only path and never a gate.
Packaged verifier download: coming — this specification is sufficient to build an independent verifier.
GRRC Certification — Independent Verification Specification (v1)
Status: Implementer specification — Phase 34 / Slice 222 (VERIFY-API-COMPLETE). Authored FROM the built resources (drift-resistant). This document defines, completely enough that a skeptic can write their own verifier, how an independent relying party confirms a GRRC robot certification without trusting — or even reaching — GRRC (the offline floor; trust-model (iv-2), the Tank Doctrine extended to verification).
Audience: an insurer's / reinsurer's technical reviewer, a standards body, or any relying party who wants to verify a robot's GRRC certification themselves and mirror the inputs so GRRC withholding can only ever show staleness, never forge validity.
Companions: the Bounded-Claim Statement (Phase34_Slice222_GRRC_BoundedClaimStatement.md — what verification asserts and explicitly does not, owner-authored (iv-1) verbatim) and the API Reference (Phase34_Slice222_GRRC_VerificationApiReference.md). The governance posture is owner-authored in RES_GRRC_TrustModel_v1.0.md (content v1.2); this is engineering only.
No operator/company name appears anywhere in this surface. The root of trust is a crypto-agile key-id + algorithm tag, never a name (CD #864). "GRRC" is an abstract role.
0 — What verification proves (and the two independent legs)
A complete verification runs two independent checks against two distinct keys, and is honest about what each proves:
- Genuineness — the clearance carries a certifier signature, verifiable against the published certifier anchor, that the robot cannot forge (the robot holds only the public anchor, never the certifier private key). This is the load-bearing guarantee against self-clearing: fixer ≠ certifier.
- Chain integrity — the robot's audit chain is intact and single-headed through a stated sequence: recompute SHA-256 over each entry's canonical encoding, confirm the
PrevChainHash → ChainHashlinkage, and verify each entry's robot audit-key signature.
Plus revocation currency (the signing key-id is still in the current signed trusted-anchor list) and a stamped-lifetime check.
Two exposure tiers (the (iv-3) privacy boundary, owner-authored, load-bearing):
- Public tier (unauthenticated): confirms cert status + genuineness + "chain intact and single-headed through sequence N" via a certifier-signed checkpoint — no enumerable per-event timeline is exposed. A layperson confirms it with signature checks alone.
- Authorized full-record tier (owner / authorized auditor): additionally carries the chain segment + the published robot audit public key, so the auditor runs the full independent chain-integrity walk and confirms the public checkpoint honestly reflects it. The chain segment operates over the encrypted envelope (ciphertext), never plaintext — verify ≠ decrypt; it is erasure-compatible by construction (a crypto-shredded payload stays unrecoverable while the chain still verifies).
1 — Cryptographic primitives
| Element | Value |
|---|---|
| Signature algorithm tag | ECDSA-P256-SHA256-IEEE-P1363 |
| Curve | NIST P-256 (secp256r1) |
| Hash | SHA-256 |
| Signature encoding | IEEE-P1363 fixed-field r‖s (64 bytes), not DER |
| Public key encoding | SubjectPublicKeyInfo (DER), base64 |
All signatures in this surface (the grant, the checkpoint, the anchor/revocation statement, and each audit-chain entry) use this one primitive. A verifier checks each with a standard ECDSA-P256 VerifyData/VerifyHash over the stated bytes, the IEEE-P1363 signature format.
2 — The resources (stable, versioned formats)
Each verification input is an independently-fetchable, cacheable, re-hostable resource carrying an explicit FormatVersion. A consumer MUST reject a resource whose FormatVersion it does not understand (fail-closed) — never best-effort parse it. The current versions:
| Resource | Format version | Media type |
|---|---|---|
| Verification bundle (offline artifact) | schemaVersion 1 |
application/vnd.grrc.verification-bundle.v1+json |
| Trusted-anchor set | 1 | application/vnd.grrc.trust-anchor-set.v1+json |
| Revocation / anchor-currency statement | 1 | application/vnd.grrc.revocation-statement.v1+json |
| Full-record artifact (authorized) | schemaVersion 1 |
application/vnd.grrc.full-record.v1+json |
2.1 — The verification bundle / full-record artifact
JSON object (the offline artifact). Public-tier bundles carry RobotAuditPublicKeySpkiBase64 = "" and ChainSegment = []; full-record artifacts populate both.
{
"SchemaVersion": 1,
"RobotUuid": "<guid>",
"GrantPayloadBase64": "<base64 of the grant payload — §3.1>",
"CheckpointPayloadBase64": "<base64 of the checkpoint payload — §3.2>",
"AnchorStatementPayloadBase64": "<base64 of the anchor/revocation statement payload — §3.3>",
"RobotAuditPublicKeySpkiBase64": "<base64 SPKI of the robot audit key, or empty>",
"ChainSegment": [ <entry>, ... ] // empty for the public tier
}
Each ChainSegment entry (encrypted envelope only — no plaintext field exists on the wire):
{
"Sequence": <int64>, "EntryId": "<guid>", "Domain": "<string>", "EventType": "<string>",
"DomainRecordId": "<guid>", "DecisionCycleId": "<guid|null>",
"OccurredAt": "<ISO-8601>", "CallerTimestamp": "<ISO-8601|null>",
"AccessTier": <int>, "SchemaVersion": <int>, "TenantId": "<guid>",
"PayloadBase64": "<base64 ciphertext envelope>",
"ChainHashBase64": "<base64 32 bytes>", "PrevChainHashBase64": "<base64 32 bytes>",
"SignatureBase64": "<base64 robot audit-key signature over ChainHash>"
}
2.2 — The trusted-anchor set
{ "FormatVersion": 1, "Anchors": [ { "KeyId": "<string>", "Algorithm": "ECDSA-P256-SHA256-IEEE-P1363", "PublicKeySpkiBase64": "<base64 SPKI>" } ] }
2.3 — The revocation / anchor-currency statement
{ "FormatVersion": 1, "AnchorStatementPayloadBase64": "<base64 of the anchor-statement payload — §3.3>" }
3 — The signed-content canonical encodings
Every signed payload follows the same two-layer shape: a signed-content canonical byte string (lex-sorted JSON keys, no whitespace) that the certifier signs SHA-256 of; and a payload envelope that stores those exact signed bytes alongside the base64 signature, so a verifier checks the signature over precisely the bytes that were signed (no canonicalization ambiguity). Envelope, for all three:
{ "schemaVersion": 1, "signature": "<base64 IEEE-P1363>", "signedContent": "<base64 of the signed-content bytes>" }
3.1 — Recertification grant (the genuineness leg)
Signed-content JSON, lex-sorted keys, no whitespace:
{"algorithm":"ECDSA-P256-SHA256-IEEE-P1363","expiresAtUtc":"<ISO-8601 O>","faultMarker":"<string>","issuedAtUtc":"<ISO-8601 O>","keyId":"<string>","robotUuid":"<guid D>","supersededDeCertCycleId":<int>,"supersededThroughCycleId":<int>}
The certifier signs SHA-256 of these bytes. faultMarker is the reasoning-integrity fault the clearance supersedes; supersededDeCertCycleId is the specific self-de-certification it clears.
3.2 — Cert-status checkpoint (the public-tier intactness claim)
Signed-content JSON, lex-sorted keys, no whitespace:
{"algorithm":"...","asOfUtc":"<ISO-8601 O>","certStatus":"<token>","headChainHashBase64":"<base64>","headSequence":<int64>,"keyId":"<string>","robotUuid":"<guid D>"}
It asserts "robot R's chain is single-headed and intact through sequence N (head hash H); cert status = {token}; under key-id K; as of {timestamp}." certStatus ∈ {valid,expired,revoked,self-de-certified,superseded} (§5).
3.3 — Trusted-anchor / revocation statement (currency)
Signed-content JSON, lex-sorted keys, no whitespace; trustedKeyIds sorted ordinal + deduped:
{"algorithm":"...","asOfUtc":"<ISO-8601 O>","signerKeyId":"<string>","trustedKeyIds":["<key-id>", ...]}
A clearance's key-id is current (not revoked) iff it appears in trustedKeyIds. Revocation is "pull the anchor" ((vi)): removing a key-id invalidates every clearance under it. GRRC withholding a fresher statement cannot forge currency — the last signed statement stands, and its asOfUtc makes staleness visible to the relying party, who weights it accordingly.
4 — The audit-chain canonical encoding (the chain-integrity leg)
For each ChainSegment entry, recompute ChainHash = SHA-256( canonicalEncoding(entry, prevChainHash) ). The encoding is length-prefixed and big-endian, portable across runtimes. The format version is selected by the entry's own stored SchemaVersion: 0 → v1, 1 → v2, ≥2 → v3. Sequence is NOT in the encoding (it is store-assigned). Byte layout (v3 shown; v2 omits the TenantId field; v1 omits both the schemaVersion stamp and TenantId):
0..3 format version, int32 big-endian (1 | 2 | 3)
[v2/v3] schemaVersion, int32 big-endian
[v3] TenantId, 16 bytes, .NET Guid.TryWriteBytes layout (MIXED-endian — first three
fields little-endian, final 8 bytes in declaration order; NOT RFC 4122)
EntryId, 16 bytes (same MIXED-endian Guid layout)
Domain length (int32 big-endian) + UTF-8 bytes
DomainRecordId, 16 bytes (Guid layout)
DecisionCycleId present-flag (1 byte: 0x00 absent / 0x01 present) + 16 bytes if present
OccurredAt: int64 big-endian UnixTimeMilliseconds + int16 big-endian UTC-offset minutes
AccessTier, int32 big-endian
Payload length (int32 big-endian) + payload bytes (the ciphertext envelope, verbatim)
PrevChainHash, 32 bytes (all-zeros for the scope genesis)
Guid byte order is the .NET
Guid.TryWriteBytesMIXED-endian layout, not RFC 4122. A non-.NET verifier must replicate it: the first three components (Data1 int32, Data2 int16, Data3 int16) are little-endian; the final 8 bytes are in declaration order.EventTypeandCallerTimestampare not hashed.
The walk is per scope and genesis-rooted: the first entry's PrevChainHash must be the 32 all-zeros genesis sentinel; each successor's PrevChainHash must equal its predecessor's recomputed ChainHash. Linkage is by PrevChainHash (gap-tolerant on Sequence, so legitimate identity-burn gaps do not false-fail; a deleted in-chain entry still breaks linkage). For each entry, also verify SignatureBase64 is a valid robot-audit-key signature over the 32-byte ChainHash (ECDSA-P256 VerifyHash, IEEE-P1363). The published robot audit public key is carried in the full-record artifact (RobotAuditPublicKeySpkiBase64).
5 — The verification algorithm, in order
Given the bundle (or full-record artifact), the published anchor set, and the verification time asOfUtc:
- Decode the three signed payloads (grant §3.1, checkpoint §3.2, anchor statement §3.3). A malformed artifact ⇒ result
indeterminate(fail closed). - Genuineness (the certifier signature + the four (vii) conditions):
- Resolve the certifier anchor for the grant's
keyId; confirm the algorithm tag matches. - Verify the grant's certifier signature over its signed-content bytes (§3.1) against the anchor's SPKI.
- Confirm the grant binds the same robot UUID as the artifact and has a well-formed lifetime.
- If any fails ⇒
genuineness-failed.
- Resolve the certifier anchor for the grant's
- Chain integrity:
- Authorized tier (segment present): run the per-scope walk (§4) over the segment; then confirm the walked head hash equals the checkpoint's
headChainHashBase64. If the walk fails or the heads disagree ⇒integrity-broken. - Public tier (no segment): confirm the checkpoint signature (§3.2) against the anchor and that it binds the same robot UUID; intactness rests on that certifier-signed claim through sequence N. If the checkpoint signature fails ⇒
integrity-broken.
- Authorized tier (segment present): run the per-scope walk (§4) over the segment; then confirm the walked head hash equals the checkpoint's
- Revocation currency: verify the anchor-statement signature (§3.3) against the anchor; confirm the grant's
keyId∈trustedKeyIds. Statement signature invalid ⇒ currency unprovable; key-id absent ⇒revoked. - Lifetime: if
asOfUtc > grant.expiresAtUtc⇒expired. - Determine the bounded status (first failing gate wins):
genuineness-failed→integrity-broken→revoked→expired→ an explicit certifier-statedself-de-certified/supersededfrom the trusted checkpoint → otherwisevalid.
OverallVerified is true iff the status is valid.
6 — The bounded result
CertStatus : valid | expired | revoked | self-de-certified | superseded
| genuineness-failed | integrity-broken | indeterminate
OverallVerified : bool
GenuinenessConfirmed : bool (+ GenuinenessKeyId — the certifier key-id)
ChainIntact : bool (+ IntactThroughSequence — authorized/offline tier only)
RevocationCurrent : bool
AsOfUtc : the verification time
DoesNotAssert : the owner-authored (iv-1) explicit-negative, VERBATIM (see the Bounded-Claim Statement)
Every result carries the explicit negative so "verified" cannot be read as a broader endorsement of the robot's conduct or the owner's rules. Read it before relying on the result.
7 — Independence, mirroring, and stability
- Offline floor (mandatory). Given {bundle + published anchor [+ revocation statement] [+ full-record artifact]} a relying party reaches the bounded result with no network and no GRRC dependency. A hosted endpoint is a courier for these materials, never the only path and never a gate.
- Mirror the inputs yourself. Fetch each resource (§2) and re-host it on infrastructure you control. Verify the freshest signed copy you hold. GRRC's availability or willingness never gates your verification; withholding only ever shows as staleness via the statement timestamps.
- Format stability. A GRRC clearance lives ~1 year. Each resource's
FormatVersionis bumped only on a breaking wire change; additive optional fields keep the version. Reject an unrecognized version rather than mis-parsing it.
End of the GRRC Independent Verification Specification (v1) — Phase 34 / Slice 222. The claim is not self-referential: this document is sufficient to build an independent verifier. The reference implementation is the offline verifier shipped with RES (Res.Core.GrrcVerification / the Res.Tools.GrrcVerify tool).