Esiur Protocol (EP) Specification
1. Overview
The Esiur Protocol (EP) [Zamil & Jasim, 2026] is a compact binary application-layer protocol for distributed object computing. It enables first-class sharing of object instances across a TCP or WebSocket connection: property values synchronize in real time, remote methods are called as if local, and events propagate transparently to every subscriber.
EP is fully self-describing: a client can discover the type schema
of any resource at runtime (a TypeDef query) and generate
strongly-typed proxies without out-of-band API documents. Actively maintained
implementations exist for .NET and
TypeScript, sharing wire concepts, packet
layout, and command names byte-for-byte.
Key design properties:
- Minimal wire overhead β a 1-byte header is a complete, valid notification packet; request/reply packets add a 4-byte callback ID.
- Correlation-safe β every request carries a callback ID matched to its asynchronous reply.
- Streamable β chunked transfers, progress reports, and pausable pull/push streams for long-running operations.
- Authenticated by construction β every connection completes an authentication phase (possibly a no-op, see Β§4) before any session packet is exchanged.
2. Transport & URI scheme
TCP
EP runs natively over TCP. EpServer defaults to TCP port
10518 in both reference implementations; a bare
ep://host URL (no explicit port) resolves to the same default.
The server accepts incoming connections and immediately begins the
Authentication Phase (Β§4).
WebSocket
EP also runs as a WebSocket sub-protocol, identifier EP
(uppercase, compared case-sensitively/ordinally by both implementations).
After the WebSocket handshake, the binary framing is identical to the
TCP wire format: each WebSocket message carries exactly one EP packet.
URI scheme
Resources are addressed with an ep:// URL:
ep://<host>[:port]/<store>/<resource-path>
Examples
ep://server.example.com/sys/thermostat
ep://localhost:10518/sys/counter
ep://iot-hub.local/sensors/temperature
The path maps directly to a resource in the server's store hierarchy,
analogous to a filesystem path. Omitting the port uses 10518.
A bare ep://host[:port] with no path resolves to the connection itself.
eps:// to
wss:// (TLS-protected WebSocket) β this is implemented and
working. The .NET client's URL resolver does not currently register a
distinct handler for an eps/EPS scheme, and
neither implementation currently wraps raw TCP connections in
TLS automatically. Confidentiality on a raw TCP connection today
relies entirely on the application-layer session encryption negotiated
during the auth handshake (Β§4.3), not on transport-layer TLS. See
Β§8 and Β§9.
3. Connection lifecycle
Client Server
β β
βββββββββββ TCP / WebSocket connect ββββββββββββΆβ
β β
βββββββββ Auth: Initialize ββββββββββββββββββββββ server sends first
β β
βββββββββ Auth: Action ββββββββββββββββββββββββββΆ zero or more rounds,
βββββββββ Auth: Acknowledge βββββββββββββββββββββ protocol-dependent
β β
βββββββββ Auth: SessionEstablished ββββββββββββββ
β β
ββββββββββββββ EP session packets ββββββββββββββΆβ
β Request / Reply / Notification β
β β
βββββββββββ connection close βββββββββββββββββββΆβ
Once the session is established, the connection is fully bidirectional β either peer may send Requests, Replies, or Notifications at any time. A resource attachment stays live until the connection closes or the client explicitly detaches; on reconnect, a client can reattach and resynchronize rather than re-fetch full state from scratch.
4. Authentication phase
Before any EP session packet is transmitted, both sides exchange one or
more auth packets, a distinct binary format from session
packets. The server always sends the first packet (Initialize).
Even a connection configured for anonymous access completes this phase β
it just negotiates AuthenticationMode.None with
EncryptionMode.None in a single round trip.
4.1 Auth packet header byte
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|---|---|
| Command2 bits | hasTDU1 bit | reserved1 bit | AuthMode2 bits β Initialize only | EncMode2 bits β Initialize only | |||
For every command other than Initialize, bits 4β0
(with bit 5 masked off) are instead read as a single EpAuthPacketMethod
byte (Β§4.3) β the low 5 bits given above only apply to the very first,
server-sent Initialize packet. hasTDU (bit 5)
β when set, a TDU payload (Β§5) follows, encoding a keyβvalue map of
handshake parameters (Β§4.4).
4.2 Auth commands (bits 7β6)
| Value | Name | Direction | Description |
|---|---|---|---|
| 0x0 | Initialize | Server β Client | Opens authentication; announces supported modes and algorithms |
| 0x1 | Acknowledge | Both | Accept, deny, or redirect an authentication step |
| 0x2 | Action | Client β Server | A handshake step β credentials or key material |
| 0x3 | Event | Server β Client | Session established, an error, or an indication |
AuthenticationMode (bits 3β2 of an Initialize packet):
| Value | Name | Description |
|---|---|---|
| 0x0 | None | No identity required from either party |
| 0x1 | InitializerIdentity | The connecting client must authenticate |
| 0x2 | ResponderIdentity | The server must present its identity to the client |
| 0x3 | DualIdentity | Mutual authentication β both parties must prove identity |
EncryptionMode (bits 1β0 of an Initialize packet):
| Value | Name | Description |
|---|---|---|
| 0x0 | None | Session data transmitted without application-layer encryption |
| 0x1 | EncryptWithSessionKey | Symmetric session key (AES-256-GCM) derived during the handshake |
| 0x2 | EncryptWithSessionKeyAndAddress | Session key additionally bound to the connection's source address |
4.3 EpAuthPacketMethod (Acknowledge / Action / Event byte)
For any command other than Initialize, the byte identifies the specific handshake step:
| Value | Name | Command | Description |
|---|---|---|---|
| 0x40 | Denied | Acknowledge | Auth denied β terminate connection |
| 0x41 | NotSupported | Acknowledge | Requested method not supported β terminate |
| 0x42 | TrySupported | Acknowledge | Try an alternative, supported method |
| 0x43 | Retry | Acknowledge | Retry with different parameters |
| 0x44 | ProceedToHandshake | Acknowledge | Proceed to the key-exchange step |
| 0x45 | ProceedToFinalHandshake | Acknowledge | Proceed to the final handshake step |
| 0x46 | ProceedToEstablishSession | Acknowledge | Derive session keys |
| 0x47 | SessionEstablished | Acknowledge | Session ready β switch to EP session packets |
| 0x80 | Handshake | Action | Client sends key material / credentials |
| 0x81 | FinalHandshake | Action | Client sends final confirmation |
| 0x82 | KeyRotation | Action | Request mid-session encryption-key rotation (.NET only β see Β§8) |
| 0xC0 | Established | Event | Auth flow complete |
| 0xC1 | ErrorTerminate | Event | Fatal auth error β connection will close |
| 0xC2 | ErrorMustEncrypt | Event | Encryption is required for this resource |
| 0xC3 | ErrorRetry | Event | Transient error β client may retry |
| 0xC8 | IndicationEstablished | Event | Informational: (re-)established indication |
| 0xC9 | KeyRotationEstablished | Event | Key rotation completed (.NET only β see Β§8) |
| 0xD0 | IAuthPlain | Event | Identity assertion, unhashed |
| 0xD1 | IAuthHashed | Event | Identity assertion, hashed |
| 0xD2 | IAuthEncrypted | Event | Identity assertion, encrypted |
4.4 Auth TDU header keys
The TDU payload of an auth packet is a keyed map. Integer keys 0β17 are defined:
| # | Key | # | Key |
|---|---|---|---|
| 0 | Version | 9 | SoftwareIdentity |
| 1 | Domain | 10 | Referrer |
| 2 | SupportedAuthentications | 11 | Time |
| 3 | SupportedHashAlgorithms | 12 | IPAddress |
| 4 | SupportedCiphers | 13 | Identity |
| 5 | SupportedCompression | 14 | AuthenticationProtocol |
| 6 | SupportedMultiFactorAuthentications | 15 | AuthenticationData |
| 7 | CipherType | 16 | ErrorMessage |
| 8 | CipherKey | 17 | CipherNonce |
5. TDU (Transmission Data Unit) encoding
Every value carried inside a session or auth packet β arguments, return values, property contents, handshake parameters β is encoded as a self-describing TDU. Decoders never need out-of-band schema information: each TDU carries its own type tag and, for variable-length types, its own length.
5.1 Identifier byte
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|---|---|
| Class2 bits | Fixed: exponent / Var-length: size-field count3 bits | Sub-type3 bits | |||||
| Bits 7β6 | Class | Bits 5β3 meaning |
|---|---|---|
00 | Fixed | Exponent β payload width = 0 if exponent is 0, else 1 << (exponentβ1) bytes. |
01 | Dynamic | Length-field byte count (0β7, see Β§5.3) β how many big-endian length bytes follow the identifier. |
10 | Typed | Same length-field encoding as Dynamic. Carries a type reference (record/enum/tuple). |
11 | Extension | Same length-field encoding as Dynamic. Protocol-level markers. |
For Dynamic, Typed, and Extension classes, the identifier recovered by a
parser is headerByte & 0xC7 (class bits + sub-type bits
only β the length-field-count bits are stripped once the length has been
read). The length bytes themselves are big-endian
(most-significant byte first):
| Bits 5β3 | OR-mask | Length-field size | Max payload |
|---|---|---|---|
000 | 0x00 | none β zero-length value | 0 B |
001 | 0x08 | 1 byte | 255 B |
010 | 0x10 | 2 bytes | 64 KB |
011 | 0x18 | 3 bytes | 16 MB |
100 | 0x20 | 4 bytes | 4 GB |
101β111 | 0x28β0x38 | 5β7 bytes | up to 72 PB |
5.2 Fixed class (0x00β0x2F)
| Width | Identifiers |
|---|---|
| 0 bytes | 0x00 Null, 0x01 False, 0x02 True, 0x03 NotModified, 0x04 Infinity |
| 1 byte | 0x08 UInt8, 0x09 Int8, 0x0A Char8, 0x0B LocalResource8, 0x0C RemoteResource8, 0x0D LocalProcedure8, 0x0E RemoteProcedure8 |
| 2 bytes | 0x10 UInt16, 0x11 Int16, 0x12 Char16, 0x13 LocalResource16, 0x14 RemoteResource16, 0x15 LocalProcedure16, 0x16 RemoteProcedure16 |
| 4 bytes | 0x18 UInt32, 0x19 Int32, 0x1A Float32, 0x1B LocalResource32, 0x1C RemoteResource32, 0x1D LocalProcedure32, 0x1E RemoteProcedure32 |
| 8 bytes | 0x20 UInt64, 0x21 Int64, 0x22 Float64, 0x23 DateTime (ticks, signed 64-bit) |
| 16 bytes | 0x28 UInt128, 0x29 Int128, 0x2A Decimal128, 0x2B UUID (RFC 4122, 16 raw bytes) |
Multi-byte Fixed-class payload contents (integers, floats, DateTime ticks) are little-endian β see Β§5.5 for verified examples. 128-bit integers are two little-endian 64-bit halves, low word first.
5.3 Dynamic class (0x40β0x47)
| Base ID | Name | Description |
|---|---|---|
| 0x40 | RawData | Opaque byte array |
| 0x41 | String | UTF-8 text |
| 0x42 | List | Ordered list of heterogeneous TDUs |
| 0x43 | ResourceList | List of resource references |
| 0x44 | RecordList | List of typed records |
| 0x45 | ResourceLink | Resource path, as a string |
| 0x46 | Map | Keyβvalue map |
| 0x47 | MapList | Ordered list of maps |
5.4 Typed & Extension classes
| Identifier | Class | Name | Description |
|---|---|---|---|
| 0x80 | Typed | Typed | Record or enum value (built-in TypedList/TypedMap/Tuple use it too); embeds a TRU metadata block before its content β see Β§5.5 |
| 0x81 | Typed | TypeDef | A full type definition, returned in answer to a TypeDef query β see Β§5.7 |
| 0x82 | Typed | TRU | A standalone TRU value β see Β§5.5 |
| 0xC0 | Extension | TypeContinuation | Multi-part type reference β continuation fragment |
| 0xC1 | Extension | TypeOfTarget | Use the enclosing resource's own type as the type reference |
5.5 TRU (Type-Representation Unit)
A TRU describes the type of a value β either as a
self-contained shape (a primitive kind, or a built-in generic like a typed
list, map, or tuple) or as a compact reference to a type registered
elsewhere. In source, the class is named Tru; the one place in
either codebase that spells the abbreviation out (a comment in the .NET
implementation) calls it a "type-representation unit."
A TRU appears on the wire in exactly two places:
- as the entire payload of a standalone
TRU(0x82) TDU β used, among other places, for every property's, argument's, return value's, event's, and constant's declared value type inside aTypeDef(Β§5.7); - as a metadata block immediately following a plain
Typed(0x80) TDU's length prefix, describing the type of the record/enum/list/map/tuple value that follows it.
The TRU header byte is laid out as follows:
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|---|---|
| Nullable1 bit | Extended1 bit | Identifier / sub-count6 bits β meaning depends on Extended | |||||
Three shapes, selected by the Extended bit (bit 6):
| Extended | Shape | Wire layout |
|---|---|---|
| 0 | Primitive | The header byte alone is the whole TRU (1 byte total) β a bare scalar kind such as a string or a fixed-width number. |
| 1, sub-count = 0 | Type reference | Header byte + a little-endian numeric type-definition ID. ID width (1/2/4/8 bytes) is selected by which of the eight Local/Remote Γ 8/16/32/64 identifiers is used; Local vs. Remote selects whose Warehouse resolves the ID. |
| 1, sub-count 1β7 | Composite | Header byte + that many nested TRUs, back-to-back (a typed list has 1 nested TRU for its element type; a typed map has 2, for key and value; a tuple has 2β7, one per element). |
Confirmed identifiers for the reference and composite shapes:
| Identifier | Name | Shape |
|---|---|---|
| 0x40 / 0x41 | LocalType8 / RemoteType8 | Reference, 1-byte ID |
| 0x42 / 0x43 | LocalType16 / RemoteType16 | Reference, 2-byte ID |
| 0x44 / 0x45 | LocalType32 / RemoteType32 | Reference, 4-byte ID |
| 0x46 / 0x47 | LocalType64 / RemoteType64 | Reference, 8-byte ID |
| 0x48 | TypedList | Composite, 1 sub-TRU (element type) |
| 0x50 | Tuple2 | Composite, 2 sub-TRUs |
| 0x51 | TypedMap | Composite, 2 sub-TRUs (key, value) |
| 0x58 / 0x60 / 0x68 / 0x70 / 0x78 | Tuple3 β¦ Tuple7 | Composite, 3β7 sub-TRUs |
The full set of primitive-shape identifiers (bare scalar kinds below
0x40) isn't reproduced here β consult
TruIdentifier in either implementation's source for the
exhaustive list; the mechanism above is complete regardless.
5.6 Group-varint integer encoding (GVWIE)
When a TypedList or TypedMap's element TRU is one
of Int16/Int32/Int64/UInt16/UInt32/UInt64,
its elements are packed with a dedicated compaction codec both codebases
call GVWIE β a proper name, not an acronym expansion found
anywhere in either implementation's source, comments, or commit history.
(Int8/UInt8 lists have no such codec and fall
back to per-element encoding.) This is a payload sub-encoding, not a TDU or
TRU identifier of its own.
Each value is zig-zag encoded, then: a value whose zig-zagged magnitude
fits in 7 bits is written as a single literal byte with the top bit clear
("fast path"). A run of consecutive values that don't fit is grouped
behind one header byte packing a count and a shared byte width, followed by
that many values at that width, least-significant byte first. Exact bit
allocation for the grouped-run header differs slightly by integer width β
see GroupInt16Codec/GroupInt32Codec/GroupInt64Codec
(and their unsigned counterparts) in either implementation's source for the
precise formula.
Verified example β the payload of a TypedList<Int32> holding [1, 2, 3], taken from a golden test vector byte-for-byte identical in both test suites:
5.7 TypeDef
A TypeDef is the schema returned in answer to a
TypeDefById or TypeDefByResourceId request (Β§7):
the name, kind, base type, and full set of exported properties, methods,
events, and constants for a resource, record, enum, or function type.
Wire mechanism: a TypeDef (0x81) TDU's
payload is itself a complete, self-contained plain Typed
(0x80) TDU β own length prefix and TRU metadata included β whose content is
a sparse map from a one-byte field index to a dynamically-typed value.
Only fields that are actually present are written; there is no zero-fill
for absent ones, and a receiver ignores any field index it doesn't
recognize. This is what lets new optional fields be added at new indices
in the future without breaking existing parsers. Every member record below
(property, function, argument, event, constant) uses the same
indexed-map mechanism recursively.
| Index | Field | Type |
|---|---|---|
| 0x00 | Version | integer |
| 0x01 | Id | integer β the numeric type ID other TRUs reference |
| 0x02 | Name | string |
| 0x03 | Namespace | string |
| 0x04 | Kind | byte β see table below |
| 0x05 | Parent | integer β base type's numeric ID |
| 0x06 | Properties | list of property records |
| 0x07 | Functions | list of function records |
| 0x08 | Events | list of event records |
| 0x09 | Constants | list of constant records |
Indices 0x20β0x25 carry optional descriptive metadata (usage, description, example, category, since, and a string-to-string annotation map) β documentation aids, not load-bearing for interoperability.
Kind (field 0x04):
| Value | Name |
|---|---|
| 0 | Resource |
| 1 | Record |
| 2 | Enum |
| 3 | Function (defined; not currently produced by either reference implementation) |
Every property, function, argument, event, and constant record shares a base shape β index 0x00 (its own member index), 0x01 (name), 0x02 (flags), plus an optional documentation block from 0x20 onward β and adds its own fields from 0x03:
| Record | Own fields (from 0x03) | Flags (bit values) |
|---|---|---|
| Property | 0x03 ValueType (TRU), 0x04 OrderingControl, 0x05 HistoryControl, 0x06 DefaultValue | Inherited=1, Deprecated=2, ReadOnly=4, Constant=8, Volatile=0x10, Historical=0x20 |
| Function | 0x03 Arguments (list of Argument records), 0x04 ReturnType (TRU), 0x05 StreamMode | Inherited=1, Deprecated=2, Static=4, ReadOnly=8, Idempotent=0x10, Cancellable=0x20, Pausable=0x40 |
| Argument | 0x03 ValueType (TRU), 0x04 DefaultValue | Optional=1, Variadic=2 |
| Event | 0x03 ArgumentType (TRU), 0x04 ArgumentName, 0x05 OrderingControl, 0x06 HistoryControl | Inherited=1, Deprecated=2, AutoDelivered=4, Historical=8 |
| Constant | 0x03 ValueType (TRU), 0x04 Value | Inherited=1, Deprecated=2 |
5.8 Verified wire examples
Each of these is a complete TDU (identifier byte, any length bytes, payload), taken from byte-for-byte matching test vectors shared between the .NET and TypeScript test suites. See Β§5.6 above for a TRU/GVWIE example.
The Int32/Int64 examples above intentionally show a multi-byte payload: note the least-significant byte comes first. This is the opposite byte order from the TDU length-prefix field (Β§5.1), which is big-endian β the two fields are independent and use different conventions.
6. EP packet format
After the authentication phase completes, all traffic uses EP session packets. Every packet begins with a single header byte; Request and Reply packets additionally carry a 4-byte callback ID. Payloads, when present, are TDU-encoded (Β§5).
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|---|---|
| Method2 bits | hasTDU1 bit | Command5 bits | |||||
| Bits 7β6 | Method | Callback ID? | Notes |
|---|---|---|---|
00 | Notification | No β 1-byte minimum packet | Unsolicited; no reply expected |
01 | Request | Yes β 4-byte little-endian uint32 | 5-byte minimum packet |
10 | Reply | Yes β echoes the Request's callback ID | 5-byte minimum packet |
11 | Extension | No | Reserved β parsed by both implementations, emitted by neither (see Β§8) |
Verified example β a KeepAlive request (no payload) with callback ID 12:
7. Commands reference
Request commands
| Value | Name | Group | Description |
|---|---|---|---|
| 0x00 | InvokeFunction | Invoke | Call a method on a resource instance by index |
| 0x01 | SetProperty | Invoke | Set a resource property by index |
| 0x02 | Subscribe | Invoke | Subscribe to property/event change notifications |
| 0x03 | Unsubscribe | Invoke | Cancel a subscription |
| 0x08 | TypeDefIdsByNames | Inquire | Resolve numeric type IDs from class name strings |
| 0x09 | TypeDefById | Inquire | Fetch a full type definition by numeric ID |
| 0x0A | TypeDefByResourceId | Inquire | Fetch the type definition for an attached resource |
| 0x0B | Query | Inquire | Search resources by path pattern |
| 0x0C | LinkTypeDefs | Inquire | Get all type definitions reachable from a resource link |
| 0x0D | Token | Inquire | Request an authentication token |
| 0x0E | GetResourceIdByLink | Inquire | Resolve a resource path to its server-assigned numeric ID |
| 0x10 | AttachResource | Manage | Attach to a resource; server returns its full current state |
| 0x11 | ReattachResource | Manage | Re-sync after reconnection |
| 0x12 | DetachResource | Manage | Stop receiving updates for this resource |
| 0x13 | CreateResource | Manage | Instantiate a new resource on the server |
| 0x14 | DeleteResource | Manage | Destroy a server-side resource |
| 0x15 | MoveResource | Manage | Rename or relocate a resource |
| 0x18 | KeepAlive | Static | Connection heartbeat β no payload |
| 0x19 | ProcedureCall | Static | Call a named server-side procedure |
| 0x1A | StaticCall | Static | Call a static/class-level method β no instance required |
| 0x1B | IndirectCall | Static | Indirect invocation via a callable reference. Declared, not yet dispatched by the reference server β see Β§8. |
| 0x1C | PullStream | Static | Pull the next chunk from a server-side stream |
| 0x1D | TerminateExecution | Static | Abort an in-progress execution |
| 0x1E | HaltExecution | Static | Pause an in-progress (pausable) execution |
| 0x1F | ResumeExecution | Static | Resume a halted execution |
Reply commands carry the Request's callback ID
| Value | Name | Group | Description |
|---|---|---|---|
| 0x00 | Completed | Success | Operation completed; payload is the return value, if any |
| 0x01 | Propagated | Success | Result delivered to a downstream subscriber |
| 0x02 | Stream | Success | Operation returned a stream handle |
| 0x04 | PermissionError | Error | Request denied β insufficient permissions |
| 0x05 | ExecutionError | Error | Exception thrown during method execution |
| 0x08 | Progress | Partial | Intermediate progress update; request still pending |
| 0x09 | Chunk | Partial | One chunk of a streamed response; more may follow |
| 0x0A | Warning | Partial | Non-fatal warning; operation continues |
Notification commands no callback ID β unsolicited
| Value | Name | Group | Description |
|---|---|---|---|
| 0x00 | PropertyModified | Invoke | A subscribed property value changed |
| 0x01 | EventOccurred | Invoke | A subscribed event was raised |
| 0x08 | ResourceDestroyed | Manage | An attached resource was deleted on the server |
| 0x09 | ResourceReassigned | Manage | An attached resource was assigned a new server ID |
| 0x0A | ResourceMoved | Manage | An attached resource was renamed or relocated |
| 0x0B | SystemFailure | Manage | System-level failure affecting the connection |
8. Implementation notes
EP is specified so that independent implementations interoperate over the wire without a translation layer, and the two reference implementations agree on every packet, command, and TDU value covered above. As of this writing, a few areas are implemented in one reference implementation but not (yet) the other:
| Area | .NET | TypeScript |
|---|---|---|
| WebSocket subprotocol enforcement on accept | Required β rejects the upgrade with HTTP 400 if the client didn't request EP | Not currently validated β EpServer accepts the upgrade regardless of the requested subprotocol |
eps:// URI scheme | Not currently registered as a distinct handler | Maps to wss:// (TLS-protected WebSocket) |
Mid-session key rotation (KeyRotation / KeyRotationEstablished, Β§4.3) | Implemented, used by the PPAP authentication provider | Not yet implemented |
| Untrusted-input TDU length guards | Configurable maximum payload/allocation limits (WarehouseConfiguration) | No equivalent limit yet at the TDU-parsing layer |
EpPacketMethod.Extension (session packets, Β§6) and IndirectCall (Β§7) | Parsed if received; not currently emitted or dispatched | Not currently emitted |
None of this affects wire compatibility for the features both implementations do support today β it's listed here so implementers building a third party don't assume behavior that isn't universal yet.
9. Security considerations
EncryptionMode.None, a connection has no
identity verification and no application-layer confidentiality at all.
-
For any internet-facing deployment, use
InitializerIdentityorDualIdentityauthentication together withEncryptWithSessionKey(orEncryptWithSessionKeyAndAddress) encryption β or terminate the connection inside a TLS tunnel (awss://reverse proxy, or a TLS-wrapping proxy in front of raw TCP), since neither reference implementation currently wraps raw TCP in transport-layer TLS automatically (Β§8). - Implementations parsing TDUs from an untrusted peer should enforce a maximum declared length before allocating a buffer for the length prefix described in Β§5.1 β an attacker-controlled length field is an obvious resource-exhaustion vector otherwise.
-
A conforming WebSocket-hosting server should require and verify the
negotiated subprotocol is exactly
EP(ordinal, case-sensitive) before treating a connection as an EP session, even though not every implementation enforces this yet (Β§8). -
Stored password verifiers should be treated as sensitive as a plaintext
password: under the
password-sha3-v1protocol, possessing the stored verifier is sufficient to authenticate. -
ppap-mlkem768-v1is available where post-quantum resistance against key material is a requirement β see the PPAP paper [Zamil & Jasim, 2025] in Β§10.
10. References
- A. K. Zamil and A. D. Jasim, "Esiur: A Resource-Centric Distributed Runtime Framework for Real-Time State Synchronization," IEEE Access, vol. 14, pp. 107062β107091, 2026, doi: 10.1109/ACCESS.2026.3711683. [Online]. Available: ieeexplore.ieee.org/document/11601010 β the primary reference for Esiur's resource model and this protocol (Β§1)
- A. K. Zamil and A. D. Jasim, "A Multi-Factor Quantum-Resistant and Privacy-Preserving Authentication Protocol for Decentralized Systems," Mesopotamian Journal of CyberSecurity, vol. 5, no. 3, pp. 1272β1291, 2025. Accessed: Jul. 22, 2026. [Online]. Available: mesopotamian.press/journals/index.php/CyberSecurity/article/view/989 β the PPAP protocol behind
ppap-mlkem768-v1(Β§9) - RFC 6455 β The WebSocket Protocol
- RFC 4122 β UUID layout, referenced by the TDU
UUIDtype - github.com/esiur/esiur-dotnet β reference implementation, .NET
- github.com/esiur/esiur-ts β reference implementation, TypeScript