Platform Semantics (Phase 1)
HelioDB is currently a single-node system: SQLite storage, in-memory pub/sub, WebSocket JSON-RPC, and TypeScript/React clients. This page captures the behavioral contract that all backends must uphold and the known gaps on the path to multi-backend support.Current Scope
- Storage: SQLite (immediate durability).
durable: trueis a no-op for now. - Transport: WebSocket JSON-RPC (
/ws) with client-auth message. - Clients: TypeScript (
@heliodb/client,@heliodb/react,@heliodb/contract). - Deployment: Single process, single machine; no sharding or replicas.
Event Model
- ID:
event_<ULID>(monotonic, time-sortable). Generated per-node; monotonicity is per process. - Fields:
namespace,resource,subject,event_type,data,metadata?,created_at(server timestamp). - Namespaces: Isolation boundary. Reads/subscribes do not cross namespaces.
Resources & Filters
resourceis a slash-delimited path (e.g.,workspace/123/threads/456).*is allowed in any segment (e.g.,workspace/*/threads).exact: truematches only the pattern depth (wildcards still match a single segment).exact: false(default) is prefix: matches the pattern and any deeper descendants.- Wildcards work with both exact and prefix modes.
Ordering & Cursors
- Append visibility:
logis synchronous; events are visible to reads/subscribes immediately after insert (read-your-writes). - Ordering: Within a resource, events are ordered by
event_id(ULID). Cross-resource ordering is not defined. - Cursors are exclusive:
cursormeans “start after this ID.” For forward reads/subscribe:id > cursor. - Subscribe = history + live: Server fetches historical events forward from
cursor(or beginning), then streams live events over in-memory broadcast.
Subscriptions
- Unified stream:
subscribe()streams both historical and live events in order. Useread()first if you need to distinguish between replay and live. - Pattern:
read()+subscribe({ start: lastId })is the recommended pattern for agents that need to know their history state before processing live events. - Fanout/matching: In-memory broadcast keyed by
(namespace, pattern, exact), with wildcard matching on*segments and prefix vs exact semantics as above. No persistence; subscriptions disappear when connections drop. - Backpressure/lag: If a subscriber lags and drops messages from the broadcast buffer, the server replays from the last delivered ID via storage. If that catch-up fails, the stream ends.
- Filtering: Optional
event_typesandsubjectfilters applied server-side during subscription and catch-up. - At-least-once delivery for live streams under lag (catch-up may re-send; consumers should handle idempotently).
Reads
- Direction: Forward by default;
reverseis available for finite historical reads only. - Limits: Optional
limitapplies after cursor filtering. - Filters:
resource(exact or prefix viainclude_children),subject(exact),event_types(any of set).
Auth
- WebSocket clients must send
authfirst. - Modes:
- JWT: Namespace/subject derived from claims; server validates signature with
HELIODB_SECRET. - Shared secret (dev/demo): Token equals server secret; namespace/subject must be provided by client.
- JWT: Namespace/subject derived from claims; server validates signature with
- All subsequent methods execute within the authenticated namespace/subject.
Validation & Schemas
- Client enforces schemas via Zod contracts. Server does not enforce schemas yet; it trusts the client’s data shape.
- Identifiers (
namespace,resource,event_type,subject) are validated server-side for format.
Known Gaps (Phase 1)
- No persistent subscriptions (all live fanout is in-memory).
- No server-side schema enforcement; no ACLs beyond namespace/subject on the token.
- Only WebSocket; no REST/SSE/webhooks.
- Only SQLite backend; no multi-node/sharded deployment.
- Multi-modal payloads are stored inline as JSON today; blobs/object-store tier is not implemented yet.
Near-Term Priorities
- Finalize semantics contract tests (shared suite every backend must pass).
- Add additional storage backends (e.g., Postgres for portability/testing, ClickHouse for analytics) behind the same
log/read/subscribecontract. - Introduce persistent subscriptions/queues for durable fanout.
- Benchmarks for append latency, catch-up scans, and subscription lag handling across backends.