Skip to main content

Record

Struct Record 

Source
pub struct Record {
    pub body: RecordBody,
    pub prev_hash: Digest,
    pub hash: Digest,
    pub signature: Option<KeySignature>,
    /* private fields */
}
Expand description

A sealed journal entry: body, chain links, and the bytes that were hashed.

Fields§

§body: RecordBody§prev_hash: Digest§hash: Digest§signature: Option<KeySignature>

Who wrote it, if the plane was configured to say.

Beside the hash rather than inside the body, and that placement is forced: the signature covers the chain hash, so putting it in the body would make the hash cover the signature that covers the hash.

None is an ordinary state, not a defect — a plane that has not been given a Signer writes unsigned records, and history written before signing was configured stays unsigned forever. What must never happen is a verifier silently accepting that; see Record::verify_signed.

Implementations§

Source§

impl Record

Source

pub const MAX_RECORD_BYTES: usize

The largest a single journal record may be.

A megabyte is generous for a record describing an effect and far too small for an inlined image, which is the intent: media belongs outside a chain that can never forget it. The number is in the same range the field settled on — Temporal caps payloads at 2 MB and claim-checks above 256 KiB — and is deliberately a hard refusal rather than a truncation, because a silently shortened record is a journal that lies.

Enforced in the step Record::seal_signed and Record::seal_at both end in, so no store can be added that quietly skips it.

Source

pub const MAX_RECORD_DEPTH: usize = 100

The deepest a record’s bytes may nest arrays and objects.

Every reader of a record has a nesting limit, and a record past it is one the chain committed to and no reader can parse: this crate’s (serde_json, 127 levels) on every read and every restore, and the second reader’s (Python’s recursion limit, around 1000). The bound sits under the lowest of them with room for the levels an export line wraps a record in, so a record that seals is a record every reader opens.

Enforced where Record::MAX_RECORD_BYTES is, and for the same reason: refusing at seal is the only moment it is still cheap.

Source

pub fn seal(body: RecordBody, prev_hash: Digest) -> Result<Self, StoreError>

Serialize canonically and link into the chain.

Source

pub fn seal_signed( body: RecordBody, prev_hash: Digest, signer: Option<&dyn Signer>, ) -> Result<Self, StoreError>

Seal, and attest it as the given signer.

The signature is taken over the chain hash, which already covers prev_hash ‖ canonical(body). Because the hash chains, this signature transitively commits to every record before this one — so rewriting any part of the prefix invalidates every later signature, not just its own.

Source

pub fn seal_at( body: RecordBody, written: Option<Vec<u8>>, prev_hash: Digest, signer: Option<&dyn Signer>, ) -> Result<Self, StoreError>

Seal at the position a store assigned, keeping the written bytes when the append carries them.

With no written bytes this is Record::seal_signed. With them — a restore — the bytes are hashed against prev_hash as they stand and the body is the store’s index view of them, so they must name the position the store assigned: bytes claiming another seq, epoch or run would land a record under a chain position its own body contradicts.

§Errors

StoreError::Corrupt for written bytes that name another position, StoreError::RecordTooLarge above the limit, and whatever Record::seal_signed refuses.

Source

pub fn from_stored_signed( raw: Vec<u8>, prev_hash: Digest, hash: Digest, signature: Option<KeySignature>, ) -> Result<Self, StoreError>

Reconstruct from storage, verifying the link before trusting the content, and carrying whatever signature the store kept.

The body is decoded from raw; the hash is recomputed from raw. A record whose stored hash disagrees is rejected rather than returned with a warning — a journal you cannot trust is worse than no journal, because it produces an audit trail that is quietly a lie.

Reads under the Identity upcaster — this build’s shapes and no others. A store that carries a real upcaster calls from_stored_with instead.

Source

pub fn from_stored_with( upcaster: &dyn Upcaster, raw: Vec<u8>, prev_hash: Digest, hash: Digest, signature: Option<KeySignature>, ) -> Result<Self, StoreError>

Reconstruct, lifting the record forward if it was written at an older shape.

The version is checked on every read, not only when something looks wrong. A record carries v, and a reader that writes it and never reads it back has a version field for decoration: a journal written by a build one shape ahead parses cleanly here, with the fields this build has never heard of dropped on the floor, and every decision downstream is then made over a record nobody fully read. So the version is the first thing asked about the parsed body, and the answer comes from the Upcaster rather than from a constant — which is what makes the seam a live path rather than a declaration waiting for its first migration to also be its first exercise.

The hash stays over the bytes that were written. An upcast produces a body this build understands and leaves raw and hash alone, so tamper evidence is unaffected by the reader’s age — the rule Upcaster states, enforced here by construction because the lift happens after the link is verified and never touches the bytes.

§Errors

StoreError::Corrupt if the stored hash does not cover the bytes, StoreError::UnknownRecordVersion if no upcaster can reach this build’s shape from the one on the record, StoreError::UnreadableRecordShape if the record is at the version this build writes and still does not parse — the skew a hard cut produces — and StoreError::Encoding if the bytes are not a record at all.

Source

pub fn raw(&self) -> &[u8] ⓘ

The exact bytes covered by Self::hash.

Source

pub fn seq(&self) -> Seq

Source

pub fn kind(&self) -> &RecordKind

Source

pub fn effect_key(&self) -> Option<EffectKey>

Source

pub fn admission_source(&self) -> Option<&str>

The producer whose origin_key admitted this run — the key’s source half — when this is the admission record and its key was built that way.

What a served surface asks before it reads or acts on a run it is addressed by id: the source is the authenticated sender the surface keyed the admission with, so it answers “whose run is this” from a field the run already carries.

Source

pub fn admitted_input(&self) -> Option<Tainted<Value>>

The input a run was admitted with, label and field labels and all — what the run’s steps read, live and in replay. None for every other record.

Source

pub fn verify_chain( records: &[Self], from: Digest, ) -> Result<Digest, StoreError>

Verify a contiguous run of records links correctly.

Checks both the chain and the sequence: a gap means records were deleted, which the per-record hash alone would not catch.

Source

pub fn verify_signed( records: &[Self], from: Digest, verifier: &dyn Verifier, require_signature: bool, ) -> Result<Digest, StoreError>

Verify the chain and that a known key signed every record.

Two separate questions, deliberately answered by two separate calls. The chain says the records are consistent with each other; the signatures say who wrote them. A caller that only runs Self::verify_chain is asking the weaker question, and the runtime does exactly that when it replays a run (a store’s read checks less still: each record’s hash against its own bytes, not the links between records) — because a plane without a configured verifier has no basis to reject anything, and failing closed there would make signing impossible to adopt incrementally.

require_signature is what stops that leniency becoming a hole. With it set, an unsigned record is a failure rather than a shrug — which is the posture an auditor wants, and the opposite of the one a plane resuming its own history wants.

§Errors

StoreError::Corrupt if the chain is broken, or SignatureError as a corrupt-record detail if a signature is missing or wrong.

Trait Implementations§

Source§

impl Clone for Record

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Record

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Record

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Record

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

impl<T> MaybeSend for T
where T: Send,

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more