Skip to content

feat: a salted transcript chain, evidence bundles and disclosure - #216

Merged
lomigmegard merged 3 commits into
mainfrom
worktree-transcript-format
Sep 27, 2026
Merged

lomigmegard merged 3 commits into
mainfrom
worktree-transcript-format

Conversation

@lomigmegard

Copy link
Copy Markdown
Contributor

The transcript opens with a header that records the run's inputs, and each fact is committed as a salted leaf in a SHA-256 chain, so the fingerprint survives redaction. Confidentiality levels are ordered integers named in the header, and each fact type has a default level: attestations are public, failed attempts, prompt answers and deviations restricted, persons and material digests confidential. Roles are declared once, and steps name their role by id. Transcript lines put the fields every line has first.

rite bundle create packages a run as an evidence bundle: the transcript, the ceremony definition and the artifacts, under an index. rite bundle disclose derives a disclosure at a level from a bundle; withheld lines keep their time, level and checkpoints, so the disclosure verifies to the same fingerprint. rite verify checks both kinds of bundle: a line withheld at or below the threshold fails, a line disclosed above it fails, a disclosure holding the ceremony definition fails, and the index must list every file the transcript records. A report of a disclosure says what it withholds. The transcript writer refuses a level its header does not declare, as the reader does. rite run ends with the commands that verify, report and bundle the run.

Before 1.0 the transcript format, the fact vocabulary and the bundle format are 0: they change between releases without a new number, the header's producer names the release, and rite verify warns when another release wrote the transcript.

A backend operation names the backend the step ran with in a field of its own; the backend's identity is on its BackendBound fact. Actions record operations through Reporter::backend_operation, which takes the step and the backend from the step being run.

The resolver refuses numbers outside the range canonical JSON records exactly (integers within 2^53 - 1).

JSON Schemas for the transcript and the bundle index are published under docs/schema, generated from the model types in tests, with descriptions of their own and every bound the format has. Each schema's $id is https://ritely.io/schemas//, and bundle.json and the transcript's first line name it as $schema.

The demo folder holds a bundle of one run, its public disclosure and a report of each. docs/transcript-format.md specifies the transcript for anyone writing a verifier, and docs/evidence-bundles.md describes bundles and disclosures.

The transcript opens with a header that records the run's inputs, and
each fact is committed as a salted leaf in a SHA-256 chain, so the
fingerprint survives redaction. Confidentiality levels are ordered
integers named in the header, and each fact type has a default level:
attestations are public, failed attempts, prompt answers and deviations
restricted, persons and material digests confidential. Roles are
declared once, and steps name their role by id. Transcript lines put
the fields every line has first.

rite bundle create packages a run as an evidence bundle: the
transcript, the ceremony definition and the artifacts, under an index.
rite bundle disclose derives a disclosure at a level from a bundle; withheld lines keep
their time, level and checkpoints, so the disclosure verifies to the
same fingerprint. rite verify checks both kinds of bundle: a line
withheld at or below the threshold fails, a line disclosed above it
fails, a disclosure holding the ceremony definition fails, and the
index must list every file the transcript records. A report of a
disclosure says what it withholds. The transcript writer refuses a
level its header does not declare, as the reader does. rite run ends with the
commands that verify, report and bundle the run.

Before 1.0 the transcript format, the fact vocabulary and the bundle
format are 0: they change between releases without a new number, the
header's producer names the release, and rite verify warns when
another release wrote the transcript.

A backend operation names the backend the step ran with in a field of
its own; the backend's identity is on its BackendBound fact. Actions
record operations through Reporter::backend_operation, which takes the
step and the backend from the step being run.

The resolver refuses numbers outside the range canonical JSON records
exactly (integers within 2^53 - 1).

JSON Schemas for the transcript and the bundle index are published
under docs/schema, generated from the model types in tests, with
descriptions of their own and every bound the format has. Each
schema's $id is https://ritely.io/schemas/<release>/, and bundle.json
and the transcript's first line name it as $schema.

The demo folder holds a bundle of one run, its public disclosure and a
report of each. docs/transcript-format.md specifies the transcript for
anyone writing a verifier, and docs/evidence-bundles.md describes
bundles and disclosures.
@lomigmegard lomigmegard self-assigned this Sep 26, 2026
Comment thread crates/rite-model/src/commitment.rs Dismissed
Comment thread crates/rite-model/src/commitment.rs Dismissed
Comment thread crates/rite-model/src/commitment.rs Dismissed
Comment thread crates/rite-model/src/commitment.rs Dismissed
Comment thread crates/rite-model/src/commitment.rs Dismissed
Comment thread crates/rite-model/src/commitment.rs Dismissed
Comment thread crates/rite-runtime/src/transcript_sink.rs Fixed
Comment thread crates/rite-runtime/src/transcript_sink.rs Fixed
Comment thread crates/rite-runtime/src/transcript_sink.rs Dismissed
They record through Reporter::backend_operation; the imports only compile under the piv and yubikey features, which the workspace lint does not enable.
rite-runtime draws from the operating system through os_random, and rite-openssl from OpenSSL through random_bytes. Each is the only place its crate starts a buffer as zeros and fills it. Transcript salts are decoded straight into their array.

The local checks in CONTRIBUTING.md are the CI lint and test commands, including the PIV and YubiKey features.
Comment thread crates/rite-runtime/src/os_random.rs Dismissed
@lomigmegard
lomigmegard merged commit 24ae790 into main Sep 27, 2026
11 checks passed
@lomigmegard
lomigmegard deleted the worktree-transcript-format branch September 27, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants