Architecture
For architects and platform engineers, this page explains how runtime, schema, client, and transport layers fit together.
LIVON is an event-driven runtime with strict module boundaries:
- @livon/runtime orchestrates hook chains and envelope lifecycle.
- @livon/schema resolves operations/subscriptions and validates payloads.
- @livon/*-ws-transport maps runtime envelopes to WebSocket wire payloads.
- @livon/client exposes generated APIs on top of runtime + transport.
Layer map
Envelope schema
All modules exchange one envelope shape (EventEnvelope).
type EventEnvelope = {
id: string;
event: string;
status: 'sending' | 'receiving' | 'failed';
metadata?: Readonly<Record<string, unknown>>;
context?: RuntimeEventContext;
payload?: Uint8Array;
error?: {
message: string;
name?: string;
stack?: string;
context?: Readonly<RuntimeEventContext>;
};
};
Field parameters
id(string): unique envelope id.event(string): event/topic name.status('sending' | 'receiving' | 'failed'): lifecycle status.metadata(Record<string, unknown>, optional): routing/correlation metadata.context(RuntimeEventContext, optional): module-enriched context.payload(Uint8Array, optional): binary payload.error(EventError, optional): normalized error payload.
Rules:
- Envelope must contain
payloadorerror. runtimecreatesidwhen missing.- Runtime executes modules in registration order: left to right in
runtime(moduleA, moduleB, ...). runtimeapplies defaultstatus:emitSendandemitEvent=>sendingemitReceive=>receiving
metadataandcontextare merged immutably when hooks callnext(update).
Serialization boundaries
@livon/runtime does not serialize. Serialization starts at schema and transport.
| Property | Runtime | Schema module | WS transport |
|---|---|---|---|
id | Created/forwarded | forwarded on errors, regenerated on new emits without id | forwarded |
event | forwarded | operation response uses same event; publish uses subscription topic | forwarded |
status | set by emit path | preserved for schema error emits | forwarded |
metadata | merged | forwarded and extended for publish (key, ack) | packed on wire |
context | merged | forwarded copy | encoded/decoded as msgpack bytes |
payload | binary only (Uint8Array) | decode input, run schema, encode output | packed as binary payload field |
error | normalized in runtime error path | converted to schema error envelope | encoded/decoded as msgpack error field |
Runtime responsibilities
- Registers
onReceive,onSend,onErrorchains. - Exposes
ctx.emitReceive,ctx.emitSend,ctx.emitError,ctx.emitEvent. - Never assumes transport details.
- Calls
onErrorhooks when any hook throws.
Schema module responsibilities
- Runs on
onReceive. - Decodes request payload (
decoder, default msgpack). - Parses input via LIVON schemas, executes operation/fieldOperation.
- Emits response event with encoded output.
- Handles subscriptions via
publishby validating input/payload/output. - Emits errors through
ctx.emitErrorwhile preserving incoming status.
Transport responsibilities
- Converts WebSocket frames to and from
EventEnvelope. - Sets inbound status to
receiving. - Emits inbound envelopes with
registry.emitReceive. - Sends outbound envelopes from
registry.onSend. - Must not run schema validation or domain logic.
Module author checklist
When writing a custom module:
- Register through runtime hooks only.
- Treat envelope as immutable input.
- Forward
id,event,metadata,contextunless you intentionally override. - Use
ctx.emitErrorinstead of throwing cross-module control errors. - Keep serialization concerns in transport/schema boundaries, not runtime core.