Antheos Protocol

This specification is stable. No implementation of it is published at present.

ANTHEOS_PROTOCOL_SPEC.md (Markdown, 126,945 bytes)

Version 1.11 — Level 1 Specification - Transport

© Are Bjørby are.bjorby@langsyn.org November 2026


Contents

  1. Introduction
  2. Design Principles
  3. Terms and Definitions
  4. Protocol Encoding
  5. Identifier Types
  6. Word Encoding
  7. Message Frame Structure
  8. Level 1 Symbols
  9. Bus Operations
  10. Service Operations
  11. Session Operations
  12. Bus Transmission Rules
  13. Standard Exception Codes
  14. Conformance Requirements
  15. Security Considerations
  16. Licensing

1. Introduction

Antheos (Ant) is an anonymous, lightweight, decentralized communication protocol with a wrap-and-tunnel philosophy and a bus abstraction that utilizes existing hardware and software. It is anonymous in a narrow sense: an instance needs no identity to communicate. Its BID is random and lasts one bus connection (Section 5.4), and Level 1 does not expose its OID:DID:IID, whose exposure is left to the device's designer (Section 5). It is designed to operate on any transport — from a UART serial line to a TCP socket, from a LoRa radio to a human carrying a USB drive.

This document defines the Level 1 Specification - Transport: the complete, self-contained procedural protocol covering bus operations, service discovery, and session management. Every verb does exactly one thing. It runs on all devices from MCU to server cluster.

Level 1 is the foundation for higher-level protocols.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals, as shown here.

1.1 Design Context: Object-Oriented Hardware — the O³ Vision

(This section is non-normative. It records the design intent behind the protocol; a conformant implementation depends only on the normative sections that follow.)

Antheos exists to let independent devices exchange services without a central operating system. The motivating observation is that a computer is already a collection of devices sharing a chassis — processor, memory, storage, network, sensors — and that as each device acquires its own compute, the operating system's historical role narrows. A traditional operating system answers a scarcity question: one expensive processor multiplexed across many tasks by a privileged arbiter. When every device is itself a processor, the machine is better understood as a small network of peers, and what those peers need is not a kernel but a shared communication law. Antheos is that law. This is why Design Principle 1 (Section 2) admits only devices and buses — no controller, no central authority.

The same protocol spans scales because the model is uniform at every scale: on every bus, whether it joins logical devices inside one chassis, physical devices on a shared wire or services across a network, instances are addressed and discovered identically. This cross-scale uniformity — one object model holding from an on-die component out to a remote service — is what this design names O³ (Object-Oriented Omni-scale), pronounced "object cubed": object-oriented in the messaging sense (peers communicate; no shared mutable state) and omni-scale because the single law holds at every distance. Only the transport changes with distance — shared memory within a machine, a socket across a network, a radio between devices — while the law above it does not. The identity model (Section 5) anticipates this: a device's DID and IID are hardwired — carried by the device, not assigned by the network — which is the shape fully object-oriented hardware takes natively.

On conventional hardware, where the processor and memory are still physically shared, this model is realized by a thin layer beneath Antheos that presents the shared resources as logical devices speaking the protocol. Such a layer need not interpose on every operation: a peer negotiates access to a resource once over Antheos (the control path), then uses the granted resource directly at hardware speed (the data path). The protocol is the control plane; raw access is the data plane. This separation is the same one found in capability-export kernels and in production paravirtualized device interfaces.

Reduced to essentials, the authority such a layer must hold is small: the power to grant access to a shared resource, and the power to reclaim it from a peer that does not yield. Everything else — isolation enforcement, stable identity, the bus itself — is either delegated to the participating devices or carried by the fabric they share. Grant and reclaim are physical-layer concerns; they do not reintroduce a central authority over meaning, which is what Principle 1 forbids. As hardware becomes natively object-oriented, even these responsibilities migrate from software into the interconnect — and the operating system, in the form Antheos assumes away, is gone.


2. Design Principles


3. Terms and Definitions

Term Definition
Text Printable ASCII characters, 0x20 to 0x7E (§6.2.4)
Blob Binary data (arbitrary bytes)
Instance Device, software, or HID endpoint communicating on a network
Network Instances connected to one or more buses
Bus Device, software, system, or person facilitating instance communication
Bus connection An instance's attachment to one bus, from establishing its BID there (Section 9.1) until it disconnects
Gateway An instance on more than one bus through which a BID on another bus is reached (Section 9.6)
Routing table An instance's own record of the BID through which another BID is reached (Section 9.3.3)
Message A head of words followed by an optional binary tail
Head The CP437 portion of a message, containing words
Word A typed, delimited unit within the head
Tail Optional binary data following the head
Flag A qualifier within a word (radix or unit)
Body The content portion of a word
Session A separate group of related messages between instances
Opener The instance that opened a session or, after a Resume, the one that resumed it (Section 11.1.2)
Trace identifier A plain ID word a Call MAY carry after its MID, which its sender chooses (Section 11.3)
Service A capability an instance offers to accomplish a specific task, found by Query and taken up by Accept (Section 10)
Origin Source, creator, or owner of instances
Id A text or number used to identify objects
Registry An entity maintaining a list of identifiers

4. Protocol Encoding

Antheos uses CP437 (Code Page 437), the character encoding developed by IBM for the original PC in 1981. CP437 is a single-byte encoding defining 256 characters. The first 128 (0x00–0x7F) align with ASCII. The upper 128 (0x80–0xFF) include accented letters, Greek symbols, and box-drawing characters.

A message's head is human-readable on any device that displays CP437's glyphs, those of the control range included, and its tail is binary. The protocol reserves 7 bytes from the CP437 control character range (0x00–0x1F) as structural delimiters.

4.1 Reserved Control Characters

All reserved bytes were selected to avoid collisions with ASCII flow control (XON 0x11, XOFF 0x13), terminal escapes (ESC 0x1B), whitespace (TAB 0x09, LF 0x0A, CR 0x0D), and null terminators (0x00). A head never holds these bytes, so it passes through software flow control and C string handling. A tail may hold any byte (Section 7.3), and a bus carries every byte unchanged (Section 12), so a link that carries Antheos MUST be 8-bit clean: without software flow control, and in raw mode on a terminal line. In its default mode, a POSIX terminal acts on 0x03 (INTR), 0x04 (EOF), 0x12 (REPRINT) and 0x1A (SUSP), and sounds 0x07 as its bell.

Hex Abbr CP437 Glyph Role
0x02 SOM ☻ Start of Message
0x03 EOM ♥ End of Message
0x04 SOR ♦ Start of Radix qualifier
0x07 SOU • Start of Unit qualifier
0x10 EOW ► End of Word
0x12 SOW ↕ Start of Word
0x1A SOB → Start of Body

These 7 bytes are forbidden in word bodies. In the head their presence always indicates protocol structure, enabling stream parsing without buffering entire messages. The tail is length-delimited (Section 7.3), so a tail block, a STRING's included, may hold any byte.


5. Identifier Types

Antheos uses a layered identity model. Each identifier type has a defined scope, format, and lifetime. The layers build from organizational identity (OID) down to per-message sequencing (MID).

Level 1 carries the BID, SID and MID. It carries no OID, DID or IID, and an instance needs none of them to conform: they are defined here for a device's designer, who controls their exposure (Section 5.3).

Id Name Scope Format Lifetime
OID Origin Origin ASCII Origin-defined, or the registry's
DID Device Origin ASCII Origin-defined (hardwired type identifier)
IID Instance Origin ASCII Origin-defined (hardwired serial)
BID Bus Bus Base-32 Ephemeral (established at bus connection)
SID Session Instance Base-32 Session duration (fresh per session)
MID Message Session Decimal Message duration (sequential counter)

5.1 OID (Origin Identifier)

The OID identifies the origin — the organization, individual, or entity that created and owns instances. An OID is a plaintext ASCII string. Registering one is optional.

5.2 DID (Device Identifier)

The DID identifies the device type within an origin's namespace. It is a hardwired, human-readable ASCII string (e.g., "Thermostat", "SensorV2"). Defined by the origin, not the network.

5.3 IID (Instance Identifier)

The IID identifies a specific instance of a device type. It is a hardwired unique serial (e.g., "SN00482").

An instance's connection to a bus is OID to BID: the BID is generated at random on every local bus, so the instance stays anonymous there (Section 5.4), and Level 1 exposes nothing of its OID:DID:IID. A hash of OID:DID:IID:BID gives a globally unique id, and registering the OID in an Antheos namespace guarantees that uniqueness within the namespace. Whether a device has such an id, which hash makes it and who sees it are left to the device's designer: Level 1 neither computes it nor carries it.

5.4 BID (Bus Identifier)

The BID is a base-32 address, generated at random and negotiated at bus connection through the Establish/Conflict protocol (see Section 9.1). BIDs are ephemeral and local to a single bus. They are kept as short as possible, growing only on collision. A device connected to multiple buses holds a separate BID on each. Carried in an ID word with radix flag U (duotrigesimal).

5.5 SID (Session Identifier)

The SID identifies a session between instances. It is a base-32 hash of a counter and device entropy (Section 11.2). Every new session receives a fresh SID: an instance never hashes the same device entropy and counter together twice, and never issues a SID a session holds. Carried in an ID word with radix flag U (duotrigesimal).

5.6 MID (Message Identifier)

The MID is a decimal counter that increments sequentially within a session, starting at 1, up to at most 4294967295 (2³²−1). MID=0 is the wrap signal (see Section 11.1.2). Carried in an ID word with radix flag D (decimal).

5.7 Identifier Encoding Summary

The identifiers Level 1 carries are carried in ID words (@), in one of three forms. An ID word in any other form, with any other radix flag included, is an invalid word (§6.1). Unit flags are never used with ID words.

Identifier Radix Wire Example
BID U (base-32) ↕@♦U→4T9X2►
SID U (base-32) ↕@♦U→A7K2M►
MID D (decimal) ↕@♦D→1►
Trace (none) ↕@→trace-7f3a►

BID/SID Disambiguation: Both BID and SID use base-32 encoding with radix flag U. The verb scope determines which identifier type is expected:

A receiver determines identifier type by the verb in the SYMBOL word, and for an Exception by its reason, not by inspecting the identifier value itself.


6. Word Encoding

A word is the fundamental data unit within an Antheos message head. Each word is delimited by SOW (0x12) and EOW (0x10), with typed content between them.

6.1 Word Structure

The general word structure is:

[SOW] [WT] ([SOR][RF]) ([SOU][UF]) [SOB] Body [EOW]

WT (Word Type) is always the first byte after SOW. SOR/RF (Radix Flag) and SOU/UF (Unit Flag) are conditionally present depending on the word type. SOB marks the start of the body content. EOW terminates the word. Appendix B gives this structure, with each word type's flags and body, as a grammar.

Flag requirements vary by word type:

