How to verify
You do not have to trust GRRC. This page carries the complete method for checking a certificate yourself, on your own machine, from published files and standard cryptography.
- Two ways to check, and which one actually settles it.
- The full specification, complete enough to build your own verifier.
- How to mirror every input, so nothing depends on GRRC staying online.
GRRC Certification — Independent Verification Specification (v1)
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.
Two ways to check, and which one settles it
Three steps, from reading the claim to reproducing the proof yourself. The third step is the point: the hosted service is a convenience, and the offline method is the authoritative floor.
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 are a convenience over that offline floor, never the only path and never a gate.
Two hosts answer the same verification surface, on separate domains: verify.grrc.ai and verify.grrc.bot. Either returns the same result, and neither is required, because the offline method does not depend on GRRC being reachable at all.
Packaged verifier download: coming. This specification is already sufficient to build an independent verifier without it.
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).
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.
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).
Source — where this text comes from, and how to check it hasn't changed
This page is a byte-for-byte transcription of a document of record. The digest below is computed at build time over the exact bytes of that file, and the link fetches those same bytes so you can hash them yourself.
byte-verbatim — sha256 over the exact bytes of Phase34_Slice222_GRRC_IndependentVerificationSpec.md — source bytes (markdown, sha256-pinned) ↗ (14,627 bytes): 6d3cd35cf88dddcf4c4ba6a5109dc7fed940929aa30194a991eee7b7a275756b
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.
Status — which version of this document this is, and when it was published
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).