@xyo-network/event-kit-node
v1.0.1
Published
Node 24+ Event Kit durability, handshake clients, and SQLite source publisher runtime
Readme
@xyo-network/event-kit-node
Node.js 24+ durability adapters for Event Kit (verified on Node 24.14.1):
createFilesystemCursorStore— publisher cursor for the wake publisher actorcreateFilesystemPublisherOutbox— durable pending-wake slot for retry stabilitycreateFilesystemWakeQueue— crash-safe at-least-onceWakeQueuecreateSqliteWakeQueue—WakeQueueovernode:sqlite, no new dependenciescreateFilesystemWakeInbox— durableAtomicWakeInboxover that queue
Handshake clients
Import @xyo-network/event-kit-node/handshake for the Phase 3 control-plane
surface. That subpath exports:
- synchronized finalized-replay readers and XL1 publication composition;
- publisher and subscriber clients for declaration, discovery, encrypted subscription/grant publication, challenge, acceptance, and revocation; and
- filesystem stores for replay checkpoints, mutation intents, publication journals, publisher encryption-key custody, and endpoint-challenge decisions.
Generated on-chain handshake mutations use a caller-supplied 16-byte
lowercase-hex operation ID.
createFilesystemHandshakeMutationIntentStore atomically binds the first
request to its exact random service/key, grant, ciphertext, or acceptance
artifacts. An exact retry loads that winner before consulting mutable chain
state; changed input under the same scoped operation ID fails closed.
Records and directories are private (0600/0700). Publisher service intents
retain the encryption private key until it can be copied idempotently into key
custody. Subscriber intents retain ciphertext and a request fingerprint, not
endpoint or challenge-secret plaintext.
createFilesystemFinalizedStatementPublicationJournal then binds the logical
mutation to its chain, Claim source, ordered object/attachment hashes, and exact
signed transaction. Restart resumes from the highest durable phase and may
rebroadcast only that evidence; it never rebuilds or re-signs an ambiguous
publication.
The handshake subpath deliberately does not load the wake queues, inbox,
outbox, or node:sqlite. A clean packed consumer typechecks and executes it
without SQLite coupling. There is one unversioned source-aware contract.
Service publication requires sources and maxPositionsPerWake; subscription
publication requires source and fromPosition. Authorization remains pinned
to the XL1 session independently of source positions. Removed schema suffixes,
block-range fields, and profile selectors have no compatibility aliases.
Resident publisher
@xyo-network/event-kit-node/publisher-runtime exports the SQLite publisher
authority/stores, persistent runtime, real D-005 discovery, bounded transports,
native REST gateway/datalake composition, and XL1/Ethereum/UTC source readers.
It uses the shared periodic actor lifecycle; the CLI owns process interrupts,
provider cleanup, and probes. Importing the module does not open a database;
opening publisher state loads node:sqlite explicitly.
Logical wake creation and pending outbox persistence co-commit. Durable admission receipt, contiguous publisher cursor, and outbox clear co-commit independently for each subscriber. Process death and ambiguous responses resend the same body with a fresh real wallet JWT. Publisher progress is admission progress, not completed receiver compute. See the runbook for ownership, crypto custody, retry/cancellation rules, transport limits, and the remaining hosted/Sequence gates.
Durability model
createFilesystemWakeInbox commits in one order and that order is the
correctness property: the wake reaches the durable queue before either
identity record is written.
- A crash in the gap redelivers the wake, which the idempotent consumer absorbs.
- The reverse order could claim
wakeIdfor a wake that was never queued and wedge it permanently, which D-002 §6 forbids.
Records and queue entries are published with link, never rename. rename is
unsound as a claim: when two callers race the same source, both can observe
success once the paths resolve to one inode, and the entry is handed out twice.
link fails with EEXIST, so exactly one caller wins and the loser re-reads the
winner's receipt.
A claim is link(pending -> inflight) then unlink(pending). A consumer that
dies mid-handler leaves its entry in inflight/; recoverInflight() returns it
to pending/ at startup. A crash between the link and the unlink leaves one
inode in both sets, and recovery keeps the pending copy.
Queue providers
Both queues implement the shared lease-based WakeQueue port and pass the
conformance suite in @xyo-network/event-kit-testing, so they are swappable
with each other and with the in-process memory provider.
SQLite makes the lease a single statement: UPDATE … WHERE state = 'pending',
where the affected-row count decides the winner. That is the transactional form
of what the filesystem provider approximates with link. WAL lets several
processes on one host share the file; it does not coordinate across hosts.
node:sqlite is built in, so this adds no dependency. It does emit an
ExperimentalWarning.
Boundary
This survives process death, not host loss, and does not coordinate across machines. The historical core implementation plan's Gate 5 durable-adapter clause is satisfied for a single node only. The on-chain publisher roadmap qualifies a single VM in Phases 4-7; a coordinated multi-VM deployment needs a transactional shared store, leases, and fencing and is roadmap Phase 8.
