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
impl JsonlLog
Sourcepub fn open(path: impl AsRef<Path>) -> Result<Self, JsonlError>
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.
Sourcepub fn head(&self) -> Option<&str>
pub fn head(&self) -> Option<&str>
Current chain head (event_hash of the most recent row).
None for an empty log.
Sourcepub fn append(
&mut self,
event: Event,
policy: &PolicyDecision,
) -> Result<String, JsonlError>
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:
- The composition includes contributors for
APPEND_EVENT_SOURCE_TIER_GATE_RULE_ID,APPEND_ATTESTATION_REQUIRED_RULE_ID, andAPPEND_RUNTIME_MODE_RULE_ID. Callers that skipped composition are refused. - When
event.sourceisEventSource::User, the attestation contributor MUST votePolicyOutcome::Allowregardless of final outcome. ADR 0026 §4 forbidsBreakGlassfrom substituting for the attestation requirement at the user-event authority boundary. - A final outcome of
PolicyOutcome::RejectorPolicyOutcome::Quarantinefails closed and writes nothing.
Sourcepub fn append_signed(
&mut self,
event: Event,
attestor: &dyn Attestor,
policy: &PolicyDecision,
) -> Result<String, JsonlError>
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:
- The composition includes contributors for
APPEND_SIGNED_KEY_STATE_CURRENT_USE_RULE_ID(ADR 0023 current-use revalidation: historical-only or revoked signing keys voteReject) andAPPEND_SIGNED_TRUST_TIER_MINIMUM_RULE_ID(ADR 0019: the principal MUST sit at or aboveVerified). Callers that skipped composition are refused. - The key-state contributor MUST vote
PolicyOutcome::Allowregardless of final outcome. ADR 0026 §4 forbidsBreakGlassfrom substituting for current-use revalidation at the signed ledger root. - A final outcome of
PolicyOutcome::RejectorPolicyOutcome::Quarantinefails closed and writes nothing.
Returns the new head hash on success.
Sourcepub fn append_schema_migration_v1_to_v2(
&mut self,
payload: SchemaMigrationV1ToV2Payload,
policy: &PolicyDecision,
) -> Result<String, JsonlError>
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.
Sourcepub fn append_schema_migration_v1_to_v2_with_event(
&mut self,
payload: SchemaMigrationV1ToV2Payload,
policy: &PolicyDecision,
) -> Result<(String, Event), JsonlError>
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:
- The composition includes contributors for
SCHEMA_MIGRATION_AUTHORITY_CLASS_RULE_ID(ADR 0019 §3: the proposing principal sits in theOperatorauthority 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), andSCHEMA_MIGRATION_CURRENT_USE_TEMPORAL_AUTHORITY_RULE_ID(ADR 0023 §2 / §5: the signing key state at attestation time isActive, notRetiredorRevoked). Callers that skipped composition are refused. - Per ADR 0026 §4 (hard wall), the attestation contributor and the
current-use temporal-authority contributor MUST each vote
PolicyOutcome::Allowregardless of final outcome. ABreakGlassdecision MUST NOT substitute for either contributor at the migration authority root. - A final outcome of
PolicyOutcome::RejectorPolicyOutcome::Quarantinefails closed and writes nothing.
Sourcepub fn last_sig_prefix(&self) -> [u8; 32]
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.
Sourcepub fn iter(&self) -> Result<JsonlIter, JsonlError>
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.
Sourcepub fn iter_signed(&self) -> Result<SignedJsonlIter, JsonlError>
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.
Sourcepub fn verify_chain(&self) -> Result<(), JsonlError>
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.