Skip to main content

JsonlLog

Struct JsonlLog 

Source
pub struct JsonlLog { /* private fields */ }
Expand description

Append-only JSONL log handle.

Owns the file path and an in-memory copy of the current chain head. The file handle is opened per write to keep the type Send + Sync and to make file-rotation / handoff straightforward (if we ever need it).

Per Lane 3.D.6 / ADR 0010 §1-§2 the log also tracks the previous row’s Ed25519 signature bytes so the next signed append can build the canonical preimage (which couples S_n to S_{n-1}).

Implementations§

Source§

impl JsonlLog

Source

pub fn open(path: impl AsRef<Path>) -> Result<Self, JsonlError>

Open (or create) the log at path.

On open we scan the existing file to recover the head hash and row count. An empty file (or non-existent path) is a fresh log with no head.

Source

pub fn path(&self) -> &Path

Path of the underlying file.

Source

pub fn len(&self) -> u64

Number of rows currently in the log.

Source

pub fn is_empty(&self) -> bool

Whether the log has zero rows.

Source

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

Current chain head (event_hash of the most recent row).

None for an empty log.

Source

pub fn append( &mut self, event: Event, policy: &PolicyDecision, ) -> Result<String, JsonlError>

Append one event to the log without an Ed25519 signature, gated through the ADR 0026 enforcement lattice.

This is the legacy / pre-3.D.6 path. Rows written via this method fail crate::audit::verify_signed_chain with crate::audit::FailureReason::MissingSignature (per ADR 0010 §1 “Single asymmetric trust domain”: rows without a valid Ed25519 signature do not verify; there is no symmetric-MAC fallback). It is retained for the local-development ledger path and for tests of non-attestation features (hash chain, payload framing); trusted-history / authority paths MUST use Self::append_signed.

policy is the composed PolicyDecision for this append and MUST satisfy:

  1. The composition includes contributors for APPEND_EVENT_SOURCE_TIER_GATE_RULE_ID, APPEND_ATTESTATION_REQUIRED_RULE_ID, and APPEND_RUNTIME_MODE_RULE_ID. Callers that skipped composition are refused.
  2. When event.source is EventSource::User, the attestation contributor MUST vote PolicyOutcome::Allow regardless of final outcome. ADR 0026 §4 forbids BreakGlass from substituting for the attestation requirement at the user-event authority boundary.
  3. A final outcome of PolicyOutcome::Reject or PolicyOutcome::Quarantine fails closed and writes nothing.
Source

pub fn append_signed( &mut self, event: Event, attestor: &dyn Attestor, policy: &PolicyDecision, ) -> Result<String, JsonlError>

Append one event with an Ed25519 signature over the canonical attestation preimage (T-3.D.6, ADR 0010 §1-§2), gated through the ADR 0026 enforcement lattice.

The preimage couples this row’s signature to S_{n-1} (the previous row’s signature, or the genesis sentinel for the first row), the row’s event_id, payload_hash, session_id, ledger_id, and the signing key’s key_id. See row_preimage for the exact shape.

policy is the composed PolicyDecision for this signed append and MUST satisfy:

  1. The composition includes contributors for APPEND_SIGNED_KEY_STATE_CURRENT_USE_RULE_ID (ADR 0023 current-use revalidation: historical-only or revoked signing keys vote Reject) and APPEND_SIGNED_TRUST_TIER_MINIMUM_RULE_ID (ADR 0019: the principal MUST sit at or above Verified). Callers that skipped composition are refused.
  2. The key-state contributor MUST vote PolicyOutcome::Allow regardless of final outcome. ADR 0026 §4 forbids BreakGlass from substituting for current-use revalidation at the signed ledger root.
  3. A final outcome of PolicyOutcome::Reject or PolicyOutcome::Quarantine fails closed and writes nothing.

Returns the new head hash on success.

Source

pub fn append_schema_migration_v1_to_v2( &mut self, payload: SchemaMigrationV1ToV2Payload, policy: &PolicyDecision, ) -> Result<String, JsonlError>

Append the schema v1 -> v2 boundary event after the current v1 head, gated through the ADR 0026 enforcement lattice.

This helper is intentionally narrow: it validates the typed payload and refuses to append unless the log’s current head matches payload.previous_v1_head_hash. It does not run the full migration or bump cortex_core::SCHEMA_VERSION.

policy is the composed PolicyDecision for the v1 -> v2 boundary append (ADR 0026 punch list #17). See Self::append_schema_migration_v1_to_v2_with_event for the preflight contract.

Source

pub fn append_schema_migration_v1_to_v2_with_event( &mut self, payload: SchemaMigrationV1ToV2Payload, policy: &PolicyDecision, ) -> Result<(String, Event), JsonlError>

Schema v1 -> v2 boundary append variant that also returns the sealed Event written to the log, gated through the ADR 0026 enforcement lattice.

Callers that mirror the boundary row into a side store (e.g. the SQLite events table during cortex migrate v2 cutover) need the sealed event in hand. The JSONL row carries the canonical hash chain; the returned event is byte-identical to what was just persisted (its prev_event_hash and event_hash reflect the head couple).

policy is the composed PolicyDecision for the v1 -> v2 boundary append and MUST satisfy:

  1. The composition includes contributors for SCHEMA_MIGRATION_AUTHORITY_CLASS_RULE_ID (ADR 0019 §3: the proposing principal sits in the Operator authority class — a non-operator key cannot mint a v2 boundary), SCHEMA_MIGRATION_ATTESTATION_REQUIRED_RULE_ID (ADR 0010 §1-§2: a fresh operator attestation is supplied over the boundary payload), and SCHEMA_MIGRATION_CURRENT_USE_TEMPORAL_AUTHORITY_RULE_ID (ADR 0023 §2 / §5: the signing key state at attestation time is Active, not Retired or Revoked). Callers that skipped composition are refused.
  2. Per ADR 0026 §4 (hard wall), the attestation contributor and the current-use temporal-authority contributor MUST each vote PolicyOutcome::Allow regardless of final outcome. A BreakGlass decision MUST NOT substitute for either contributor at the migration authority root.
  3. A final outcome of PolicyOutcome::Reject or PolicyOutcome::Quarantine fails closed and writes nothing.
Source

pub fn last_sig_prefix(&self) -> [u8; 32]

32-byte prefix of the most recently appended row’s signature, or the genesis sentinel if no signed row has been appended yet. Exposed so callers (audit verifier, tests) can reproduce the chain coupling.

Source

pub fn iter(&self) -> Result<JsonlIter, JsonlError>

Iterate every row in the log in append order.

Each item is the parsed Event (the inner semantic event of the signed envelope). For access to the row signature use Self::iter_signed. Decode errors short-circuit the iterator and surface as Err.

Source

pub fn iter_signed(&self) -> Result<SignedJsonlIter, JsonlError>

Iterate every row in the log as SignedRow envelopes — i.e. the Event plus its optional RowSignature.

Used by crate::audit::verify_signed_chain to reconstruct the per-row attestation preimage. Decode errors surface as Err and short-circuit.

Source

pub fn verify_chain(&self) -> Result<(), JsonlError>

Verify the chain end-to-end.

Walks every row, recomputes both payload_hash and event_hash under the canonical framing, and confirms each row’s prev_event_hash matches the previous row’s event_hash. Returns Ok(()) on success or JsonlError::ChainBroken with a short summary on failure. For per-row diagnostics use crate::audit::verify_chain.

Trait Implementations§

Source§

impl Debug for JsonlLog

Source§

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

Formats the value using the given formatter. Read more

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<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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