Open standard · Apache-2.0 · RFC 3161

Sign and verify compliance evidence anyone can check.

Without trusting — or even contacting — whoever produced it. One JSON envelope binds a content hash, a signature, and an independent timestamp to any evidence artifact.

View on GitHub Read the spec
$npm install open-evidence-signing
npm open-evidence-signing
{
  "@context": "https://openevidence.dev/signing/v1",
  "version": "1.0",
  "issuer": {
    "id": "https://compliance.acme.example",
    "keyDiscovery": "…/.well-known/oes-signing"
  },
  "subject": {
    "type": "compliance-evidence:bundle",
    "contentHash": {
      "algorithm": "SHA-256",
      "value": "968cf4029649c428bb95df41be7e08df…"
    },
    "metadata": {
      "title": "Q1 Access Review",
      "framework": "SOC 2",
      "controlRef": "CC6.1"
    }
  },
  "proof": {
    "algorithm": "RSA-SHA256",
    "publicKeyFingerprint": "549f3b9895b9b973",
    "signatureValue": "VamHE4+fmVENH6c+rQrg…",
    "canonicalization": "sorted-keys-2space",
    "timestamp": {
      "type": "RFC3161",
      "authority": "http://timestamp.digicert.com",
      "timestampedAt": "2026-04-20T10:30:06Z"
    }
  }
}
$ npx oes evidence.oes.json --content evidence.zip \
    --keys issuer-discovery.json --offline

✓ VALID

   Content Hash — SHA-256 of evidence matches subject.contentHash
   Signature    — Verified with key 549f3b9895b9b973
   Timestamp    — RFC 3161 token binds signature at genTime
   Key standing — known, unrevoked, unexpired

  issuer:      https://compliance.acme.example
  signed at:   2026-04-20T10:30:05Z
  timestamped: 2026-04-20T10:30:06Z
  key:         549f3b9895b9b973
  content hash verified: true

exit 0
The problem

Evidence is only as trustworthy as the system that hands it to you.

Screenshots and exports are trivially altered. An auditor receiving a PDF or a ZIP has no way to tell whether it changed after collection.
Verification means logging into someone's platform. Proof depends on an account, an API key, or a relationship with the issuer that may not exist in five years.
Time is self-attested. "Collected on April 20" is a claim the collector makes about itself. Nothing stops backdating.
Every vendor has its own format. GRC platforms, scanners and CI pipelines each sign differently — if at all — so nothing composes across tools.
Who it's for

One format, three sides of the audit.

If you're an auditor
Verify without a relationship with the issuer.

Receive the evidence and its .oes.json, run one command, and record the result in your workpapers. The TSA timestamp says when; the published key says who.

$ npx oes ep_MFA-01.oes.json --content ep_MFA-01.zip --offline
If you're a compliance team
Evidence that outlives the tool that collected it.

Sign with your own key — in your KMS or HSM — so proof stays portable across vendors and audit cycles. Hand auditors a package they can check with no login and no vendor lock-in.

BYOK · Signer interface · /.well-known/oes-signing
If you're a GRC platform or pipeline
Ingest, verify, re-sign. Interoperate.

Verify on ingestion, show signed / timestamped / issuer in your UI, and wrap forwarded evidence in your own envelope with priorEnvelopes for a chain of custody. CI jobs sign scanner output with a service key.

npm i open-evidence-signing · exit 0 / 1 / 2 · --json
How it works

Four checks, in order. All of them offline after one key fetch.

A verifier fits in under 200 lines in any language with a standard crypto library. The reference verifier ships in TypeScript and Python.

01 · Integrity
Does the evidence still hash to what was signed? SHA-256 over the raw bytes. For bundles, a whole-ZIP hash plus a per-file hash in subject.files[].
02 · Authenticity
Was it signed by the claimed key? RSA-SHA256 or ECDSA-SHA256 over a canonical subject — sorted keys, 2-space indent, UTF-8.
03 · Time
When did the signature exist? An RFC 3161 token from an independent TSA (DigiCert, Sectigo, …) over the signature bytes. Legally recognized under eIDAS.
04 · Key standing
Is the key known, unrevoked, unexpired? Resolved from the issuer's /.well-known/oes-signing document, cached once, then verified offline forever.
Sign · Verify

Zero runtime dependencies. Keys that never leave your KMS.

The reference library uses Node.js built-ins only. Signing goes through a tiny Signer interface, so a private key in AWS KMS, Google Cloud KMS or an HSM never touches the library.

Envelopes for any artifact: screenshots, config exports, logs, scan results, ZIP bundles.
Bundles with a standard internal manifest and optional inner signatures for standalone integrity.
CI-friendly CLI: exit 0 valid, 1 failed, 2 usage. --json for pipelines.
Chain of custody via priorEnvelopes; optional export to W3C Verifiable Credentials.
sign.tsnpm i open-evidence-signing
import { signEnvelope, localSigner } from 'open-evidence-signing';

const signer = localSigner(readFileSync('signing-key.private.pem', 'utf-8'));

const envelope = await signEnvelope(signer, {
  content: readFileSync('evidence.zip'),
  type: 'compliance-evidence:bundle',
  metadata: { title: 'Q1 Access Review', framework: 'SOC 2', controlRef: 'CC6.1' },
  issuer: { id: 'https://compliance.acme.example', name: 'Acme Corp' },
  tsa: { urls: ['http://timestamp.digicert.com'] },  // optional RFC 3161
});

writeFileSync('evidence.oes.json', JSON.stringify(envelope, null, 2));
Ecosystem

Built to be reimplemented.

Everything needed to write a conformant issuer or verifier in your own stack is published, versioned, and proven byte-equivalent against production.

Adopting OES in your product or audit practice? Open an issue or email [email protected] — we list conformant implementations here.

Versions & roadmap

v1.0 is stable. v1.1 is open for input.

v1.0 · Stable · 2026-04-27
Envelope, canonicalization, key discovery, RFC 3161 binding, bundle manifest, verification algorithm. Implemented in production.
v1.1 · In discussion · backward compatible
Structured custody chain, sampling metadata, redaction transforms, multi-signer bundles, optional RFC 8785 (JCS) canonicalization mode.
Later minors
New algorithms (Ed25519, post-quantum) and evidence types. Verifiers must reject unknown algorithms rather than skip them.
Governance
LicenseApache-2.0 — spec, reference implementation, test vectors. MaintainerScreenata, released as a vendor-neutral open standard. Any issuer can produce; any auditor can verify. VersioningSemantic. Patch = clarifications. Minor = optional fields, new algorithms. Major = new @context URL. Extensionsx-* fields for producer data. Generic verifiers ignore them; unknown non-prefixed fields are rejected. Trust boundaryThe spec proves who signed and when. Which issuers and TSAs you trust is your policy — never the spec's. ProcessChanges via GitHub issues and pull requests. Vectors and spec update together; a parity test fails otherwise.
Read the seven open questions →
FAQ

Questions implementers ask first.

{{ f.a }}

Verify your first envelope in under a minute.

$ npx oes 01-plain-envelope/envelope.oes.json --keys issuer-discovery.json --offline