@xyo-network/event-kit-statement-graph
v1.0.0
Published
Statement Graph read-model adapter for Event Kit wake grants
Downloads
1,443
Readme
@xyo-network/event-kit-statement-graph
Finalized Statement Graph replay and Event Kit handshake read models.
The package has four public seams:
createFinalizedEventKitReplayControllerfolds the Event Kit lifecycle vocabulary from finalized XL1 blocks and hash-verified bodies;createEventKitHandshakeReadModelderives publisher service, key, subscription, grant, acceptance, replacement, and revocation views from that replay; andcreateStatementGraphGrantReadModeladapts a canonical Statement Graph viewer to the narrower wake-admission grant port; andcreateActiveSourceWakeGrantAuthorizerrequires the exact active D-005 subscription and its exact live grant for source-aware delivery.
Finalized replay
const replay = await createFinalizedEventKitReplayController({
bodies: dataLakeViewer,
chain: finalizedBlockViewer,
chainId,
checkpointStore,
floorBlock: 0,
network: 'local',
publisher,
sourceId: dataLakeEndpoint,
})
await replay.sync()
// A bounded worker still reports the real live finalized head while advancing
// its durable projection by at most the requested number of blocks.
await replay.sync({ maxBlocks: 4_096, signal })
// The durable projection exposes its exact canonical XL1 root.
const at = replay.anchor() // { block, hash } or null before the first commit
const handshakes = createEventKitHandshakeReadModel({
body: hash => replay.body(hash),
viewer: replay.viewer(),
})The controller bulk-stages contiguous finalized ranges through Statement Graph
with the exact Event Kit object-schema allowlist. The default full sync()
catches up to the independently observed live head; sync({ maxBlocks, signal })
lets a bounded worker retain that real finalizedHead while advancing
indexedThrough by at most maxBlocks. A staged range is published only after
every publisher-scoped object and required challenge/response attachment in it
is verified. An unresolved Statement Graph event, a missing or corrupt claimed
object, or a missing or corrupt attachment records the earliest affected block
as an explicit blocked boundary and leaves indexedThrough before that block.
A clean prefix is re-scanned and may be committed first. A later sync() retries
the same blocked block; unavailability is never interpreted as absence or
revocation. Cancellation retains only prefixes whose atomic checkpoint save has
already resolved.
Published Statement Graph 0.5.3 admits consensus-elevated, subject-bearing
Claims and decodes Claim bodies from finalized block carriers while resolving
claimed objects and acceptance attachments through the datalake. The Gate 2
live rerun against that release remains the acceptance boundary.
Statement Graph 0.5.x uses fold version 4 and scan-checkpoint format 2. Event
Kit's publisher-scoped outer checkpoint is format 3 and binds
indexedThrough to the exact canonical XL1 root hash. Only the exact
current checkpoint and ordered Event Kit schema policy are accepted. Missing,
additional, reordered, or version-suffixed policy entries fail closed as
invalid content; no alternative-policy reader or in-place migration exists.
status() exposes two distinct watermarks:
finalizedHeadis the latest finalized height observed from XL1; andindexedThroughis the highest block whose Event Kit projection was fully committed, ornullbefore the floor block has been indexed.
anchor() returns the durable {block, hash} pair for indexedThrough. A
height-only checkpoint from the previous format is replayed from the floor; it
is never upgraded into root-bound evidence by assumption.
The strict EventKitReplayCheckpointZod checkpoint binds network, chain ID,
publisher, datalake source identity, floor block, Statement Graph fold version,
and the ordered schema policy. It retains the complete canonical
AppliedEventRecord history and verified bodies in addition to the upstream
slim claim checkpoint. That extra history is required to reconstruct exact
same-claim subscription fans after restart. A checkpoint from another dataset
fails closed instead of being silently reused. Restoration also recomputes
every persisted body hash and cross-checks progress, fold records, applied-event
provenance, resume watermark keys, and blocked position. Concurrent sync()
calls are serialized around durable checkpoint replacement.
EventKitReplayCheckpointStore.save is an atomic-persistence port. The new
snapshot becomes visible only after save resolves. This package includes a
memory implementation for structural tests; a filesystem or SQLite adapter is
not supplied here.
createCheckpointStatementGraphViewer restores the canonical viewer surface
from a checkpoint, including claimsByObject, claimsBySubject, per-source
applied history, and resolved objects.
Handshake discovery
The handshake read model:
- independently reports the currently selectable publisher-authored service and preferred key, if one exists;
- uses
claimsBySubject(publisher)to discover candidate subscriber sources without a full-chain source scan; - enumerates each candidate source's applied-event history under that same
publisher subject and
groups it by immutable
(txHash, eventHash)evidence; - accepts only an exact same-claim fan containing one selected service, one grant, and one encrypted subscription with matching publisher, chain, service, key, key ID, grant, event kind, and lifetime policy;
- resolves publisher-authored acceptances and their hash-bound challenge and response attachments; and
- applies authorized service, key, grant, subscription, and acceptance revocations plus deterministic revision cutover.
The reconciliation view is intentionally separate from current advertisement
discovery. Under existing-acceptances-continue, an exact finalized acceptance
continues after service/key expiry, revocation, removal, or supersession; those
retirement events only prevent a new acceptance under that descriptor.
An accepted subscription becomes active at acceptanceBlock + 1. Wrong-source
revocations are ignored. Current-advertisement discovery fails closed on an
unavailable, malformed, or ambiguous selection. Reconciliation rejects stale,
ahead-of-finality, or unavailable projections but can still return exact
continuing acceptances alongside a nested advertisement-selection failure. Both
surfaces retain concrete block/transaction/event/hash evidence.
This read model intentionally does not hold publisher private keys, decrypt a
subscription, compare the grant's deployment and queue to the encrypted
plaintext, perform the endpoint challenge, or verify its proof. The Phase 3
publisher client in @xyo-network/event-kit-node/handshake consumes this
finalized candidate view, performs those private checks through protocol APIs,
durably records the one-time challenge decision, and publishes the exact
acceptance. Phase 4 adds the resident scanner and service orchestration.
Wake-grant admission adapter
const readModel = createStatementGraphGrantReadModel({ viewer: replay.viewer() })
const grants = createActiveSourceWakeGrantAuthorizer({
route,
publisher,
serviceHash,
grantHash,
viewer: readModel,
handshake: handshakes,
})The adapter parses branded addresses and hashes rather than casting them and
preserves the materializing claim evidence used by the D-002 admission gate.
Statement Graph owns Claim only: it provides structural
{source, subject, hashes}, no target, and no revoke verb. The admission
adapter requires Claim subject to equal the grant body's publisher. Event Kit
negation is an ordinary revocation object under that same publisher subject,
claimed by the authorized controller. See
D-003.
Evidence boundary
For source-aware ingress, construct createActiveSourceWakeGrantAuthorizer
with {route, publisher, serviceHash, grantHash, viewer, handshake}. The route
includes authorizationChainId, exact source, canonical filterHash, and
immutable subscriptionHash. viewer is the grant read model and handshake
is the finalized handshake read model over the same pinned chain. Both must
identify the independently read, uncapped finalized XL1 head. The helper rejects
pending, expired, revoked, superseded, wrong-service, or stale bindings and will
not substitute another grant with the same deployment/queue scope. Existing
accepted subscriptions continue after advertisement retirement as D-005 allows.
Discovery and reconciliation use the single source-aware event kind,
SOURCE_WAKE_EVENT_KIND. Source identity remains explicit in the service and
encrypted filter; it is never inferred from the authorization chain.
Verification scope
Offline deterministic-chain specs currently cover:
- strict checkpoint round-trip and dataset-identity rejection;
- full applied-history persistence and equivalent viewer restoration;
- block-atomic retry after a missing claimed object or acceptance attachment;
- refusal to publish staged state after checkpoint-save failure;
- rejection of hash-tampered or cross-field-inconsistent restored checkpoints and serialization of concurrent sync calls;
- publisher service/key discovery and a pending unaccepted tuple;
- exact same-claim reconstruction rather than assembly across separate claims;
- activation at
acceptanceBlock + 1, authorized revocation, and ignored wrong-source revocation; - exact acceptance-revocation grouping, source-scoped tuple identity, and continuity after service/key retirement;
- stale and ahead-of-finality rejection; and
- equivalent lifecycle state after replay from block zero.
The original api-local fold evidence used legacy unelevated Claim semantics
with MemoryStatementDataLake. It is superseded by the passing Statement Graph
0.5.0 live run and these two stronger structural boundaries:
src/spec/node/restDataLakeProcess.spec.tsuses real XL1 SDK REST runner and viewer clients against a separate child process. The fixture stores one canonical body per hash on the filesystem and proves readback, temporary unavailability, restoration, process restart, and readback after restart.src/spec/api-local/liveHandshake.spec.tsuses two independently derived accounts, the realapiLocalXL1 runner/viewer,publishFinalizedStatements, and separately constructed REST writer/reader clients against that process. It proves finalized service/key discovery and hash verification, a pending same-claim subscription/grant, acceptance-block cutover atA + 1, revocation, replay-from-zero equality, distinctfinalizedHead/indexedThroughwatermarks, and retry from the exact blocked block after a required body is restored.
pnpm test:live runs this handshake alongside the existing local-chain head,
admission, and Statement Graph fold specs. All 14 tests pass against xyo-chain
and apiLocal 5.3.0, XL1 SDK/protocol 5.4.0, and Statement Graph 0.5.0:
the chain validates elevated Claims and Statement Graph reads each Claim from
its finalized block carrier. The REST service is a test-owned loopback fixture,
not the hosted shared public datalake, so this is not public-stack, Sequence,
Mainnet, Proxmox, multi-VM, or production qualification.
