Query Plane
Today's storage API and the draft SessionQL 1.0 contract
USL has two deliberately separate query surfaces:
- the implemented L1 storage API, a small Rust API for deterministic record reads;
- the draft portable query plane in RFC 0001, which specifies SessionQL 1.0, Query IR 1.0, search, lineage, insights, subscriptions, and a read-only SQL attachment.
The RFC is an implementation contract, not a claim that every capability below ships in usl-core today.
Implemented: L1 storage API
The current Rust surface is session-scoped and schema-agnostic:
Store::create(path, opts) / Store::open(path, opts) -> Store
store.append(&Record) -> seq // assigned monotonically
store.flush() // full fsync
store.scan(&SessionId, from_seq) -> Vec<StoredRecord>
store.get(&SessionId, seq) -> Option<StoredRecord>
store.next_seq() -> u64
Store::verify(path) -> Verification // read-only integrity reportReading
- scan returns a session's records with
seq >= from_seq, in ascending order — the basis for replay and live tail. - get is a point read by session + seq.
- verify opens read-only and reports
data_end,next_seq,frame_count, and the truncation offset (if any), without mutating the file.
The index
The store keeps an in-memory Index (session_id → frame offsets), rebuilt by replaying the log on open. Reads open a fresh file handle and re-verify CRC on every frame — defense in depth against silent corruption. This index is part of the current L1 engine; it is not the structured/full-text/vector sidecar system proposed by RFC 0001.
Draft: portable query contract
The RFC adds a stable boundary shared by CLIs, SDKs, UIs, natural-language translators, and interchangeable execution engines:
| operation | purpose |
|---|---|
query.execute | Execute SessionQL text or a typed QueryPlanV1 |
query.explain | Return the normalized plan, indexes, row counts, scores, and freshness decision |
query.subscribe | Replay then tail a deterministic, bounded audit query |
resume.describe | Locate a result in its original runtime without overstating resume capabilities |
Every result is fixed to an asOfSeq. Cursors bind the query, access policy, snapshot, index generations, and full sort key, so pagination cannot silently drift after concurrent appends.
interface QueryResultV1<Row> {
apiVersion: "query.usl.dev/v1";
queryHash: string;
asOfSeq: number;
watermarks: IndexWatermarkV1[];
rows: Row[];
nextCursor?: string;
warnings: QueryWarningV1[];
explain?: QueryExplainV1;
}System boundary
- ASP defines canonical events, identity, causality, and provenance.
- USL persists the append-only source of truth and defines query semantics.
- SESDB may execute the plan, but does not define the specification.
- SQL is an optional read-only analytics attachment, not a SessionQL equivalent.
Continue with SessionQL, Search & indexing, or Insights & subscriptions.
Source of truth: usl-core/src/store.rs.