docs.vcisolite.com · Methodology
Auditors, regulators and acquirers rely on records vCISO Lite keeps: who read which file, what an auditor decided, when a report was issued. This page sets out how those records are sealed, what each seal proves and what it doesn't, and how to check them yourself. The checks are built so you don't have to trust us to run them.
Written 2026-10-07. Every command on this page was run before publication; the footer says against what. Where something isn't available yet, the status section says so.
Audit evidence usually travels as files and screenshots. Everyone who touches it has to take someone's word that it's the same thing that was collected, and that the trail behind each decision is the one that happened. Sealing replaces that trust with a check anyone can run.
A record on its own proves nothing; anyone can write a line in a database. Each layer below commits to the one beneath it and is checkable by a wider audience. The last layer is checkable by anyone with a network connection, and depends on a party that isn't us.
| Layer | Proves | Doesn't prove |
|---|---|---|
| Record | This record's contents are the ones that were fingerprinted, and vCISO Lite's key signed that fingerprint. | That what the record says is true. A record of "file read" proves the read was recorded, not that the file was the right evidence. |
| Chain | No record was deleted, inserted or reordered without breaking every link after it. | That the whole chain wasn't rewritten from some point onward, links and all. |
| Checkpoint | A batch of records existed in this exact form by the time an outside authority stamped it. | Anything about records written after the checkpoint. |
| Public log | We've only ever appended checkpoints, and everyone was shown the same history. | What any single checkpoint contains. The log publishes commitments, never customer content. |
Sealed isn't the same as true. Every check here proves a record wasn't changed after it was written. None of them prove the record was right when it was written. That's the auditor's job, and nothing on this page replaces it.
Every event vCISO Lite records is an entry on a chain. Each organization's records are kept on separate chains per product, so tampering with one can't invalidate another.
Each entry's entry_hash is SHA-256 over a serialization of these fields, in this order:
Because previous_hash is inside the fingerprint, each entry commits to every entry before it.
The fingerprint is SHA-256 over one JSON object, written byte for byte by these rules:
"name":value. Leave out event_subtype when it's empty.
sequence_number is a bare integer; every other field except details is a string.\" \\ \b \f
\n \r \t; any other character below U+0020 as \u00xx (lowercase hex);
< > & as \u003c \u003e \u0026;
U+2028 and U+2029 as \u2028 \u2029.details is the event's own JSON. Object keys are sorted by UTF-8 byte length, then byte by
byte, at every depth; arrays keep their order. Numbers are plain decimals that keep their written scale
(1.50 stays 1.50), with no exponent (1e-7 becomes 0.0000001) and no
negative zero.timestamp is UTC to the microsecond, as 2026-10-07T18:04:05.12Z: trailing zeros of
the fraction are dropped, and the fraction is dropped entirely when it's zero.entry_fingerprint.py implements these rules with the Python standard library alone, and entry_vectors.json holds test vectors produced by our production hashing code: each gives a record's fields, the exact bytes and the fingerprint. The script reproduces all of them. If yours doesn't, the rules above are wrong, and we want to know.
Entries before May 24, 2026, 15:19 EDT carry no content fingerprint. An earlier serialization didn't survive database storage, so their fingerprints can't be recomputed from their contents. Their deletion, insertion or reordering is still detected through the chain links, but an edit to their contents wouldn't be. Every entry since then, including every vCISO Lite for Auditors entry, carries a full content fingerprint.
Each entry is signed with Ed25519 (RFC 8032). The signed message is exactly:
{chain_id} is the chain identifier that comes with the record. The prefix stops a
signature from being replayed against any other value we sign; the chain identifier stops it being
replayed against an identical entry on another chain. Each signature records the
ID of the key that made it. Signing never blocks a write: if the key is unavailable the entry is
stored unsigned and reported as unsigned, never as valid.
Each chain's new entries are gathered into a checkpoint. A checkpoint records its sequence
range, entry count and a Merkle root over those entries' fingerprints.
It is chained to the checkpoint before it (previous_checkpoint_hash) and summarized as
its own checkpoint_hash.
How often. Every chain with new entries gets a checkpoint at 00:00 and 12:00 UTC, and sooner if it reaches 10,000 entries. A chain with nothing new gets none. A new record is in a checkpoint, and that checkpoint in a published and timestamped log head, within 24 hours. Before October 7, 2026 a checkpoint waited for 10,000 entries, so slow-growing chains could wait much longer.
The Merkle root is then sent to an outside Time-Stamp Authority (RFC 3161; currently DigiCert,
http://timestamp.digicert.com). The authority signs the root and the time it saw it.
Its signature chains to a public root certificate we don't control, so neither we nor anyone else
can backdate it. The timestamp's message imprint is the 32-byte root itself.
A checkpoint states which construction built its root (tree_epoch). Always use the one it names:
SHA-256(0x00 || leaf), node hash SHA-256(0x01 || left || right), split at the
largest power of two below the width. Checkpoints made from October 7, 2026 use it, as the public log
always has. Older checkpoints stay on epoch 1, because their roots are already timestamped.Any one entry can be proven to sit under a checkpoint's root with an inclusion proof: the sibling hashes along its path. You don't need the rest of the checkpoint's entries.
Every checkpoint's checkpoint_hash is appended as a leaf to a single public transparency
log spanning every organization (RFC 6962). Each time checkpoints are added, the log publishes
a new head: the tree size and root. Each head is timestamped by the same outside authority.
Heads are immutable once published.
Leaves are opaque per-checkpoint commitments and are never published. A consistency proof is
served only when it reveals no individual leaf. A pair of even sizes is always served, so keep
heads with even tree_size.
The log's strength comes from time and other people. A head you saved last month and a head you fetch today must be consistent; if we'd rewritten history, they couldn't be. A check run once and thrown away proves nothing tomorrow. Keep the heads you're given.
You need curl, openssl (1.1 or later) and Python 3. Nothing to install, no account.
external_anchor is a base64 RFC 3161 time-stamp response. Decode it, then have OpenSSL check
it against the head's root and your system's trusted certificates:
Expected: Verification: OK. Change one character of the digest and it says
Verification: FAILED. This proves the root existed by the stamped time, signed by an
authority that isn't us.
Download verify_consistency.py (Python standard library only, 84 lines; read it before you run it). Give it the size of a head you kept. It fetches the proof from that head to the latest and checks it with the RFC 9162 §2.1.4.2 algorithm:
It also re-fetches each head on its own and confirms it matches the head the proof was served with. A rewritten history fails here; a proof that stopped short fails here; a tampered element fails here.
Open the report's code at
verify.vcisolite.com and drop the PDF on the page. The fingerprint is computed in your
browser; the file isn't uploaded. Or compute it yourself and compare it with the issued
fingerprint the page shows:
An organization, and the auditors it grants access, can request a verifiable export of a slice of its records through our API. The export carries each entry, an inclusion proof for each entry already in a checkpoint, and the checkpoints with their timestamps. Offline, for each entry:
tree_epoch names.checkpoint_hash is in the public log: a checkpoint receipt carries the
RFC 6962 inclusion proof to a published head. Then compare that head with the one you fetch from the log.Entries newer than the latest checkpoint appear in the export, marked as not yet provable.
Put a record's fields in a file by name (an entry from the export in E has them), then rebuild the bytes and the fingerprint. The vectors run first, so you know the script agrees with us before you trust its answer:
The printed SHA-256 must equal the record's entry_hash.
A seal's page offers its receipt: verify.vcisolite.com/seal/{ref}/receipt.json.
verify_receipt.py (standard library only) checks the signature
against the published key, the record's path to its checkpoint, the checkpoint's own hash, its place in
the public log and the head it sits under. It writes each timestamp to a file and prints the
openssl ts -verify command that checks the authority's signature. If you downloaded the
sealed record itself, pass it too, and its fingerprint is checked:
Publishing verification material shouldn't publish anyone's business. Here is what each public endpoint discloses, and what it doesn't:
Report and seal lookups, portal sign-in requests and the public log are rate-limited per IP address at our network edge.
| Check | Who | Status |
|---|---|---|
| Log heads, outside timestamps, consistency proofs (B, C) | Anyone | Live |
| Verifiable export with inclusion proofs (E) | The organization and its auditors, through our API | Live |
| A checkpoint within 24 hours of every record | Everyone who relies on a record | Live |
| Issued report fingerprint at verify.vcisolite.com (D) | Anyone with the report and its code | Live |
| Seal page: every check runs in your browser, with a receipt you can re-check offline (G) | Anyone with the seal link | Live |
| Public signing key at verify.vcisolite.com/.well-known/keys | Anyone | Live |
| Checkpoints built with RFC 6962 (epoch 2), like the public log, from October 7, 2026 | Anyone checking a checkpoint | Live |
| Byte-exact recompute rules and test vectors for record fingerprints (F) | Anyone with a record | Live |
This page will be updated as each of these ships. If something here doesn't work as described, that's a defect: tell us at support@vcisolite.com.