Joopler docs

Verifiable evidence

Every artifact is signed, timestamped, and hash-chained, and anyone can verify it.

Most compliance tools ask you to trust their dashboard. Joopler makes the evidence itself provable.

How it works

Every collected artifact is:

  • Hashed, so any change is detectable.
  • Signed, so its origin is provable.
  • Independently timestamped (RFC 3161), so its time is provable.
  • Hash-chained into a tamper-evident ledger, so records cannot be reordered or removed without detection.

The public verifier

Anyone you share evidence with can confirm an artifact is authentic and unaltered through the public verifier at /verify, without a Joopler account. Verification runs entirely in the browser with the Web Crypto API; the bundle and the key never leave the page.

Following a verify link from any signed record loads that record and its signing key and checks it for you. Otherwise, paste a bundle downloaded from the Evidence page and load the key by tenant.

The verifier checks three things:

  • the artifact hashes to its recorded SHA-256, so the content is unaltered;
  • the ECDSA P-256 signature over that digest is valid for the tenant's public key;
  • the ledger record hash binds the previous hash, the sequence and the content hash, so the record's place in the append-only chain is consistent.

Verifying without trusting us

Be precise about what a green result means. If you take the bundle, the public key and the verifier page all from Joopler, then every input came from one party. The cryptography is real and the check genuinely runs in your browser, but a result obtained that way says the evidence is internally consistent with a key we served you. That is worth having. It is not the same as verification that does not depend on us, and we would rather say so than let the distinction pass.

Two steps close that gap. Both are yours to perform, and neither needs our cooperation once you have started.

1. Pin the signing key. Fetch the tenant's public key once, at the start of an engagement, and keep your own copy:

# the key, and its fingerprint, which is the short form worth writing down
curl -s https://api.joopler.com/v1/public/signing-key/<tenant> | tee key.json | jq -r .publicKeyPem > joopler.pem
jq -r .fingerprintSha256 key.json

Every evidence bundle also carries signingKeyFingerprint, so each record states which key signed it and you can spot a change without fetching the key again.

The private half is an AWS KMS asymmetric key and is non-exportable, so it cannot be extracted by anyone, including us. What matters on your side is that the public key never changes without explanation. Compare it on every later check. A substituted key is the single thing that would let a forged bundle pass, and pinning is what makes that detectable.

2. Verify the signature with your own tools. You do not have to run our page. Download a bundle from the Evidence page. The signed message is the bundle's raw object rendered as canonical JSON: every object's keys sorted, no whitespace, no trailing newline. The signature is DER ECDSA (P-256) over the SHA-256 of those bytes.

# canonical JSON of the signed payload, with no trailing newline
jq -cSj .raw bundle.json > canonical.txt

# it must hash to the payloadHash the bundle records
openssl dgst -sha256 -r canonical.txt          # compare to: jq -r .payloadHash bundle.json

# and the signature must verify against the pinned key
jq -r .signature bundle.json | base64 -d > sig.der
openssl dgst -sha256 -verify joopler.pem -signature sig.der canonical.txt
# -> Verified OK

3. Check the record's place in the chain. The record hash binds the previous hash, the sequence and the payload hash, so a record cannot be reordered or silently dropped:

printf '%s|%s|%s' "$(jq -r .prevHash bundle.json)" \
  "$(jq -r .seq bundle.json)" "$(jq -r .payloadHash bundle.json)" \
  | openssl dgst -sha256 -r            # compare to: jq -r .recordHash bundle.json

4. Chain the timestamp to its root. Each bundle carries an RFC 3161 token from an external timestamp authority (DigiCert). Our verifier proves the token's message imprint is that artifact's digest, that the signed attributes bind the time to that imprint, and that the authority's signature verifies against the certificate embedded in the token. It deliberately does not chain that certificate chain. The chain embedded in the token is now validated link by link and must terminate at a root we pin (currently DigiCert Trusted Root G4), and the verifier names the root it anchored at. If it anchors at a root we do not pin, the token is reported as internally sound with an unpinned issuer rather than as invalid, because that is what a certificate rotation looks like before we catch up with it.

If you would rather not take our pinned set on faith, extract the token and validate it against DigiCert's root yourself with openssl ts -verify.

If you do those, a valid result no longer rests on our good behaviour. That is the standard we think an evidence product should be held to, including by you, about us.

Transparency anchors

The head of your ledger is periodically anchored: the head record hash is stored with an RFC 3161 timestamp from an external authority. A buyer or auditor can record your ledger head today and later confirm the chain still leads to exactly that value, so history cannot be quietly rewritten.

Two things to be accurate about. The anchor list is published by us rather than to a third-party log, so what makes an anchor hard to fake is the external timestamp on it, not the fact that we published it. And anchors are only reachable publicly when your Trust Center is public and its transparency surface is switched on; anchoring runs regardless, but until you publish it, the list is visible to you and your auditors rather than to anyone:

curl -s https://api.joopler.com/v1/public/transparency/<tenant>

OSCAL-native

The control catalog and your program export are OSCAL-native, so evidence and structure travel to auditors and government profiles in an open, standard format.

Verifiable evidence