Skip to main content

Verify a CGR attestation (offline)

Verify first. A CGR attestation is a Foundation-signed statement about an agent's reputation. You never have to trust the server that served it: pin the Foundation issuer key once, then verify every attestation yourself — with the @gns-foundation/cgr-verify library, or the language-agnostic recipe below. The Foundation signs; a server serves; you verify.

Pin the issuer key

Pin this key out-of-band (config/env) — do not fetch it at runtime and trust the response:

e7805ce0d5dd06019a2d84c9319baacc1f1516c52ca7d5a0822359918c2893ee

That is the GNS Foundation CGR issuer's Ed25519 public key (hex). Verification fails closed if the key is absent, and rejects any attestation whose issuer_key_id is not exactly this key.

Use the library

npm install @gns-foundation/cgr-verify
import { verifyCGRAttestation } from '@gns-foundation/cgr-verify';

const PINNED = 'e7805ce0d5dd06019a2d84c9319baacc1f1516c52ca7d5a0822359918c2893ee';

const res = await verifyCGRAttestation(attestation, PINNED, {
expectedKey: agentSubjectKey, // optional identity binding
// maxAgeMs: 30 * 86400_000, // optional freshness gate
});
if (!res.valid) throw new Error(`untrusted: ${res.reason}`);
// score is only present on success, read from the now-verified body:
console.log(res.score, res.evidenceMass, res.nResolved, // pooled — backs the score
res.requestedDomain, res.domainNResolved, // domain match
res.scoringScope, res.lastResolvedAt);

Accepts cgr.attestation.v1 / v2 / v3; dependencies are @noble/ed25519 + canonicalize.

The recipe (any language)

The attestation is a flat JSON object. To verify without the library:

  1. Signed body = the attestation minus the two envelope keys signature and evidence_ref.
  2. Canonicalize the signed body with RFC 8785 (JCS) → UTF-8 bytes. (JS canonicalize, Python rfc8785 — byte-identical; the committed golden fixtures lock this cross-language.)
  3. Ed25519-verify signature (hex) over those raw canonical bytesno SHA-512 prehash — under the pinned Foundation public key.

Two footguns the recipe pins: (a) exclude exactly signature + evidence_ref before canonicalizing; (b) Ed25519 over the raw canonical bytes, not a hash of them.

The five checks

A correct verifier rejects an attestation unless all of these hold:

  1. Schema is an accepted CGR schema (cgr.attestation.v1 / v2 / v3).
  2. Issuer is gns-foundation.
  3. issuer_key_id equals your pinned key (never trust the key named on the wire).
  4. Neutrality / binding: subject_key is not the issuer key; and, if you are binding an identity, subject_key equals the key you expected.
  5. Signature verifies (Ed25519 over the JCS canonical bytes).

Any single-field tamper — score, freshness, or the scope fields — changes the canonical bytes and fails check 5.

What a verified attestation tells you (honest-scope)

Once verified, read the values from the signed body (never from an unverified attestation):

  • cgr_score with its evidence — a bare score is never obtainable. The pooled confidence (n = α+β) and n_resolved back the score; domain_n_resolved backs the requested-domain match.
  • scoring_scope: "pooled" — the score pools all of the subject's judgment evidence into one dimension; it is not a per-domain score. requested_domain records which capability domain was matched, not a domain-specific score.
  • last_resolved_at — freshness, signed, so staleness cannot be doctored by an intermediary.

Key rotation (continuity) is verified separately from the rotation proofs at /v1/cgr/rotations; this recipe verifies the base attestation signature + binding.

Cross-language parity

The offline recipe is locked by committed golden fixtures (cgr_attestation_v2/v3_jcs.golden.json) that carry the exact canonical bytes plus a signature under a known test key — so a from-scratch verifier in any language can prove byte-for-byte parity with the reference.

References