# Public API
```rust
use std::{path::Path, sync::Arc};
pub use kcode_k1_canonical_chain::{SubmitError, TxId};
pub use kcode_k1_transaction::{
GENESIS_PARENT,
REGISTER_AT_TIP,
SubsystemId,
};
pub trait Subsystem: Send + Sync + 'static {
fn submit_txn(&self, id: TxId, payload: &[u8]) -> Result<(), String>;
fn reorg(&self) -> Result<(), String>;
}
pub struct K1TxnOrdering;
impl K1TxnOrdering {
pub fn open(root: &Path) -> Result<Self, String>;
pub fn register_subsystem(&self, subsystem: SubsystemId, after: Option<TxId>, handler: Arc<dyn Subsystem>) -> Result<(), String>;
pub fn submit_txn(&self, transaction: &[u8]) -> Result<(), SubmitError>;
pub fn submit_local_txn<F, Q>(&self, timestamp: u64, creator: [u8; 32], subsystem: SubsystemId, payload: &[u8], signer: F, queue_propagation: Q) -> Result<(TxId, Vec<u8>), String>
where
F: FnOnce(&[u8]) -> Result<[u8; 64], String>,
Q: FnOnce(&[u8]) -> Result<(), String>;
pub fn contains(&self, id: TxId) -> bool;
pub fn tip(&self) -> Option<TxId>;
pub fn between_txids(&self, older: TxId, newer: TxId) -> Result<Vec<TxId>, String>;
pub fn get_txn(&self, id: TxId) -> Result<Option<Vec<u8>>, String>;
}
```
# Chain and storage
`open` owns the canonical-chain root containing `ordering.dat` and `k1-transaction-store/`. An absent root or existing empty real directory is initialized; a complete existing root opens without migration. Mixed, wrongly typed, symlinked, or malformed required components are rejected. The caller supplies one live instance and process, no separately opened component handles or external mutation, and a supported local storage device.
Complete transaction bytes become durable before their 32-byte order record. Appends synchronize before publication; a winning fork synchronizes suffix truncation and then its replacement before publication. Removed bytes remain stored. Mutation I/O failure, full or ambiguous storage outcome, external mutation, or missing or corrupt canonical bytes emits one concise secret-free fatal diagnostic and aborts without retry, repair, reopening, callback, or ambiguous return. Definite malformed input, missing parent, losing fork, signer failure, and transaction-ID collision are ordinary errors. `GENESIS_PARENT` and `REGISTER_AT_TIP` are reserved and cannot identify real transactions. Storage and wire formats are otherwise unchanged.
# Submission
`submit_txn` accepts complete peer bytes only after caller validation of signature, leader authority, timestamp, ingress policy, and parent-first catch-up order. Unknown or noncanonical parent returns `SubmitError::MissingParent` without mutation. An exact canonical duplicate succeeds without storage or delivery. A tip extension is stored and ordered. Siblings rank by lower complete creator key, then timestamp, then complete SHA-256 digest; a loser is not stored and a winner replaces the canonical suffix. `GENESIS_PARENT` is the first-transaction parent sentinel. `REGISTER_AT_TIP` is never a transaction parent.
Peer extension delivery is reserved only for an active target registration. An absent, replaying, or out-of-commission target does not reject peer bytes and discovers canonical transactions on later replay. A reserved callback error or panic returns `SubmitError::Other` containing the committed `TxId` and faults only that registration.
`submit_local_txn` requires an active target registration before mutation. Under the global writer and chain lanes it selects the durable tip or `GENESIS_PARENT`, invokes the signer once, stores the complete bytes, publishes the order record, derives the committed `TxId`, and reserves the target lane's next ticket. Signer failure or panic precedes mutation. The queue-propagation closure then receives the complete committed bytes outside every KTO and subsystem-lane lock and completes before that transaction's subsystem callback can begin. Queue error or panic does not suppress or fault subsystem integration. The call returns only after its reserved callback completes.
`Ok((id, bytes))` returns the exact committed `TxId` and complete committed bytes, with `id == TxId::for_transaction(&bytes)`. Every queue or integration failure after commitment is a plain string that identifies the committed `TxId` as committed and must not be treated as a retryable precommit rejection. A later winning peer fork may reorganize committed bytes.
# Subsystems
Each registration owns an independent ticketed callback lane. Live tickets are reserved in canonical commit order. Original submission callers wait for their own ticket, invoke the callback without a KTO or lane lock, then advance and wake the lane. Same-subsystem callbacks are ordered. A callback error or panic is caught, faults only that registration, and causes already-reserved later tickets to complete with committed-not-integrated errors instead of invoking callbacks. Propagation failure does not fault a registration.
`register_subsystem` interprets its `after` cursor as follows:
- `None` and `Some(GENESIS_PARENT)` replay matching canonical transactions from genesis.
- `Some(REGISTER_AT_TIP)` atomically installs an active registration at the current canonical tip without historical callbacks.
- Any other `Some(id)` requires a canonical transaction assigned to that subsystem and starts replay strictly after it.
Genesis and real-cursor registration load matching transactions one at a time, oldest first, outside KTO and lane locks. Replaying registrations receive no live reservations. At apparent catch-up, registration rechecks under writer-then-chain coordination and becomes active while commits remain excluded, preventing replay/live gaps and duplicates. Tip registration performs no historical scan or payload read and activates under the same writer-then-chain exclusion boundary, so transactions committed before activation are ignored and later matching transactions cannot be missed. On an empty chain it simply activates for the first later matching transaction. Neither sentinel is stored as an applied transaction ID.
Cursor invalidation or callback error or panic faults only that registration. Re-registration replaces only an out-of-commission or faulted registration whose old lane has no running or reserved work.
A winning reorganization reserves ordered reorg tickets for every active registration whose latest scheduled or delivered transaction was removed, marks those registrations out of commission, and reserves replacement delivery only when its target remains active. Reorg callbacks and an unaffected replacement delivery run independently outside global locks; the submission remains synchronous until all reserved work completes. Callback errors or panics are committed errors through the existing submission surface.
Callbacks are synchronous, unbounded, and unretried. There is no persistent worker, async runtime, payload queue, timeout, retry, or admission limit. A caller waits only behind earlier tickets for the same registration, with no finite wall-clock bound and no coordination required for unrelated registrations. A blocked callback does not hold the writer or chain, block queries, or prevent another subsystem's submission, callback, registration, or replay. A blocked signer intentionally holds writer and chain until local commit or rejection. Callbacks may re-enter unrelated subsystem operations. Recursive submission to the same subsystem is unsupported because it waits behind its own callback ticket.
Only canonical callbacks for a subsystem may update that subsystem's durable materialization. No required persistent state may depend on another subsystem's materialization. Empty-state replay of only that subsystem's payloads must reproduce its durable materialization. A subsystem that intentionally owns no materialized state may register at `REGISTER_AT_TIP` and retrieve any historical canonical transaction directly by ID. Callers hold no subsystem persistence lock while submitting or waiting.
# Queries and performance
`contains` excludes both reserved sentinels. `tip` returns the newest canonical ID. `get_txn` returns `None` for noncanonical IDs and aborts on missing or corrupt canonical bytes. `between_txids` uses exclusive canonical boundaries and returns IDs oldest first. `older` may be `GENESIS_PARENT`; other boundaries must be canonical. Equal valid boundaries return empty; unknown or reversed boundaries are errors. At most 128 IDs are returned by the canonical chain's documented even-sampling formula.
For canonical length `N`, transaction length `B`, and removed suffix `R`: open is `O(N)` time and memory without payload reads; membership and tip are `O(1)`; bounded-between returns at most 128 IDs; local construction, retrieval, and normal submission are `O(B)` plus persistence; replacement removes `O(R)` memory; genesis or real-cursor registration scans `O(N)` and reads matching transactions; tip registration performs bounded in-memory work with no history scan or payload read. The managed one-million-record root must open in under five seconds. A complete `open` over 100 milliseconds emits exactly one JSON warning containing only module, operation, elapsed microseconds, and `ready` or `error`. No wall-clock guarantee applies to storage, signer, queue propagation, lane wait, or callback latency.