Public Key Registry
๐Ÿ”‘ This key is used to sign Enterprise compliance reports. To generate signed evidence packages for your AI agents, contact us at hello@cyphrex.io Let's talk. โ†’
ACTIVE

cyphrex-signer-prod-01

Ed25519 signing key ยท Active since 2026-05-06 ยท Public record

RAW PEM (TEXT/PLAIN)

This page is HTML. Verifiers must fetch the key from the PEM endpoint, not from this URL.

https://cyphrex.io/api/keys/cyphrex-signer-prod-01

Same bytes from the API: https://cyphrexapi-production.up.railway.app/v1/keys/cyphrex-signer-prod-01

KEY ID

cyphrex-signer-prod-01

ALGORITHM

Ed25519 (EdDSA)

STATUS

Active

ACTIVATED

2026-05-06

KEY NUMBER

KEY โ„–0001

NETWORK

Public record

PUBLIC KEY

Loading key...

VERIFY A REPORT

Fetch this key

curl https://cyphrex.io/api/keys/cyphrex-signer-prod-01

Build canonical payload + verify (Node.js, RFC 8785 JCS)

Packages are tagged canonicalization: "rfc8785-jcs". Canonicalize with an RFC 8785 library (npm canonicalize), not sorted-key JSON.stringify. Strip only sha256, signature, algorithm, and publicKeyUrl. Keep canonicalization and the full timestamp object in the signed core.

// =============================================================================
// Cyphrex signed report โ€” RFC 8785 JCS + verify (schemaVersion 1.0.0)
// =============================================================================
// Node.js 18+. npm install canonicalize@2
// report.canonicalization === "rfc8785-jcs"
//
// You must define:
//   const report = ...        // full object from POST /v1/events/export-signed
//   const publicKeyPem = `...` // PEM from GET https://cyphrex.io/api/keys/<keyId>
// =============================================================================

const canonicalize = require('canonicalize');
const { createHash, verify, createPublicKey } = require('crypto');

const STRIP = new Set(["sha256", "signature", "algorithm", "publicKeyUrl"]);

if (typeof report === 'undefined') throw new Error('Define `report`.');
if (typeof publicKeyPem === 'undefined') throw new Error('Define `publicKeyPem`.');

const core = Object.fromEntries(Object.entries(report).filter(([k]) => !STRIP.has(k)));
if (core.canonicalization !== "rfc8785-jcs") {
  throw new Error(`Unsupported canonicalization: ${core.canonicalization}`);
}

const canonical = canonicalize(core);

const publicKey = createPublicKey(publicKeyPem);
const isValid = verify(
  null,
  Buffer.from(canonical, 'utf8'),
  publicKey,
  Buffer.from(report.signature, 'base64')
);
if (!isValid) throw new Error("Invalid signature");

const computedHash = createHash('sha256').update(canonical, 'utf8').digest('hex');
if (computedHash !== report.sha256) throw new Error('Hash mismatch');

console.log('Valid signature');

Python (rfc8785 + cryptography only)

# pip install rfc8785 cryptography
# report.canonicalization == "rfc8785-jcs"
import base64, hashlib, json
import rfc8785
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.serialization import load_pem_public_key

STRIP = ("sha256", "signature", "algorithm", "publicKeyUrl")

report = json.loads(open("cyphrex-report.json", encoding="utf-8").read())
pem = open("cyphrex-public.pem", "rb").read()
if report.get("canonicalization") != "rfc8785-jcs":
    raise SystemExit("Unsupported canonicalization: " + str(report.get("canonicalization")))

core = {k: v for k, v in report.items() if k not in STRIP}
canonical = rfc8785.dumps(core)
if isinstance(canonical, str):
    canonical = canonical.encode("utf-8")

# Check order: Ed25519 first (always), then SHA-256 of the same JCS bytes.
key = load_pem_public_key(pem)
try:
    key.verify(base64.b64decode(report["signature"]), canonical)
except InvalidSignature:
    raise SystemExit("Invalid signature")

digest = hashlib.sha256(canonical).hexdigest()
if digest != report["sha256"]:
    raise SystemExit("Hash mismatch")
print("Valid signature")

Inclusion proof (membership in the on-chain root)

Signature check alone does not walk the Merkle path. Download verify_inclusion_independent.py (rfc8785 + cryptography only). It picks the first anchored: true event by default.

curl -fsS -o verify_inclusion_independent.py https://cyphrex.io/tools/verify_inclusion_independent.py
curl -fsS -o cyphrex-public.pem https://cyphrex.io/api/keys/cyphrex-signer-prod-01
python3 verify_inclusion_independent.py report.json cyphrex-public.pem

Verify only (Node.js)

// =============================================================================
// Cyphrex signed report โ€” verify only (schemaVersion 1.0.0, rfc8785-jcs)
// =============================================================================
// Node.js 18+. Built-in `crypto` only.
//
// You must define:
//   canonical   โ€” UTF-8 string from RFC 8785 JCS of the signed core
//                   (NOT JSON.stringify / sorted-key stringify)
//   signature   โ€” base64 string from report.signature
//   publicKeyPem โ€” PEM from this registry page
// =============================================================================

const { verify, createPublicKey } = require('crypto');

if (typeof canonical === 'undefined') throw new Error('Define `canonical` (see comments at top of this snippet).');
if (typeof signature === 'undefined') throw new Error('Define `signature` (see comments at top of this snippet).');
if (typeof publicKeyPem === 'undefined') throw new Error('Define `publicKeyPem` (see comments at top of this snippet).');

const publicKey = createPublicKey(publicKeyPem);
const isValid = verify(
  null, // null algorithm for Ed25519
  Buffer.from(canonical, 'utf8'),
  publicKey,
  Buffer.from(signature, 'base64')
);

console.log(isValid ? 'Valid signature' : 'Invalid signature');