Category Radix (SOR/RF) Unit (SOU/UF) Word Types
Radix + Unit required Required Required INTEGER (#), REAL ($), SCIENTIFIC (%), BLOB (*), COMPLEX (|), STRING (")
Radix optional, Unit forbidden Optional Forbidden ID (@)
Both forbidden Forbidden Forbidden SYMBOL (!), PATH (/), TEXT (~), LOGICAL (?), DATETIME (&), DURATION (:), BOOL (=), NULL (_)

A body names exactly one value, and each value has one spelling for its word type, radix and unit; the same value in another radix is another word (e.g., #♦D•B→10 and #♦H•B→A). A body has at least one character, except a TEXT word's, which may be empty (the empty text, §6.2.4), and a NULL word's, which is always empty (§6.2.11).

A body that breaks the rules of its word type (§6.2) makes the word invalid, and a receiver treats it as a malformed word (§7.4).

6.2 Word Types

CP437 Hex Type Description Flags
! 0x21 SYMBOL Protocol verbs and markers None
@ 0x40 ID Identifiers (BID, SID, MID, trace) Radix optional
/ 0x2F PATH Routing sequences None
~ 0x7E TEXT Plain text in printable ASCII, §6.2.4 None
# 0x23 INTEGER Signed integer value, §6.2.5 Radix + Unit
$ 0x24 REAL Binary floating-point value, held exactly, §6.2.6 Radix + Unit
% 0x25 SCIENTIFIC A number carrying its significant figures, §6.2.7 Radix + Unit
? 0x3F LOGICAL Logical expression, §6.2.1 None
& 0x26 DATETIME ISO 8601 date and time in UTC, §6.2.2 None
* 0x2A BLOB Tail block size in bytes, §6.2.8 Radix + Unit
= 0x3D BOOL Truth value, §6.2.9 None
: 0x3A DURATION ISO 8601 length of time, §6.2.3 None
| 0x7C COMPLEX Complex number, §6.2.10 Radix + Unit
_ 0x5F NULL Explicit absent value, §6.2.11 None
" 0x22 STRING Unicode string in UTF-8, carried in the tail, §6.2.12 Radix + Unit

SYMBOL, ID and PATH bodies are defined where they are used: SYMBOL in Section 8, ID in Section 5 and PATH in Section 9.3. Every other type's body is defined below.

6.2.1 LOGICAL

A logical expression over variables and constants. The whole expression is a truth value, so A + 1 is invalid.

Characters: A–Z, a–z, 0–9, &, |, ^, ~, -, >, =, (, ), <, +, *, / and a space, one on each side of an operator between two operands (e.g., A & B).

Variables and constants: a variable is a single letter, and A and a are different variables. A LOGICAL word carries the expression, not its value: what each variable names is set by the message that carries the word, as a Scaleback names a flag by its letter (§9.2.2). Each operand is a truth value or a number, by the operator it belongs to: those of ~, &, |, ^ and -> are truth values, those of -, +, *, /, < and > are numbers, and < and > give a truth value. A variable is one of the two throughout an expression. A constant is written in decimal digits with no leading zero: a number where a number is expected (e.g., (A + 2) < B), and 0 for false or 1 for true where a truth value is.

Numbers: exact. A number is the exact value its constant or variable names, and +, -, * and / are exact rational arithmetic, so nothing rounds or overflows (e.g., (7 / 2) > 3 and ((1 / 3) * 3) = 1 are both true). A division by zero makes the whole expression false.

Words a variable names: when a variable names a word, a number variable may name an INTEGER, a REAL or a SCIENTIFIC, and stands for the exact value it holds, a SCIENTIFIC's being its magnitude alone: figures do not count, so A = B is true when A names 6.02E23 and B names 6.020E23 (§6.2.7). A truth variable may name a BOOL. No other word type may be named, a COMPLEX included.

Operators:

Operands of ~ and negation: ~ and negation - apply to a variable, a constant, or two operands joined by an operator in parentheses, and to nothing else: ~A, ~1, -2 < A and ~(A & B) are valid, and ~~A, --A and ~(~A) are not. The whole expression is never in parentheses, so (A & B) is not valid.

Examples: A & B, ~A | B, (A & B) -> (C ^ D) (if A and B are true, C and D differ), ~H & ~q (a Scaleback's: neither hex nor quadword).

6.2.2 DATETIME

An ISO 8601 date and time in UTC: YYYY-MM-DDTHH:mm:SSZ.

Characters: 0–9, -, ., T, : and Z.

Fields:

Variants: the seconds may carry a decimal fraction of 1 to 9 digits, with no trailing zero (e.g., 2025-04-03T12:34:56.789Z, not 2025-04-03T12:34:56.7890Z or 2025-04-03T12:34:56.0Z).

Examples: 2025-04-03T12:34:56Z, 2023-11-15T09:00:00Z.

6.2.3 DURATION

A length of time, positive, negative or zero, to the nanosecond: an ISO 8601 duration in days, hours, minutes and seconds, PnDTnHnMnS, with no bound on the days. A day is 24 hours, an hour 60 minutes and a minute 60 seconds, so a duration has a fixed length; years and months are not used, since theirs vary, and a week is written as 7 days.

Characters: 0–9, -, ., P, D, T, H, M and S.

Terms: P, then these, in order, each written only when its number is not zero, every number in decimal with no leading zero:

Each term holds only what the next larger cannot, so each duration has one spelling (e.g., PT1H30M, not PT90M, and P1DT12H, not PT36H), and only the seconds carry a fraction (e.g., PT1H30M, not PT1.5H). The zero duration is PT0S, the one spelling that writes a zero term, and it has no sign (not -PT0S). A negative duration is written with - before P (e.g., -PT2H), the sign ISO 8601-2 adds: ISO 8601-1's durations have none.

Examples: PT1H30M, P1DT12H, P7D, PT0.5S, -PT2H, PT0S.

6.2.4 TEXT

Characters: ASCII excluding control characters, which is 0x20 (space) to 0x7E (~). Text past ASCII is carried by STRING (§6.2.12).

Values: any run of these characters, the empty one included: a TEXT word with an empty body is the empty text.

6.2.5 INTEGER

The value has no leading zero, and zero has no sign (e.g., 10 and 0, not 010, 00 or -0).

Characters: the digits of the word's radix (§6.3), and - before a negative value.

Unit: the value's width, B 8, W 16, D 32 or Q 64 bits; a value of n bits is from -2^(n-1) to 2^(n-1)-1 (e.g., B from -128 to 127), and one outside its range is invalid. M, G and T are not allowed.

6.2.6 REAL

Any real value its format holds (see Unit), positive or negative, large or small, integers and fractions included.

The value is written in positional notation, with no exponent: E-notation is SCIENTIFIC's (§6.2.7). The integer part has no leading zero, a lone 0 before the point aside (e.g., 10 and 0.5, not 010 or .5), the fractional part has no trailing zero (e.g., 1.5 and 1, not 1.50 or 1.0), and -0 is the negative zero the IEEE format holds.

Characters:

Unit: the IEEE 754 binary format the value is held in: W binary16, D binary32, Q binary64. B is not allowed, since there is no 8-bit format, and neither are M, G and T. A value the format does not hold exactly is invalid, and is not rounded, so a REAL names exactly the value it is held as (e.g., $♦D•W→0.5 is valid and $♦D•W→0.1 is not; binary16's nearest to 0.1 is 0.0999755859375). A value beyond the format's finite range is therefore invalid, as is a nonzero value smaller than the least the format holds, and a subnormal value the format holds is valid.

6.2.7 SCIENTIFIC

A number carrying its significant figures, in scientific notation, a x 10^b, written in E-notation, 1.23E2 for 1.23 x 10^2. The figures are part of the value: 6.02E23 and 6.020E23 are the same magnitude but different values. The coefficient is not zero, since significant figures are counted from its first nonzero digit and a zero has none (e.g., 0.0E0 is invalid).

The coefficient is written with one nonzero digit, followed, when it has more than one figure, by the point and the rest (e.g., 6E23 for one figure, 6.0E23 for two, and 1.23E2, not 12.3E1 or 0.123E3), so every digit it carries is a significant figure and each value has one spelling. The exponent has no leading zero, and a zero exponent has no sign (e.g., 1.5E0, not 1.5E00 or 1.5E-0).

Characters:

Radix: decimal (D) only, since every SCIENTIFIC value carries an exponent.

Unit: the IEEE 754 decimal format the value is held in, which keeps its figures: D decimal32, Q decimal64. B and W are not allowed, since there is no 8- or 16-bit decimal format, and neither are M, G and T. The format holds a value as its figures, read as an integer, times ten to the exponent of its last figure, q (e.g., 1.23E2 is 123 with q 0, and 6.0E23 is 60 with q 22): decimal32 holds 7 figures with q from -101 to 90, and decimal64 16 figures with q from -398 to 369. A value with more significant figures than its format holds, or whose q is outside that range, is invalid; it is not rounded, since its figures are part of the value.

Examples: 6.02E23, 1.6E-19, 6E23.

6.2.8 BLOB

The size in bytes of the tail block the word declares (§7.3): a count from 0 to the unsigned maximum of its unit, with no leading zero.

Characters: the digits of the word's radix (§6.3).

Unit: the count's width, B 8, W 16, D 32 or Q 64 bits; a count of n bits is from 0 to 2^n-1, and one outside its range is invalid. M, G and T are not allowed.

An invalid BLOB word breaks the frame, since the length of the tail cannot be known from it (Section 7.4): *♦D•B→300 (beyond its unit), *♦D•M→1 (a unit no BLOB takes) and *♦H•B→0FF (a leading zero) each break it.

6.2.9 BOOL

A truth value.

Characters: 0 and 1.

Values: the body is one character, 0 for false or 1 for true.

6.2.10 COMPLEX

A complex number: a real part and an imaginary part, each any real value its format holds (see Unit), as a REAL's is (§6.2.6).

The body is the real part, then the imaginary part's sign, - if it is negative and + if not, then the imaginary part without its sign, then j (e.g., 3+4j and 1.5-0.25j). Each part is written as a REAL's body is, in positional notation with no exponent, so -0 is the negative zero in either (e.g., -0-0j). Both parts and the sign between them are always written, so each value has one spelling (e.g., 0+4j, not 4j, and 3+0j, not 3).

Characters:

Unit: the IEEE 754 binary format each part is held in, as a REAL's (§6.2.6): W binary16, D binary32, Q binary64, and B, M, G and T are not allowed. Both parts are held in the one format, and a part the format does not hold exactly is invalid, and is not rounded (e.g., |♦D•W→0.5+1j is valid and |♦D•W→0.1+1j is not).

Examples: 3+4j, 0.5-1.5j, 0+1j, -0-0j.

6.2.11 NULL

An explicit absent value: a NULL word states that a value is absent, and is not the empty text, a zero or false (§6.2.4, §6.2.5, §6.2.9).

Characters: none.

Values: one, whose body is empty: [SOW] _ [SOB] [EOW] is the NULL word.

6.2.12 STRING

A string of any Unicode characters, in UTF-8 (RFC 3629), where a TEXT word holds ASCII alone (§6.2.4). The string is not the word's body but its block in the tail, as a BLOB's data is (§7.3), and the body is the string's size in bytes, written as a BLOB word's body is, a count from 0 to the unsigned maximum of its unit with no leading zero (§6.2.8); so no UTF-8 enters the head, and an instance that never reads a string still knows the tail's length from its size, as it does from a BLOB's.

Characters: in the body, the digits of the word's radix (§6.3); in the block, the code points U+0000 to U+10FFFF except the surrogates U+D800 to U+DFFF, each in its one UTF-8 encoding, of one to four bytes.

Values: any run of those code points, the empty one included: "♦D•B→0, with an empty block, is the empty string. A string is not normalized: the same letter in other code points is another string (e.g., é as U+00E9, the bytes 0xC3 0xA9, and as e then U+0301, the bytes 0x65 0xCC 0x81).

A block that is not well-formed UTF-8, as one holding an overlong form, a surrogate, a code point past U+10FFFF, a byte that begins no sequence or a sequence the block's end cuts off, makes the word invalid, and its message malformed (Section 7.4). Its size, and so the tail, is known, so a receiver reads past the block. A STRING word whose flags or body are invalid breaks the frame, as an invalid BLOB word does (§6.2.8).

Examples: "♦D•B→5 with the block hello, "♦D•B→6 with héllo, "♦D•B→0 with an empty block.

6.3 Radix Flags

Specifies the base encoding of a numeric body. Follows the SOR (0x04) delimiter.

Flag Name Base Digits
I Binary Base 2 0–1
O Octal Base 8 0–7
D Decimal Base 10 0–9
H Hexadecimal Base 16 0–9, A–F
U Duotrigesimal Base 32 0–9, A–V

Letters are upper case. These are the digits of a numeric word's body (§6.2); the base-32 identifiers of Section 5 are identifiers, not numeric values, and have an alphabet of their own (Section 5.7).

6.4 Unit Flags

Specifies the data width of a numeric value. Follows the SOU (0x07) delimiter. Required for INTEGER, REAL, SCIENTIFIC, BLOB, COMPLEX and STRING word types. Forbidden for all other types including ID. Each word type takes only the units its rules name (§6.2), and none takes M, G or T, which are reserved.

Flag Name Size
B Byte 8 bits
W Word 16 bits
D Doubleword 32 bits
Q Quadword 64 bits
M Megabyte 2²⁰ bytes
G Gigabyte 2³⁰ bytes
T Terabyte 2⁴⁰ bytes

6.5 Word Encoding Examples

Each example shows the pseudo form (using abbreviations) and the wire form (using CP437 glyphs). In wire form: ↕=SOW, ♦=SOR, •=SOU, →=SOB, ►=EOW.

SYMBOL word (verb "Establish"):

Pseudo:  [SOW] ! [SOB] E [EOW]
Wire:    ↕!→E►

No flags. Body is a single character.

ID word (ASCII, no radix — trace identifier):

Pseudo:  [SOW] @ [SOB] example [EOW]
Wire:    ↕@→example►

Plain ASCII identifier. No radix or unit flags.

ID word (base-32 radix — BID):

Pseudo:  [SOW] @ [SOR]U [SOB] 4T9X2 [EOW]
Wire:    ↕@♦U→4T9X2►

Radix flag U (duotrigesimal). No unit flag.

ID word (decimal radix — MID):

Pseudo:  [SOW] @ [SOR]D [SOB] 1 [EOW]
Wire:    ↕@♦D→1►

Radix flag D (decimal). No unit flag.

TEXT word:

Pseudo:  [SOW] ~ [SOB] temperature [EOW]
Wire:    ↕~→temperature►

No flags. Body is printable ASCII per §6.2.4.

PATH word (dot-separated BID sequence):

Pseudo:  [SOW] / [SOB] AA.BB.CC.DD [EOW]
Wire:    ↕/→AA.BB.CC.DD►

No flags. Body is a dot-separated sequence of BIDs forming a route.

INTEGER word (decimal, byte-width, value 32):

Pseudo:  [SOW] # [SOR]D [SOU]B [SOB] 32 [EOW]
Wire:    ↕#♦D•B→32►

Radix D (decimal), Unit B (byte). Both required.

INTEGER word (hex, doubleword, value FF00):

Pseudo:  [SOW] # [SOR]H [SOU]D [SOB] FF00 [EOW]
Wire:    ↕#♦H•D→FF00►

Radix H (hex), Unit D (doubleword).

INTEGER word (decimal, word-width, negative):

Pseudo:  [SOW] # [SOR]D [SOU]W [SOB] -273 [EOW]
Wire:    ↕#♦D•W→-273►

Signed, within -32768 to 32767 for unit W, per §6.2.5.

REAL word (decimal, binary16, value 0.5):

Pseudo:  [SOW] $ [SOR]D [SOU]W [SOB] 0.5 [EOW]
Wire:    ↕$♦D•W→0.5►

Unit W (binary16), which holds 0.5 exactly, per §6.2.6.

SCIENTIFIC word (decimal, decimal32):

Pseudo:  [SOW] % [SOR]D [SOU]D [SOB] 6.02E23 [EOW]
Wire:    ↕%♦D•D→6.02E23►

Three significant figures, unit D (decimal32), per §6.2.7.

LOGICAL word (exclusion expression):

Pseudo:  [SOW] ? [SOB] ~H & ~q [EOW]
Wire:    ↕?→~H & ~q►

No flags. Body is a logical expression per §6.2.1 — in a Scaleback, neither hex nor quadword.

BLOB word (hex, doubleword, declaring 6656 byte tail block):

Pseudo:  [SOW] * [SOR]H [SOU]D [SOB] 1A00 [EOW]
Wire:    ↕*♦H•D→1A00►

Radix H, Unit D. Declares a tail block of 0x1A00 (6656) bytes.

DATETIME word:

Pseudo:  [SOW] & [SOB] 2026-02-08T12:00:00Z [EOW]
Wire:    ↕&→2026-02-08T12:00:00Z►

No flags. Body is an ISO 8601 date and time in UTC per §6.2.2.

DURATION word:

Pseudo:  [SOW] : [SOB] P1DT12H [EOW]
Wire:    ↕:→P1DT12H►

No flags. Body is an ISO 8601 duration per §6.2.3: one day and twelve hours, not PT36H.

BOOL word:

Pseudo:  [SOW] = [SOB] 1 [EOW]
Wire:    ↕=→1►

No flags. True, per §6.2.9.

COMPLEX word (decimal, binary32):

Pseudo:  [SOW] | [SOR]D [SOU]D [SOB] 3+4j [EOW]
Wire:    ↕|♦D•D→3+4j►

Both parts held in binary32, per §6.2.10.

NULL word:

Pseudo:  [SOW] _ [SOB] [EOW]
Wire:    ↕_→►

No flags. The body is always empty, per §6.2.11.

STRING word (decimal, byte-width, declaring a 6-byte tail block):

Pseudo:  [SOW] " [SOR]D [SOU]B [SOB] 6 [EOW]
Wire:    ↕"♦D•B→6►

The block in the tail holds héllo in UTF-8, six bytes, per §6.2.12.


7. Message Frame Structure

7.1 Frame Layout

[SOM] Word₁ Word₂ ... Wordₙ [EOM] Tail₁ Tail₂ ...

A message begins with SOM (0x02) and the head ends with EOM (0x03). The head contains one or more words, each delimited by SOW/EOW. The optional tail follows EOM and consists of binary data blocks. Appendix B gives a message's syntax as a grammar.

7.2 Head

The head is the structured, CP437-encoded portion of the message. It contains words that specify the operation, addressing, parameters, and any tail size declarations. Words are parsed sequentially as they arrive — a receiver does not need to buffer the entire head before processing.

7.3 Tail

The tail is optional binary data following EOM. It is partitioned into blocks whose sizes are declared by BLOB (*) and STRING (") words in the head, indexed by their position among those words. The first such word declares the size of the first tail block, the second the second block, and so on. A BLOB block is arbitrary bytes; a STRING block is a string in UTF-8 (§6.2.12). A receiver that has parsed the head knows the exact byte count of the tail before the first tail byte arrives.

BLOB Word Semantics: The BLOB body is a number representing the byte count of the corresponding tail block, per §6.2.8. The radix flag specifies the number's encoding (decimal, hex, etc.) and the unit flag specifies the numeric range (B = 8-bit, W = 16-bit, D = 32-bit, Q = 64-bit). A STRING body is the same count for its block. For example, *♦D•B→10 declares a 10-byte block; *♦H•D→1A00 declares a 6656-byte block.

Example: A message with two BLOB words in the head:

[SOM] ...words... [SOW]*[SOR]H[SOU]D[SOB]100[EOW] [SOW]*[SOR]H[SOU]D[SOB]200[EOW] [EOM] <256 bytes> <512 bytes>

Both BLOB words use unit D (doubleword / 32-bit size field) and radix H (hexadecimal). The first declares 0x100 = 256 bytes. The second declares 0x200 = 512 bytes. Total tail length is 768 bytes.

BLOB and STRING words are positional within a message head. Each declares exactly one tail block of its own, indexed by its ordinal position among the head's BLOB and STRING words. A tail shorter than the sum of their declared sizes makes the message malformed. A receiver detects that only when the bytes stop before the tail is complete, and then drops the message (Section 7.4).

7.4 Stream Parsing

Antheos is designed for stream parsing. A receiver processes one byte at a time:

  1. Wait for SOM (0x02). All bytes before SOM are discarded.
  2. Parse words as SOW/EOW pairs arrive. Each word is self-describing: word type, optional radix flag, optional unit flag, and body content.
  3. On EOM (0x03), the head is complete. Sum the sizes declared by any BLOB and STRING words to determine the total tail length.
  4. Read tail bytes until the declared total is reached. The next SOM starts a new message.

A receiver that cannot process a message drops it, and how it finds the next one depends on whether the frame is whole. A word whose flags or body its type's rules do not admit (§6.1, §6.2), other than a BLOB or STRING word, makes the message malformed but leaves the frame whole: the receiver reads on to EOM, skips the tail the message's BLOB and STRING words declare (Section 7.3), and the next byte is outside the message. A word of a type the receiver does not implement leaves the frame whole in the same way, and its message is dropped: every instance recognizes the fifteen word types of §6.2, though it need not implement them all (Section 14.1). The frame is broken when a byte that must be a delimiter or a flag is not one, when the byte after SOW is none of the fifteen word types, when a word holds a reserved byte (Section 4.1), or when a BLOB or STRING word has flags or a body its rules do not admit (§6.2.8, §6.2.12), since the tail it declares cannot then be trusted, and the receiver then abandons the frame and scans forward for the next SOM byte. A message whose BLOB and STRING words declare more than 2⁶⁴−1 bytes of tail in all breaks the frame as well. A receiver MAY break the frame when the tail a message declares is longer than it will read, or MAY read past that tail with the frame whole (Section 15). It MAY answer a message beyond its limits, such as one too long for its buffer, with a Scaleback (Section 9.2), and one holding a word of a type it does not implement with !X UNSUPPORTED_TYPE if it implements Exception (Section 13). It MAY answer a malformed message, such as one with an invalid word (§6.1), with !X MALFORMED_FRAME if it implements Exception (Section 13), and never answers one with a Scaleback.

Because SOM is a reserved byte (Section 4.1) that cannot appear inside a head word, a SOM encountered before the current frame's EOM is unambiguously the start of a new message. A stream parser abandons the incomplete frame and resynchronizes at this SOM — consistent with step 1, only the bytes before the SOM are discarded; the SOM itself begins the new frame — rather than consuming it and waiting for a later SOM (which would also discard the frame the in-head SOM begins). This does not apply within the tail: tail bytes are length-delimited binary (Section 7.3), so a 0x02 byte there is data, not a delimiter, and is consumed as part of the declared block.

7.5 Messages Without Tail

A message with no BLOB or STRING words in the head has no tail. The byte immediately following EOM is either the SOM of the next message or non-message bus traffic. This is the common case for control messages (Establish, Conflict, Ping, etc.).

7.6 Word Order

Words within a message head MUST appear in the following order:

  1. SYMBOL word — exactly one, always the first word.
  2. ID words — zero or more: those the verb's section defines, in the order it defines them (e.g., a session message's SID, sender BID, target BID and MID, Section 11).
  3. Payload words — zero or more, in the order the verb's section defines where it defines one (e.g., a Scaleback's, Section 9.2.2, and a path-addressed message's index and PATH last, Section 9.3), and otherwise in application-defined order.

7.7 Text Payloads

Some payload words carry text of the sender's own: the text of a Broadcast (Section 9.4) or a Relay (Section 9.3.2), the capability of a Query, an Offer, an Accept or a SERVICE_UNKNOWN Exception (Section 10), and the request, response or event of a Call (Section 11.3) or a Notify (Section 11.5). Such a payload is a TEXT word when TEXT can hold it, which is when it is printable ASCII (§6.2.4), and otherwise a STRING word, whose block in the tail is the text in UTF-8 (§6.2.12). A receiver reads the two alike: the payload is the text, whichever word carries it, and a STRING holding text TEXT could hold is read as that text. A sender writes as TEXT all the text TEXT can hold; the examples in Sections 9 to 11 are printable ASCII and are TEXT words.

The other TEXT words carry protocol tokens, which are printable ASCII and are always TEXT: the nonce of an Establish (Section 9.1.1), the reason of an Exception (Section 13) and the state of a Status (Section 11.4).

Bytes that are not UTF-8 are not text, and are carried by a BLOB (§6.2.8).

Pseudo:  [SOM] [SOW]![SOB]N[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]D[SOB]3[EOW] [SOW]"[SOR]D[SOU]D[SOB]12[EOW] [EOM] <12 bytes: température>
Wire:    ☻↕!→N►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→3►↕"♦D•D→12►♥température

Session Notify from 4T9X2 to 7M3K9: SID=A7K2M, MID=3, event="température", which TEXT cannot hold. The twelve bytes after EOM are the event in UTF-8, the STRING's block in the tail.


8. Level 1 Symbols

Level 1 defines three scopes of operation: bus, service, and session. Symbols are single ASCII characters following the SYMBOL word type (!). Every symbol is unique across all scopes. No disambiguation is required.

8.1 Bus Scope

Bus scope operations are those between the instances on a bus: addressing, message relay, keepalive, capability negotiation, and error reporting.

Symbol Verb Description
E Establish Claim a BID on this bus
C Conflict Report a BID collision
B Broadcast Send to all instances on this bus
P Ping Keepalive / presence check
p Ping response Report presence, with the responder's BID
R Relay Forward a message along a path
D Discover Find how a BID is reached
d Discover response Report the BID a target is reachable via
S Scaleback Declare receiver capability limits (reactive)
W Acknowledge Confirm receipt of a Scaleback
X Exception Report a structured error

A Ping or Discover is answered with its own symbol in lower case, so a response is never read as a request. A bus delivers a response to every instance on it, so a response answers every instance that asked, each matching it to its request by what it names: a Ping or Discover response by the BID it names, and an Offer by its capability (Section 10.2). No request names its sender.

8.2 Service Scope

Service scope operations handle runtime capability discovery. An instance that wants a capability queries for it. Instances that can provide it offer. The querying instance accepts. No upfront advertisement, no schema exchange.

Symbol Verb Description
Q Query Request a service or capability
O Offer Offer to provide the requested service
A Accept Accept an offer, establishing the service agreement

8.3 Session Scope

Session scope operations manage active communication sessions between instances. Sessions are optional — not every interaction requires one.

Symbol Verb Description
K Call Send a request within a session
T Status Request or report session state
N Notify Push an event notification within a session
L Locate Find the instance holding a session (SID)
U Resume Reconnect to an interrupted session
F Finish Close a session

9. Bus Operations

9.1 BID Establishment

When an instance connects to a bus, it MUST obtain a unique BID through the Establish/Conflict protocol.

9.1.1 Establishment Procedure

  1. Generate a random BID candidate of the current length, in base-32 (Section 5.7). The current length is the initial length when the attempt begins.
  2. Transmit an Establish message with the candidate BID and a fresh nonce: 8 random bytes, written as 16 lower-case hex characters in a TEXT word.
  3. Wait for the timeout period.
  4. If a Conflict is received for this BID, or an Establish for it whose nonce is not the one this instance sent, increment the BID length and return to step 1, unless the length now exceeds the maximum: then go to step 5. An Establish read back with its own nonce is this instance's own, which a bus may deliver to its transmitter (Section 12).
  5. If the BID length exceeds the maximum, abort the connection attempt. An instance that implements Exception MAY first transmit one with reason "BID_OVERFLOW", which names no BID (Section 9.8).
  6. If no Conflict is received within the timeout, the BID is claimed. The instance begins listening on the bus, and keeps its nonce for as long as it holds the BID, so that it knows its own Establish when the bus delivers it late (Section 9.1.2).

The initial length, the maximum and the timeout are the instance's own, for the bus it connects to. The protocol fixes none of them.

Establishment detects a collision only while it runs. If two instances propose one BID and both Establishes are lost, each claims it, and nothing in this specification detects the duplicate afterwards.

9.1.2 Conflict Detection

While listening on a bus, an instance that receives an Establish message for a BID it already holds MUST respond with a Conflict message containing that BID, unless the Establish carries its own nonce: that one is its own, read back (Section 9.1.1).

9.1.3 State Machine

State Entry Condition Action Transitions
IDLE Initial / after disconnect None PROPOSING (on connect)
PROPOSING Generated candidate BID Send !E, start timeout ESTABLISHED (timeout expires, no conflict) / PROPOSING (Conflict or rival Establish received, length < max) / FAILED (length >= max)
ESTABLISHED No conflict within timeout Listen on bus IDLE (on disconnect)
FAILED Max BID length exceeded MAY send !X "BID_OVERFLOW" IDLE

9.1.4 Wire Examples

Establish BID:

Pseudo:  [SOM] [SOW]![SOB]E[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]~[SOB]9f3a61c08e2b4d57[EOW] [EOM]
Wire:    ☻↕!→E►↕@♦U→4T9X2►↕~→9f3a61c08e2b4d57►♥

Instance proposes BID "4T9X2" on the bus, with the nonce 9f3a61c08e2b4d57.

Conflict detected:

Pseudo:  [SOM] [SOW]![SOB]C[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→C►↕@♦U→4T9X2►♥

Existing instance reports BID "4T9X2" is already in use.

Establishment failed:

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]~[SOB]BID_OVERFLOW[EOW] [EOM]
Wire:    ☻↕!→X►↕~→BID_OVERFLOW►♥

Instance could not establish a BID within the maximum length.

9.2 Reactive Scaleback

Scaleback is the mechanism by which a constrained receiver communicates its limits to a sender. There is no upfront capability handshake. A receiver that cannot process a message because it exceeds the receiver's limits has two options: send a Scaleback response stating its limits, or silently drop the message. A malformed message is never answered with a Scaleback (Section 7.4).

9.2.1 Drop (Silent)

A receiver that cannot process a message and cannot (or chooses not to) send a Scaleback simply discards the rest of the message, as Section 7.4 describes. This is the universal fallback that requires zero transmit capability. The sender receives no indication that the message was dropped.

9.2.2 Scaleback Message

A Scaleback message carries its sender's BID, then the BID of the instance its limits apply to, and communicates those limits using two mechanisms:

A Scaleback carries no payload word but these: at most one LOGICAL, then at most two INTEGERs. Any other makes it malformed (Section 7.4).

A device with no limits sends no Scaleback, except to lift limits it has stated (Section 9.2.4). Only constraints are communicated: a dimension a Scaleback omits is unconstrained.

9.2.3 Scaleback Wire Examples

Full Scaleback (type exclusions + size limits):

Pseudo:  [SOM] [SOW]![SOB]S[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]?[SOB]~H & ~q[EOW] [SOW]#[SOR]D[SOU]W[SOB]48[EOW] [SOW]#[SOR]D[SOU]W[SOB]0[EOW] [EOM]
Wire:    ☻↕!→S►↕@♦U→7M3K9►↕@♦U→4T9X2►↕?→~H & ~q►↕#♦D•W→48►↕#♦D•W→0►♥

From 7M3K9 to 4T9X2: cannot handle hex or quadword. Max head 48 bytes. No tail.

Size-only Scaleback (no type exclusions):

Pseudo:  [SOM] [SOW]![SOB]S[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]#[SOR]D[SOU]W[SOB]64[EOW] [SOW]#[SOR]D[SOU]W[SOB]0[EOW] [EOM]
Wire:    ☻↕!→S►↕@♦U→7M3K9►↕@♦U→4T9X2►↕#♦D•W→64►↕#♦D•W→0►♥

From 7M3K9 to 4T9X2: no type exclusions. Max head 64 bytes. No tail.

Flags-only Scaleback (no size limits):

Pseudo:  [SOM] [SOW]![SOB]S[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]?[SOB]~q & ~w[EOW] [EOM]
Wire:    ☻↕!→S►↕@♦U→7M3K9►↕@♦U→4T9X2►↕?→~q & ~w►♥

From 7M3K9 to 4T9X2: cannot handle the quadword or word (16-bit) unit. No size constraints.

Tail-only Scaleback (head unconstrained):

Pseudo:  [SOM] [SOW]![SOB]S[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]#[SOR]D[SOU]W[SOB]0[EOW] [SOW]#[SOR]D[SOU]W[SOB]512[EOW] [EOM]
Wire:    ☻↕!→S►↕@♦U→7M3K9►↕@♦U→4T9X2►↕#♦D•W→0►↕#♦D•W→512►♥

From 7M3K9 to 4T9X2: no type exclusions. Head unconstrained. Max tail 512 bytes.

Lifting Scaleback (no limits):

Pseudo:  [SOM] [SOW]![SOB]S[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→S►↕@♦U→7M3K9►↕@♦U→4T9X2►♥

From 7M3K9 to 4T9X2: every limit stated before is lifted.

On receiving a Scaleback, the sender may resend the message within the stated limits, attempt a different encoding, or accept that this receiver cannot handle the message. The sender has no obligation to retry.

9.2.4 Scaleback Semantics

9.3 Path Addressing

Antheos uses a path-and-index mechanism for multi-hop routing. A PATH word contains a dot-separated sequence of BIDs representing the complete route. An INTEGER index word identifies the current position within that path. The index is zero-based: position 0 is the origin, position N is the Nth hop.

When paths are used for addressing, the message ends with the index followed by the path. This ordering is intentional: a stream-parsing instance reads the index first, then scans the path counting dots to find its target position. Single-pass, no backtracking, no buffering the entire path.

... [payload words] [#index] [/path] [EOM]

Beyond the bus it is sent on, Level 1 carries the text of a broadcast that carries a PATH (Section 9.3.1) and of a Relay, and the RELAY_FAILED Exception (Section 9.3.3), and nothing else: a Query, Offer and Accept (Section 10) and every session message (Section 11) are addressed on one bus. A service on another bus is reached through an instance on both, which offers it on the one and uses it on the other.

9.3.1 Broadcast Path (Expanding)

A broadcast that carries a PATH travels beyond its bus, and the path expands as it goes. An instance on more than one bus forwards it onto its other buses, appending to the path the BID it holds on the bus it forwards onto. It forwards no broadcast whose path already holds one of its BIDs. No index is needed during broadcast — the message goes to all instances.

Each entry of a path is therefore the BID its instance holds on the bus it shares with the next entry, and the last entry the BID its instance holds on the bus it shares with the one before. A BID is unique only on its own bus (Section 5.4), so a path can hold one string for two instances on different buses, and either can take the other's entry for its own: it stops a broadcast that was not its to stop, or takes a relay not meant for it. Longer BIDs make this rarer.

A receiver that replies first appends to the path the BID it holds on the bus it received the broadcast on, so that the path holds the complete return route. It then sets the index to its own position, the last entry, and sends a Relay along that path (Section 9.3.2).

Nothing else bounds a broadcast in a mesh of buses. It can arrive at an instance once by each route that passes no instance twice, a receiver may reply along each, and nothing de-duplicates it (Section 15).

Trace: Broadcast from A (BID=AA) through B, C to D:

At A (origin):

Pseudo:  [SOM] [SOW]![SOB]B[EOW] [SOW]~[SOB]Hello[EOW] [SOW]/[SOB]AA[EOW] [EOM]
Wire:    ☻↕!→B►↕~→Hello►↕/→AA►♥

Origin broadcasts. Path contains only the origin BID.

At B (appends BB):

Pseudo:  [SOM] [SOW]![SOB]B[EOW] [SOW]~[SOB]Hello[EOW] [SOW]/[SOB]AA.BB[EOW] [EOM]
Wire:    ☻↕!→B►↕~→Hello►↕/→AA.BB►♥

B forwards broadcast, appending its BID to the path.

At C (appends CC):

Pseudo:  [SOM] [SOW]![SOB]B[EOW] [SOW]~[SOB]Hello[EOW] [SOW]/[SOB]AA.BB.CC[EOW] [EOM]
Wire:    ☻↕!→B►↕~→Hello►↕/→AA.BB.CC►♥

C forwards broadcast, appending its BID.

At D (appends DD):

Pseudo:  [SOM] [SOW]![SOB]B[EOW] [SOW]~[SOB]Hello[EOW] [SOW]/[SOB]AA.BB.CC.DD[EOW] [EOM]
Wire:    ☻↕!→B►↕~→Hello►↕/→AA.BB.CC.DD►♥

D receives the broadcast with the path AA.BB.CC and, to reply, appends the BID it holds on that bus. The path is now complete and fixed.

9.3.2 Relay Path (Index Decrement)

A relay carries the complete fixed path (established during broadcast or known in advance) and a zero-based index indicating the current sender's position. Its originator holds the path's last entry and sets the index to that position, as a broadcast's replier does (Section 9.3.1), and a recovery origin retransmits it from its own position (Section 9.3.3). Each forwarding instance decrements the index and forwards to the BID at position index–1 in the path. The procedure routes every message that carries an index and a PATH: a Relay, and the RELAY_FAILED Exception of Section 9.3.3.

The path never changes as a relay is forwarded, except where a recovery origin writes a gateway into it (Section 9.3.3). Only the index is modified. This means a forwarding instance performs no string manipulation — it reads the index, subtracts one, counts dots in the path to find the next-hop BID, and forwards.

Relay procedure at each hop:

  1. Read the index from the INTEGER word, and compute my position p = index − 1.
  2. If p < 0, or the index is past the path's last position, discard the message and answer nothing.
  3. Find path[p] by counting dots (zero-based) in the PATH body. If path[p] is none of the BIDs I hold, on any bus, the message is not addressed to me: discard it (Section 12).
  4. If p = 0: I am the destination. Deliver the message.
  5. Otherwise: write p as the new index and forward to path[p − 1], on the bus where I reach that BID. Which bus that is is my own knowledge (Section 2): the bus the path's broadcast arrived on, or a route a Discover gave me (Section 9.3.3).

Trace: D (index=3) replies to A via the established path AA.BB.CC.DD:

D sends (index=3, D's position):

Pseudo:  [SOM] [SOW]![SOB]R[EOW] [SOW]~[SOB]Reply[EOW] [SOW]#[SOR]D[SOU]B[SOB]3[EOW] [SOW]/[SOB]AA.BB.CC.DD[EOW] [EOM]
Wire:    ☻↕!→R►↕~→Reply►↕#♦D•B→3►↕/→AA.BB.CC.DD►♥

D sends relay. Index=3, next hop is path[2]=CC.

At C (decrements to 2):

Pseudo:  [SOM] [SOW]![SOB]R[EOW] [SOW]~[SOB]Reply[EOW] [SOW]#[SOR]D[SOU]B[SOB]2[EOW] [SOW]/[SOB]AA.BB.CC.DD[EOW] [EOM]
Wire:    ☻↕!→R►↕~→Reply►↕#♦D•B→2►↕/→AA.BB.CC.DD►♥

C forwards. Index=2, next hop is path[1]=BB.

At B (decrements to 1):

Pseudo:  [SOM] [SOW]![SOB]R[EOW] [SOW]~[SOB]Reply[EOW] [SOW]#[SOR]D[SOU]B[SOB]1[EOW] [SOW]/[SOB]AA.BB.CC.DD[EOW] [EOM]
Wire:    ☻↕!→R►↕~→Reply►↕#♦D•B→1►↕/→AA.BB.CC.DD►♥

B forwards. Index=1, next hop is path[0]=AA.

At A (index=1, path[0]=AA = me): message delivered.

A receives the message. Index-1=0, path[0]=AA matches own BID. Delivered.

9.3.3 Path Recovery

Failure Detection: A forwarding instance that cannot deliver to the next-hop BID (instance disconnected, bus lost, timeout) MUST NOT forward the message. It becomes the recovery origin for that message.

Recovery: The recovery origin issues a Discover (!D) for the unreachable BID, broadcasting it on all buses it is connected to. If a Discover response is received within timeout, the recovery origin records the route it gives and retransmits the message from its own position in the path. A response from the BID's holder leaves the path as it is. A response from a gateway, which names the BID the unreachable one is reached via (Section 9.6), writes that BID into the path just before the recovery origin's entry, so that the recovery origin's position, and the index it sends, is one greater: every instance that handles the message holds a position in its path.

Wire example (C cannot reach BB):

Pseudo:  [SOM] [SOW]![SOB]D[EOW] [SOW]@[SOR]U[SOB]BB[EOW] [EOM]
Wire:    ☻↕!→D►↕@♦U→BB►♥

C broadcasts Discover for BID BB.

Wire example (the gateway 7M3K9 answers !d @U BB @U 7M3K9, and C retransmits D's Relay through it):

Pseudo:  [SOM] [SOW]![SOB]R[EOW] [SOW]~[SOB]Reply[EOW] [SOW]#[SOR]D[SOU]B[SOB]3[EOW] [SOW]/[SOB]AA.BB.7M3K9.CC.DD[EOW] [EOM]
Wire:    ☻↕!→R►↕~→Reply►↕#♦D•B→3►↕/→AA.BB.7M3K9.CC.DD►♥

C writes 7M3K9 in before its own entry and sends from its new position, 3. 7M3K9 (p = 2) forwards to BB, and BB (p = 1) to AA.

Discover Fails: If no Discover response is received within timeout, the recovery origin sends !X RELAY_FAILED back to the Relay's originator. Its path is the Relay's path from the originator's entry to the recovery origin's, reversed, so that the originator is at position 0 and the recovery origin last; its index is the recovery origin's position in that path, and it is routed as Section 9.3.2 describes. The Exception carries the unreachable BID as its one ID word, then its reason, then the index and the path: !X @U <unreachable BID> ~RELAY_FAILED #<index> /<path>. It names no sender, the path's entry at its index being the recovery origin's.

Wire example (D relays to A along AA.BB.CC.DD, and C cannot reach BB):

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]@[SOR]U[SOB]BB[EOW] [SOW]~[SOB]RELAY_FAILED[EOW] [SOW]#[SOR]D[SOU]B[SOB]1[EOW] [SOW]/[SOB]DD.CC[EOW] [EOM]
Wire:    ☻↕!→X►↕@♦U→BB►↕~→RELAY_FAILED►↕#♦D•B→1►↕/→DD.CC►♥

C sends RELAY_FAILED for BB along DD.CC, the Relay's path from D to C reversed. Index=1, C's position. D, at position 0, delivers it.

Return Path Also Broken: If the recovery origin cannot relay the Exception back, it MUST silently discard the message. The originator will detect failure through its own timeout.

9.4 Broadcast

A broadcast message is sent to all instances on the current bus. A broadcast with no PATH, as below, stays on that bus. One with a PATH travels on beyond it (Section 9.3.1), and a reply returns along the path as a Relay (Section 9.3.2).

Pseudo:  [SOM] [SOW]![SOB]B[EOW] [SOW]~[SOB]Hello all[EOW] [EOM]
Wire:    ☻↕!→B►↕~→Hello all►♥

Broadcast to all instances on this bus.

9.5 Ping

Ping is a keepalive and presence check. It may target a specific BID or be broadcast. The response is a Ping response (!p) carrying the responder's BID. A Ping naming a BID is answered by the holder of that BID alone, and every other instance stays silent. A broadcast Ping is answered by every instance that reads it, its sender included when the bus delivers the Ping back to it (Section 12).

Ping a specific instance:

Pseudo:  [SOM] [SOW]![SOB]P[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→P►↕@♦U→4T9X2►♥

Ping instance at BID "4T9X2".

Response:

Pseudo:  [SOM] [SOW]![SOB]p[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→p►↕@♦U→4T9X2►♥

Pong: instance 4T9X2 confirms presence.

Broadcast ping:

Pseudo:  [SOM] [SOW]![SOB]P[EOW] [EOM]
Wire:    ☻↕!→P►♥

Broadcast ping. All instances respond with their BID.

Broadcast responses:

Wire:    ☻↕!→p►↕@♦U→4T9X2►♥
Wire:    ☻↕!→p►↕@♦U→7M3K9►♥

Multiple instances respond, each with own BID.

9.6 Discover

Discover (!D) finds how a BID is reached: through its holder, or through an instance that can reach it. This is used for path recovery when a relay cannot reach the next hop.

Request:

Pseudo:  [SOM] [SOW]![SOB]D[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→D►↕@♦U→4T9X2►♥

Discover: where is BID 4T9X2?

Response (from the holder of BID 4T9X2):

Pseudo:  [SOM] [SOW]![SOB]d[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→d►↕@♦U→4T9X2►↕@♦U→4T9X2►♥

Discovered: BID 4T9X2 answers for itself.

Response (from a gateway to BID 4T9X2):

Pseudo:  [SOM] [SOW]![SOB]d[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [EOM]
Wire:    ☻↕!→d►↕@♦U→4T9X2►↕@♦U→7M3K9►♥

Discovered: BID 4T9X2 is reachable via BID 7M3K9.

A Discover response (!d) carries the target BID, then the BID it is reachable via. An instance that receives a Discover for a BID it holds responds with that BID as both. An instance connected to multiple buses that knows the target BID is reachable on another bus MAY respond with the gateway BID for that route as the second. Every other instance stays silent. If no response is received, the BID is considered unreachable.

9.7 Acknowledge

Acknowledge (!W) confirms receipt of a Scaleback, and of nothing else. It carries its sender's BID, then the BID of the instance whose Scaleback it confirms: an Acknowledge from X to Y confirms the most recent Scaleback from Y to X (Section 9.2.4).

Pseudo:  [SOM] [SOW]![SOB]W[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [EOM]
Wire:    ☻↕!→W►↕@♦U→4T9X2►↕@♦U→7M3K9►♥

Acknowledge: 4T9X2 confirms receipt of the most recent Scaleback from BID "7M3K9".

Acknowledge is never mandatory. A Scaleback's sender that receives none MAY retransmit the Scaleback (Section 9.2.4).

9.8 Exception

Exceptions are structured error reports. The SYMBOL !X is followed by the BIDs that address the Exception, then a TEXT word containing the error reason. An Exception names its sender's BID, then, when the message it answers names its sender, that instance's BID, as every message naming two BIDs names its sender first. One answering a message that names no sender, as a Broadcast, Query, Ping or Discover does not, names its sender's BID alone. An instance drops an Exception that names another instance as its target.

Three reasons are addressed otherwise. BID_OVERFLOW names no BID: its sender holds none, and its last candidate is another instance's (Section 9.1.1). RELAY_FAILED is addressed by its path, and carries the BID its sender could not reach, with an index and a path after its reason (Section 9.3.3). A reason of Session scope (Section 13) carries the session's IDs, as set out below. A SERVICE_UNKNOWN carries the capability after its reason (Sections 10.1 and 10.3.1).

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]~[SOB]MALFORMED_FRAME[EOW] [EOM]
Wire:    ☻↕!→X►↕@♦U→4T9X2►↕@♦U→7M3K9►↕~→MALFORMED_FRAME►♥

Instance "4T9X2" reports a malformed message from "7M3K9".

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]~[SOB]UNSUPPORTED_TYPE[EOW] [EOM]
Wire:    ☻↕!→X►↕@♦U→4T9X2►↕~→UNSUPPORTED_TYPE►♥

Instance "4T9X2" reports a word type it does not implement, in a message that named no sender.

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]~[SOB]BID_OVERFLOW[EOW] [EOM]
Wire:    ☻↕!→X►↕~→BID_OVERFLOW►♥

Exception: BID establishment exceeded maximum length.

Session reasons: an Exception whose reason is of Session scope answers a session message, and is addressed as one is (Section 11): the SID, the sender's BID, then the BID of the instance it answers, which it always names. Its reason is its only TEXT word and it carries no MID, and one in any other shape is malformed (Section 7.4). An instance drops one that names its own BID as sender, or another instance as its target. Section 11.7 shows one.


10. Service Operations

Service discovery follows a strict Query/Offer/Accept pattern. An instance that needs a capability queries for it. Instances that can provide it respond with offers. The querying instance selects and accepts an offer.

Sessions are not obligatory. A service interaction may consist of a single Query/Offer/Accept exchange followed by direct message exchange, or it may establish a long-lived session. The service scope defines the discovery mechanism; the session scope (Section 11) defines optional persistent communication. Both are addressed on one bus (Section 9.3).

10.1 Query

An instance broadcasts a query describing the service it needs, or directs it to one instance by naming that instance's BID before the capability. A directed Query is answered by the instance it names alone. The query carries the desired capability as a text payload (Section 7.7). The capability MUST be non-empty: an empty query describes no service, and a match-all interpretation would constitute the capability enumeration the demand-driven model prohibits (Sections 10.2, 15). A sender MUST NOT send an empty capability, and a receiver MUST NOT answer a Query with one.

Pseudo:  [SOM] [SOW]![SOB]Q[EOW] [SOW]~[SOB]temperature[EOW] [EOM]
Wire:    ☻↕!→Q►↕~→temperature►♥

Query: I need a temperature service.

Directed Query:

Pseudo:  [SOM] [SOW]![SOB]Q[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]~[SOB]temperature[EOW] [EOM]
Wire:    ☻↕!→Q►↕@♦U→4T9X2►↕~→temperature►♥

Query to 4T9X2 alone: do you offer a temperature service?

The target of a directed Query that does not offer the capability MAY answer with !X SERVICE_UNKNOWN, naming its own BID before the reason and carrying the capability after it, so that the refusal is matched to its Query as an Offer is (Section 8.1). A broadcast Query is never answered with it.

SERVICE_UNKNOWN:

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]~[SOB]SERVICE_UNKNOWN[EOW] [SOW]~[SOB]temperature[EOW] [EOM]
Wire:    ☻↕!→X►↕@♦U→4T9X2►↕~→SERVICE_UNKNOWN►↕~→temperature►♥

4T9X2 does not offer a temperature service.

10.2 Offer

Instances that can provide the requested service respond with an offer. The offer includes the responding instance's BID and the capability the Query asked for, exactly as the Query wrote it, and answers every instance whose Query carried that text (Section 8.1). The capability names the interface — there is no separate interface definition language.

An Offer MUST only be sent in response to a Query. Unsolicited Offers (broadcasting capabilities at startup or at any time without a preceding Query) are prohibited. This prevents capability enumeration by passive observers and enforces the demand-driven discovery model described in Section 8.2.

Pseudo:  [SOM] [SOW]![SOB]O[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]~[SOB]temperature[EOW] [EOM]
Wire:    ☻↕!→O►↕@♦U→4T9X2►↕~→temperature►♥

Offer: instance at BID "4T9X2" can provide temperature.

10.3 Accept

The querying instance accepts an offer by addressing the offering instance's BID. This establishes the service agreement and may optionally initiate a session.

10.3.1 Accept Wire Format

An Accept names its sender's BID, then the offering instance's, then the capability it takes, as its Query wrote it (Section 10.1). Naming its sender lets the offering instance identify who accepted its offer without waiting for a session to be established, and naming the capability tells it which of its offers was taken.

Pseudo:  [SOM] [SOW]![SOB]A[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]~[SOB]temperature[EOW] [EOM]
Wire:    ☻↕!→A►↕@♦U→7M3K9►↕@♦U→4T9X2►↕~→temperature►♥

Accept: instance "7M3K9" accepts the offer of temperature from BID "4T9X2".

The first @U word is the sender's BID (the accepting instance) and the second the target's (the offering instance), as every message naming two BIDs names its sender first.

An Offer has no lifetime. An offering instance that no longer provides the capability an Accept takes MAY answer it with !X SERVICE_UNKNOWN, naming its own BID and then the accepting instance's, since the Accept names its sender, and carrying the capability after the reason.

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]~[SOB]SERVICE_UNKNOWN[EOW] [SOW]~[SOB]temperature[EOW] [EOM]
Wire:    ☻↕!→X►↕@♦U→4T9X2►↕@♦U→7M3K9►↕~→SERVICE_UNKNOWN►↕~→temperature►♥

4T9X2 refuses 7M3K9's Accept: it no longer offers a temperature service.

10.4 Offer Collection

A Query may elicit multiple Offers from different instances. The querying instance decides how to handle them:

Offers not followed by an Accept are implicitly declined. No explicit rejection message is required. An offering instance MUST NOT assume its Offer will be accepted and MUST NOT allocate resources until Accept is received.


11. Session Operations

Sessions provide persistent, stateful communication between instances. They are optional — bus-scope operations and one-shot service interactions do not require sessions. When an interaction needs message sequencing, state tracking, or long-lived communication, a session is established.

Addressing: every session message carries the SID, then the BID of its sender, then optionally the BID of the instance it is meant for, its target. Call, Status and Notify carry a MID after them (Section 11.1.2), a Call MAY carry a trace identifier after its MID (Section 11.3), and Locate, Resume and Finish carry neither. A session message in any other shape is malformed (Section 7.4). A bus may deliver a frame to its sender as well as to every other instance, so an instance can read its own session messages back: it drops one that names its own BID as sender. An instance drops a session message that names another instance as its target. An Exception answering a session message is addressed the same way, and always names its target (Section 9.8).

11.1 Session Lifecycle

11.1.1 Creation

A session is typically initiated after a service Accept, but may be opened directly between instances that already know each other. The opener generates a fresh SID (Section 11.2) and opens the session with a Call carrying it. An instance that receives a Call at MID 1 carrying a SID it does not hold takes it as the opening of a session, whether or not an Accept came first; this is the direct open. An instance MAY decline a session by dropping its first Call. One that cannot hold another session instead refuses that Call with !X SESSION_LIMIT (Section 9.8), and no session opens.

Any other session message carrying a SID the receiver does not hold opens nothing. A Call at any other MID, a Status or a Resume draws !X SESSION_NOT_FOUND (Section 9.8), so that an opener whose peer has lost the session, as by a reboot, learns it and opens again under a fresh SID (Section 11.2). A Locate, a Notify or a Finish is dropped: a Locate is a search, which an instance not holding the session leaves to the one that does (Section 11.6).

A Call at MID 1 carrying a SID the receiver already holds, other than the first Call after a wrap (Section 11.1.2), would open a second session under that SID, unless it comes from the instance that opened that session. From that instance it is a retransmission (Section 11.1.2): the receiver answers it again while MID 1 is the last request it answered, and drops it as stale after, keeping that answer and nothing more. From any other instance, the receiver refuses it with !X SID_IN_USE (Section 9.8), and that instance opens again under a fresh SID (Section 11.2). It sends no Finish for the refused SID, which names the receiver's other session.

11.1.2 Active Communication

Within a session, each end sequences the messages it sends by MID, a decimal counter of its own that starts at 1 and increments by 1 with each Call, Status or Notify it sends. Only the opener sends requests, a Call (!K) or a Status (!T) querying the session's state, and it sends one at a time: the other end's next Call answers a Call, and its next Status answers a Status. Either end may push an unsolicited event with Notify (!N).

MID Wrap: After sending a message with MID 4294967295, an end transmits a wrap message: a Call (!K) addressed as its other messages in the session are (Section 11), with MID 0 and no payload, which answers nothing and expects no response. The receiver resets its expectation of that end's MIDs to 1. Normal sequencing resumes immediately after the wrap message.

Retransmission: a retransmitted Call or Status carries its original MID. An end keeps the last answer it sent in each session. When the other end sends that answer's request again, with its MID, the end sends the answer again, unchanged, and does not act on the request twice. Of the MIDs the other end has sent since its last wrap message, if any, the end accepts each one it has not received and that is not stale: one above the next it expects, the messages between having been lost, as a Notify may be, and one below it, a lost message sent again, as a Call is when a Notify sent after it arrived first. How many MIDs below the highest it has received an end remembers is its own, and it takes an older one as received. One below the MID of the last request it answered is stale and is dropped, as is any MID it has already received, other than that of the request it answers again. A request that arrives while an earlier one is unanswered is taken in turn, after it. For example, 7M3K9's Call at MID 5 is answered by 4T9X2's Call at MID 9, which is lost: 7M3K9 sends its Call at MID 5 again, and 4T9X2 sends its Call at MID 9 again, as it first sent it, without acting on the request a second time.

Another instance: a session's opener is the instance that opened it or, after a Resume, the instance that resumed it (Section 11.7). The session's other end takes no Call, Status or Notify in it from any other instance. It refuses a Call from another instance with !X SID_IN_USE (Section 9.8) at any MID, as at MID 1 (Section 11.1.1), and drops a wrap message, a Status or a Notify from it, so none of them disturbs the session or its opener. A sender's BID is self-asserted, so this stops an accident or casual interference and not a forger (Section 15).

11.1.3 Interruption and Recovery

If a session is interrupted (bus disconnect, device reset), the Locate verb finds where the remote instance now resides, and Resume reconnects to the session. An ACTIVE session with no message from the other end within the instance's idle time, which is implementation-defined, becomes SUSPENDED, as on a disconnect, and is then resumed or times out as any other (Section 11.1.5).

11.1.4 Termination

Finish closes a session, and its SID is discarded (Section 11.2).

11.1.5 State Machine

State Entry Condition Transitions
IDLE No session ACTIVE (on the opening Call, Section 11.1.1)
ACTIVE Session created, SID assigned SUSPENDED (on disconnect or idle time) / IDLE (on Finish)
SUSPENDED Bus lost or timeout ACTIVE (on Resume, or a message from the other end) / IDLE (on Finish or timeout expiry)

Session Timeout: The duration before a SUSPENDED session transitions to IDLE is implementation-defined. An instance that receives a Resume (!U) for a session that has timed out responds with !X SESSION_EXPIRED while it still remembers the SID. How long it remembers one is implementation-defined, and once it has forgotten the SID, responding with !X SESSION_NOT_FOUND is conformant. Either end MAY signal an impending timeout with an unsolicited Status (!T) reporting EXPIRING (Section 11.4) before transitioning.

11.2 SID Generation

SIDs are variable-length base-32 hashes derived from a monotonic counter and device entropy. Every session receives a fresh SID, and no SID is issued while a session holds it. SID length starts short and grows on collision, keeping typical messages compact while scaling to high session counts.

SID Length: An implementation defines initial length, increment on collision, and maximum length, which is at most 26: the 16 bytes the procedure below hashes make 26 base-32 characters. SID generation starts at the initial length and grows on collision until a unique SID is found or maximum length is reached.

Device Entropy: device entropy is 8 bytes the instance holds, and the counter runs from 0 to 4294967295 (2³²−1). An instance never hashes the same device entropy and counter together twice. It either keeps both across reboots, storing the counter, or a lease of a block of counters, before the sessions it covers are opened, or draws fresh device entropy from a cryptographically secure source at every start, the counter then free to start at 0. Either way, it draws new device entropy when the counter wraps, before making another SID. It MAY draw fresh device entropy more often, as often as for every SID.

Generation Procedure:

  1. Hash the counter, written in decimal with no leading zero, followed by the 8 bytes of device entropy, with FNV-1a 64-bit (offset basis 14695981039346656037, prime 1099511628211). Store the hash as 8 bytes, least significant first, then hash those 8 bytes, as stored, with FNV-1a 64-bit from its offset basis, and store that hash after them, least significant first, for 16 bytes in all. (Truncation keeps the characters from the low-order bytes, which FNV-1a's last rounds vary most. It can still collide badly at very short lengths, so an implementation's initial length SHOULD stay clear of that regime — e.g. an initial length of 6 base-32 characters gives a 32⁶ ≈ 1.07×10⁹ space.)
  2. Encode the 16 bytes as base-32 (Section 5.7), five bits to a character, most significant bit first, the last character padded with zero bits, and keep the first characters, as many as the current SID length (starting at initial length).
  3. Check whether the truncated SID names a session the instance holds.
  4. If collision: increase length by increment, re-truncate, and repeat from step 3.
  5. If length exceeds maximum: increment sid_counter, reset length to initial, and repeat from step 1.
  6. If no collision: SID is assigned, increment sid_counter.

Rotation:

SIDs are generated fresh for every session. When a session finishes, its SID is discarded. The monotonic counter ensures forward progress, and the device entropy rule keeps a hash input from repeating, across reboots as within one.

11.2.1 Non-conformant Escape Hatch

The §11.2 generation procedure is the conformance bar — implementations claiming spec conformance MUST run the collision-check-and-grow algorithm against the active-session set. An implementation MAY additionally expose a non-conformant SID-generation path (e.g. an "unchecked" acquire that skips the active-set check) for memory-constrained environments where maintaining the active-set is infeasible.

Such a path is a structurally-named escape hatch — not a default. Implementations exposing one MUST:

These requirements bind an implementation's interface and documentation, not its messages, and no peer can test them on the wire.

A conformant peer encountering a counterparty that produced a duplicate SID refuses the second-arriving session with !X SID_IN_USE (Section 11.1.1). A duplicate SID from the session's own counterparty, the instance that opened it, is read as a retransmission instead. The conformance bar binds the implementation that generates the SID; the receiving peer is entitled to assume §11.2 was followed.

11.3 Call

Call sends a request within an active session and expects a response; the wrap message of Section 11.1.2 is the one Call that expects none. The response is itself a Call, the other end's next message, numbered from that end's own count (Section 11.1.2). Receipt of the response confirms delivery of the request. If no response arrives within timeout, the sender MAY retransmit it, with its original MID (Section 11.1.2), or treat the session as interrupted.

Trace identifier: a Call MAY carry a trace identifier, which its sender chooses, as a plain ID word after its MID (Section 5.7), so it is printable ASCII with no space or colon. A Call's payload is the application's, whole: no header is read in it, and a payload beginning T: is kept as it is.

Request:

Pseudo:  [SOM] [SOW]![SOB]K[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]D[SOB]1[EOW] [SOW]~[SOB]get_reading[EOW] [EOM]
Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→1►↕~→get_reading►♥

Session Call from 7M3K9 to 4T9X2: SID=A7K2M, MID=1, request="get_reading".

Response:

Pseudo:  [SOM] [SOW]![SOB]K[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]D[SOB]1[EOW] [SOW]~[SOB]23.5[EOW] [EOM]
Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→1►↕~→23.5►♥

Session Call response from 4T9X2 to 7M3K9: SID=A7K2M, MID=1, response="23.5".

Request with a trace identifier:

Pseudo:  [SOM] [SOW]![SOB]K[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]D[SOB]1[EOW] [SOW]@[SOB]trace-7f3a[EOW] [SOW]~[SOB]get_reading[EOW] [EOM]
Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→1►↕@→trace-7f3a►↕~→get_reading►♥

Session Call from 7M3K9 to 4T9X2: SID=A7K2M, MID=1, trace identifier "trace-7f3a", request="get_reading".

11.4 Status

Status requests or reports the current state of a session. A Status carrying a state is a report, and one carrying none is a request, which only the opener sends (Section 11.1.2). Either end MAY send a report unsolicited, as one reporting EXPIRING (Section 11.1.5). An end with a Status request outstanding takes the next report from the other end as its answer, an unsolicited one included, which states the session's state as truly; a report with none outstanding is unsolicited. A report answering a request confirms both the request's delivery and the session's current state.

A Status report carries one of three states. ACTIVE: the session is in use. SUSPENDED: it is interrupted and can be resumed (Section 11.1.3). EXPIRING: it is about to time out (Section 11.1.5). A Status reporting any other state is malformed (Section 7.4). IDLE is never reported, since an IDLE session does not exist.

Request:

Pseudo:  [SOM] [SOW]![SOB]T[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]D[SOB]2[EOW] [EOM]
Wire:    ☻↕!→T►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→2►♥

Session Status request from 7M3K9 to 4T9X2: SID=A7K2M, MID=2.

Response:

Pseudo:  [SOM] [SOW]![SOB]T[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]D[SOB]2[EOW] [SOW]~[SOB]ACTIVE[EOW] [EOM]
Wire:    ☻↕!→T►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→2►↕~→ACTIVE►♥

Session Status response from 4T9X2 to 7M3K9: SID=A7K2M, MID=2, state="ACTIVE".

11.5 Notify

Notify pushes an unsolicited event within a session. Unlike Call, it does not expect a response and delivery is not confirmed. Notify is fire-and-forget; use Call if acknowledgment is required.

Pseudo:  [SOM] [SOW]![SOB]N[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]D[SOB]3[EOW] [SOW]~[SOB]threshold_exceeded[EOW] [EOM]
Wire:    ☻↕!→N►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→3►↕~→threshold_exceeded►♥

Session Notify from 4T9X2 to 7M3K9: SID=A7K2M, MID=3, event="threshold_exceeded".

11.6 Locate

Locate finds the instance holding a session (SID), by its BID. This is used after a bus change or reconnect. A Locate is a search and names no target. The holder's response names the requester as its target, and that is how a response is told from a request. An instance answers a Locate only for a session it holds SUSPENDED: one whose other end is still active with it is not the session sought. A holder drops a Locate that arrives while its session is still ACTIVE, and answers one that arrives after its idle time has suspended the session (Section 11.1.3). A requester that is not answered MAY send its Locate again; how often, and when it gives up, are its own.

Request:

Pseudo:  [SOM] [SOW]![SOB]L[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [EOM]
Wire:    ☻↕!→L►↕@♦U→A7K2M►↕@♦U→7M3K9►♥

Locate from 7M3K9: where is session A7K2M?

Response (session found):

Pseudo:  [SOM] [SOW]![SOB]L[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [EOM]
Wire:    ☻↕!→L►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►♥

Located, to 7M3K9: session A7K2M is at BID 4T9X2.

11.7 Resume

Resume reconnects to an interrupted session. The remote instance responds with Status confirming the session state, its next message in the session, numbered from its own count (Section 11.1.2), or with an Exception if the session is no longer available. An instance that holds the session a Resume names, but will not resume it, responds with !X RESUME_DENIED, as does one holding that session ACTIVE. Only a session's opener sends a Resume: the opener answers a Resume of its own session with !X RESUME_DENIED, and an end that has moved waits for the opener to Locate it (Section 11.6) and resume the session.

A Resume answered with a Status moves the session to the Resume's sender, whose BID is its opener's from then on (Section 11.1.2): an instance that has reconnected holds a new BID (Section 5.4). A Resume answered with an Exception moves nothing.

Request:

Pseudo:  [SOM] [SOW]![SOB]U[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→U►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►♥

Resume session A7K2M, from 7M3K9 to 4T9X2.

Response (success):

Pseudo:  [SOM] [SOW]![SOB]T[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]D[SOB]47[EOW] [SOW]~[SOB]ACTIVE[EOW] [EOM]
Wire:    ☻↕!→T►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→47►↕~→ACTIVE►♥

Session resumed, from 4T9X2 to 7M3K9: SID=A7K2M, MID=47, state=ACTIVE. The Status is 4T9X2's next message, and each end goes on from its own count.

Response (failure):

Pseudo:  [SOM] [SOW]![SOB]X[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]~[SOB]SESSION_EXPIRED[EOW] [EOM]
Wire:    ☻↕!→X►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕~→SESSION_EXPIRED►♥

Resume refused, from 4T9X2 to 7M3K9: session A7K2M has timed out and been reclaimed.

11.8 Finish

Finish closes a session. The sender immediately transitions the session to IDLE and discards the SID. Finish is fire-and-forget — no response is expected. If the remote instance does not receive the Finish, its session timeout will eventually reclaim the session.

Pseudo:  [SOM] [SOW]![SOB]F[EOW] [SOW]@[SOR]U[SOB]A7K2M[EOW] [SOW]@[SOR]U[SOB]7M3K9[EOW] [SOW]@[SOR]U[SOB]4T9X2[EOW] [EOM]
Wire:    ☻↕!→F►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►♥

Finish session A7K2M, from 7M3K9 to 4T9X2. SID discarded.


12. Bus Transmission Rules


13. Standard Exception Codes

The following table lists every exception reason this specification defines. Implementations may define additional reason strings for application-specific errors. A reason an implementation defines is of Bus scope, and is addressed as Section 9.8 sets out. An error within a session is reported with a reason this table defines. Exception reasons are carried in a TEXT word following the !X symbol and the IDs that address the Exception (Section 9.8).

Reason Scope Meaning
BID_OVERFLOW Bus BID establishment exceeded maximum length
RELAY_FAILED Bus Message could not be forwarded to the next hop
MALFORMED_FRAME Bus Message is malformed (Section 7.4)
UNSUPPORTED_TYPE Bus Word type not supported by this instance
SERVICE_UNKNOWN Service This instance does not offer the requested service
SESSION_NOT_FOUND Session SID does not match any known session
SESSION_EXPIRED Session Session has timed out and been reclaimed
RESUME_DENIED Session Session cannot be resumed
SID_IN_USE Session SID already names a session the receiver holds
SESSION_LIMIT Session Receiver cannot hold another session

14. Conformance Requirements

This section defines the requirements for a conformant Antheos implementation: the minimum every instance implements, and the features beyond it that an instance claims whole.

14.1 Minimum Conformant Implementation

An instance MUST:

14.2 Optional Features

An instance MAY implement any of the features below. A feature is the symbols it lists, as this specification describes them, and an instance that claims a feature implements all of it and every feature it requires. A claim of conformance names the features the instance implements, and an instance that implements part of a feature does not claim it.

Feature Symbols Requires
Ping P, p (Section 9.5) —
Discover D, d (Section 9.6) —
Broadcast B (Section 9.4) —
Scaleback S, W (Sections 9.2 and 9.7) INTEGER
Exception X (Section 9.8) —
Relay R, with path addressing (Section 9.3) Discover, Exception, PATH, INTEGER
Service Q, O, A (Section 10) —
Sessions K, T, N, L, U, F (Section 11) Exception

Each word type beyond SYMBOL, ID and TEXT is a feature of its own, as is BLOB or STRING read beyond the sizes it declares.


15. Security Considerations

Antheos Level 1 provides no encryption, authentication, or integrity protection. The protocol is designed as a transparent transport layer: it carries what it is given.

An application that needs encryption or authentication provides them itself, in the content of its messages, and can find a service that offers them through Query/Offer/Accept. The Antheos frame structure (typed words, delimited boundaries) provides clear attachment points for security wrappers around message content.

SIDs are generated fresh per session, so that a SID does not link one session to another. A BID does: it is carried in clear in every session message, and links everything an instance does for as long as its bus connection lasts. The SID hash, FNV-1a (Section 11.2), is not a cryptographic hash, and this specification does not claim that a SID cannot be predicted. Knowledge of a SID allows interaction with the session.

A session's opener is the instance that sent its opening Call or, after a Resume, the one that resumed it (Section 11.1.2). Its BID is as self-asserted as any sender's, so the receiving peer can attribute the session to that BID, and to nothing more certain.


16. Licensing

MIT License

Copyright (c) 2025-2026 Are Bjørby are.bjorby@langsyn.org

Permission is hereby granted, free of charge, to any person obtaining a copy of this specification and associated documentation files (the "Specification"), to deal in the Specification without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Specification, and to permit persons to whom the Specification is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Specification.

THE SPECIFICATION IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SPECIFICATION OR THE USE OR OTHER DEALINGS IN THE SPECIFICATION.


Appendix B: Grammar

This appendix gives the syntax of a message, of each word and of each body in ABNF (RFC 5234), with the %s of RFC 7405 marking a string matched with its case. It is normative: where the text of this specification and this grammar disagree on syntax, the grammar governs. ALPHA, DIGIT, OCTET and SP are the core rules of RFC 5234 (Appendix B.1), and UTF8-char is the rule of RFC 3629 (Section 4).

A message the grammar does not match is malformed, or its frame is broken, as Section 7.4 sets out. A symbol is one this specification defines. The grammar does not state what the text does: the range a unit gives a value (§6.2.5, §6.2.8, §6.2.12), a MID's greatest value (Section 5.6), that a REAL or a COMPLEX part is held exactly (§6.2.6, §6.2.10), a SCIENTIFIC value's figures and exponent (§6.2.7), the days of each month and where a leap second falls (§6.2.2), the sort of a LOGICAL operand (§6.2.1), the length of the tail (Section 7.3), and which ID and payload words each symbol takes (Sections 8 to 11).

B.1 Message

message         = SOM head EOM tail
head            = symbol-word *id-word *payload-word
payload-word    = path-word / text-word / integer-word / real-word
                / scientific-word / logical-word / datetime-word
                / blob-word / bool-word / duration-word / complex-word
                / null-word / string-word
tail            = *OCTET     ; the blocks the BLOB and STRING words declare,
                             ; in their order and of their sizes (Section 7.3)
string-block    = *UTF8-char ; a STRING word's block (§6.2.12)

SOM             = %x02
EOM             = %x03
SOR             = %x04
SOU             = %x07
EOW             = %x10
SOW             = %x12
SOB             = %x1A

B.2 Words

word            = symbol-word / id-word / payload-word

symbol-word     = SOW %x21 SOB symbol EOW
symbol          = %s"E" / %s"C" / %s"B" / %s"P" / %s"p" / %s"R" / %s"D"
                / %s"d" / %s"S" / %s"W" / %s"X"
                / %s"Q" / %s"O" / %s"A"
                / %s"K" / %s"T" / %s"N" / %s"L" / %s"U" / %s"F"

id-word         = SOW %x40 ( SOB plain-id
                           / SOR %s"U" SOB base32-id
                           / SOR %s"D" SOB decimal-id ) EOW
path-word       = SOW %x2F SOB path EOW
text-word       = SOW %x7E SOB text EOW
logical-word    = SOW %x3F SOB logical EOW
datetime-word   = SOW %x26 SOB datetime EOW
duration-word   = SOW %x3A SOB duration EOW
bool-word       = SOW %x3D SOB ( "0" / "1" ) EOW
null-word       = SOW %x5F SOB EOW

integer-word    = SOW %x23 SOR ( %s"I" SOU count-unit SOB integer-2
                               / %s"O" SOU count-unit SOB integer-8
                               / %s"D" SOU count-unit SOB integer-10
                               / %s"H" SOU count-unit SOB integer-16
                               / %s"U" SOU count-unit SOB integer-32 ) EOW
real-word       = SOW %x24 SOR ( %s"I" SOU real-unit SOB real-2
                               / %s"O" SOU real-unit SOB real-8
                               / %s"D" SOU real-unit SOB real-10
                               / %s"H" SOU real-unit SOB real-16
                               / %s"U" SOU real-unit SOB real-32 ) EOW
complex-word    = SOW %x7C SOR ( %s"I" SOU real-unit SOB complex-2
                               / %s"O" SOU real-unit SOB complex-8
                               / %s"D" SOU real-unit SOB complex-10
                               / %s"H" SOU real-unit SOB complex-16
                               / %s"U" SOU real-unit SOB complex-32 ) EOW
blob-word       = SOW %x2A SOR count-flags EOW
string-word     = SOW %x22 SOR count-flags EOW
count-flags     = %s"I" SOU count-unit SOB count-2
                / %s"O" SOU count-unit SOB count-8
                / %s"D" SOU count-unit SOB count-10
                / %s"H" SOU count-unit SOB count-16
                / %s"U" SOU count-unit SOB count-32
scientific-word = SOW %x25 SOR %s"D" SOU decimal-unit SOB scientific EOW

count-unit      = %s"B" / %s"W" / %s"D" / %s"Q"
real-unit       = %s"W" / %s"D" / %s"Q"
decimal-unit    = %s"D" / %s"Q"

B.3 Identifiers, Paths and Text

plain-id        = 1*( %x21-39 / %x3B-7E )  ; printable ASCII but space and ":"
base32-id       = 1*id-char
id-char         = %x41-48 / %x4A-4E / %x50-5A / %x32-39
                                           ; A to Z but I and O, and 2 to 9
decimal-id      = "0" / nz-10 *dig-10
path            = base32-id *( "." base32-id )
text            = *%x20-7E

B.4 Numbers

A numeric body is written in the digits of its word's radix (§6.3), so each rule whose name ends in a base is given for each of the five.

dig-2           = %x30-31
dig-8           = %x30-37
dig-10          = %x30-39
dig-16          = %x30-39 / %x41-46
dig-32          = %x30-39 / %x41-56
nz-2            = %x31
nz-8            = %x31-37
nz-10           = %x31-39
nz-16           = %x31-39 / %x41-46
nz-32           = %x31-39 / %x41-56

count-2         = "0" / nz-2 *dig-2
count-8         = "0" / nz-8 *dig-8
count-10        = "0" / nz-10 *dig-10
count-16        = "0" / nz-16 *dig-16
count-32        = "0" / nz-32 *dig-32

integer-2       = "0" / [ "-" ] nz-2 *dig-2
integer-8       = "0" / [ "-" ] nz-8 *dig-8
integer-10      = "0" / [ "-" ] nz-10 *dig-10
integer-16      = "0" / [ "-" ] nz-16 *dig-16
integer-32      = "0" / [ "-" ] nz-32 *dig-32

magnitude-2     = count-2 [ "." *dig-2 nz-2 ]
magnitude-8     = count-8 [ "." *dig-8 nz-8 ]
magnitude-10    = count-10 [ "." *dig-10 nz-10 ]
magnitude-16    = count-16 [ "." *dig-16 nz-16 ]
magnitude-32    = count-32 [ "." *dig-32 nz-32 ]

real-2          = [ "-" ] magnitude-2
real-8          = [ "-" ] magnitude-8
real-10         = [ "-" ] magnitude-10
real-16         = [ "-" ] magnitude-16
real-32         = [ "-" ] magnitude-32

complex-2       = real-2 ( "+" / "-" ) magnitude-2 %s"j"
complex-8       = real-8 ( "+" / "-" ) magnitude-8 %s"j"
complex-10      = real-10 ( "+" / "-" ) magnitude-10 %s"j"
complex-16      = real-16 ( "+" / "-" ) magnitude-16 %s"j"
complex-32      = real-32 ( "+" / "-" ) magnitude-32 %s"j"

scientific      = [ "-" ] nz-10 [ "." 1*dig-10 ] %s"E" exponent
exponent        = "0" / [ "-" ] nz-10 *dig-10

B.5 Logical Expressions

An expression has no precedence: an operand of two operands is in parentheses, and the whole expression never is (§6.2.1).

logical         = binary / single
binary          = operand SP operator SP operand
operand         = single / "(" binary ")"
single          = variable / constant / unary
unary           = ( "~" / "-" ) ( variable / constant / "(" binary ")" )
operator        = "&" / "|" / "^" / "->" / "=" / "-" / "<" / ">"
                / "+" / "*" / "/"
variable        = ALPHA
constant        = "0" / nz-10 *dig-10

B.6 Dates and Durations

datetime        = year "-" month "-" day %s"T"
                  hour ":" minute ":" second [ "." fraction ] %s"Z"
year            = 4dig-10
month           = "0" nz-10 / "1" %x30-32
day             = "0" nz-10 / %x31-32 dig-10 / "3" %x30-31
                                         ; within its month (§6.2.2)
hour            = %x30-31 dig-10 / "2" %x30-33
minute          = %x30-35 dig-10
second          = %x30-35 dig-10 / "60"  ; 60 where §6.2.2 allows it
fraction        = *8dig-10 nz-10         ; 1 to 9 digits, none trailing zero

duration        = %s"PT0S"
                / [ "-" ] %s"P" ( days [ %s"T" clock ] / %s"T" clock )
days            = nz-10 *dig-10 %s"D"
clock           = hours [ minutes ] [ seconds ] / minutes [ seconds ] / seconds
hours           = ( nz-10 / "1" dig-10 / "2" %x30-33 ) %s"H"
minutes         = ( nz-10 / %x31-35 dig-10 ) %s"M"
seconds         = ( ( nz-10 / %x31-35 dig-10 ) [ "." fraction ] / "0." fraction ) %s"S"

B.7 Examples

Each word below is shown in wire form, with the rule it matches or the reason it matches none.

Word Reading
↕!→E► symbol-word
↕@♦U→4T9X2► id-word, a base32-id
↕@♦D→0► id-word, a decimal-id
↕/→AA.BB.CC► path-word
↕#♦H•B→-7F► integer-word, an integer-16
↕$♦D•W→0.5► real-word, a real-10
↕%♦D•D→6.02E23► scientific-word
↕|♦D•D→-0-0j► complex-word, a complex-10
↕?→~(A & B)► logical-word
↕:→-PT2H► duration-word
↕_→► null-word
↕@♦D→01► none: a decimal-id has no leading zero
↕#♦D•B→-0► none: an integer-10's zero has no sign
↕%♦H•D→1E2► none: a scientific-word's radix is D
↕?→(A & B)► none: a logical is never in parentheses whole
↕:→PT90M► none: minutes are below 60
↕&→2025-04-03T12:34:56.0Z► none: a fraction has no trailing zero

Appendix C: Revision History

This appendix is non-normative.

Every change to this document is a new version. A version whose row says that no requirement changed interoperates with the one before it; every other version is a wire break against the one before it: peers of the two do not interoperate. This document's date is that of its latest version.

Version Date Changes
1.11 November 2026 Section 2 says what Wrap and Tunnel means: existing hardware and software is wrapped in a shim, and Antheos is spoken through existing network protocols. No requirement changed.
1.10 November 2026 Section 1.1 says that O³ is pronounced "object cubed". No requirement changed.
1.9 November 2026 The rulings on an outside audit of Version 1.7: UNKNOWN_BID leaves Section 13; a holder answers a Locate that arrives once its session is suspended, and a requester may send its Locate again (Section 11.6); a session's controlling end is its opener, defined in Section 3, who alone sends requests and a Resume (Sections 11.1, 11.4, 11.7 and 15); and an end accepts every MID it has not received that is not stale, one below the next it expects included (Section 11.1.2).
1.8 November 2026 The Establish procedure grows its candidate after a collision and gives up once the length passes the maximum, as Sections 5.4 and 9.1.3 state, and an instance keeps its nonce while it holds its BID, as Section 9.1.2 needs (Section 9.1.1). The security considerations name a forged Acknowledge, an Offer from an instance that does not provide the capability, and a tail size made larger by corruption (Section 15). Section 1.1 no longer says that instances are authenticated, and Appendix B no longer cites Appendix A. No requirement changed.
1.7 November 2026 The title is Level 1 Specification - Transport, and the footer names its version. No requirement changed.
1.6 November 2026 Verify leaves this document, with its symbols, and Sections 9.8 and 9.9 become 9.7 and 9.8: Level 1 does not expose an instance's OID:DID:IID, which is left to the device's designer (Section 5.3). An instance needs none of them to conform, an OID need not be registered (Section 5.1), and a SID is a hash of its counter and device entropy alone (Section 11.2). A session's other end refuses another instance's Call at any MID and drops its other messages (Section 11.1.2), a Resume moves a session to its sender (Section 11.7), and SESSION_LIMIT refuses a session an instance cannot hold (Sections 11.1.1 and 13).
1.5 November 2026 Section 14 leaves this document, and Sections 15 to 17 become 14 to 16. The Introduction holds the one thing this specification says of what is built on it, that it is a foundation; the others leave, from the security considerations, Appendix B and this history. No requirement changed.
1.4 October 2026 Appendix A leaves this document, with its symbol, its kind of plain ID, its exception reason and its row of features. The appendices that remain keep their letters.
1.3 October 2026 The rulings of the publish-readiness review, among them: an Establish carries a nonce (Section 9.1.1), an Exception names its sender and the instance it answers (Section 9.8), an Accept names its sender first and a Query may name its target (Section 10), RELAY_FAILED carries the BID it could not reach and returns along the Relay's path (Section 9.3.3), a Call carries its trace identifier as an ID word (Section 11.3), five exception reasons are removed (Section 13), and Appendix B gives the grammar, which governs syntax.
1.2 October 2026 The rulings of the Version 1.1 review, among them: every session message names its sender (Section 11), each end numbers its own messages from 1 (Section 11.1.2), responses have symbols of their own (Section 8.1), a Scaleback and its Acknowledge carry two BIDs (Sections 9.2 and 9.7), an ID word has one of three forms (Section 5.7), and a malformed message is read to its end (Section 7.4).
1.1 October 2026 Fifteen word types, and one spelling for each value (Section 6). TEXT moves to ~ and holds printable ASCII, STRING takes " and carries UTF-8 in the tail, DURATION moves to :, DATETIME is UTC only, BOOL, COMPLEX and NULL are new, and LOGICAL has no precedence.
1.0 February 2026 The first version.

— End of the Antheos Protocol Specification, Version 1.11 —