Antheos Object Layer

This specification is a draft. It may change in ways that break compatibility, and no implementation of it is published at present.

ANTHEOS_OBJECT_LAYER_SPEC.md (Markdown, 24,500 bytes)

Version 0.2 — Level 3 Specification - Objects

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

This is a draft. It has been exercised in no implementation and on no bus.


Contents

  1. Introduction
  2. Design Principles
  3. Terms and Definitions
  4. Carriage
  5. Frames
  6. Objects
  7. Dispatch
  8. Events
  9. Level 1 Behaviour
  10. Conformance
  11. Cost
  12. Security Considerations
  13. Licensing

1. Introduction

Antheos Level 1 is a procedural protocol: every verb does exactly one thing (Level 1 Section 1).

This document defines the Antheos Object Layer, a Level 3 protocol that lets two instances work in classic object-oriented terms: objects that answer to names, overloading chosen by the types of what a name is given, classes and inheritance, exceptions, and events. It is carried in BLOBs inside Level 1 messages, over Level 1 alone or under Level 2 or a layer that replaces it.

"Level 1" means the Antheos Protocol, Version 1.10, Level 1 Specification - Transport. "Level 2" means the Antheos Security Layer, Version 0.3, Level 2 Specification - Security, a draft. "Level 1 Section 11.1.2" refers to the first, "Level 2 Section 6.1" to the second, and "Section 6" to this document.

The layer knows Level 1 and Level 2 and nothing above itself. It is a foundation for higher-level protocols.

The layer needs no change to Level 1 or Level 2: no new symbol, word or verb in either. Neither requires it or knows of it, and an instance that does not run it conforms to both. Each of its verbs takes the letter and the form of a Level 1 verb.

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.


2. Design Principles


3. Terms and Definitions

Term Definition
Frame A Level 3 message: a complete message in Level 1's encoding, carried in the block of a BLOB (Section 4.1)
Caller The session's opener (Level 1 Section 3), which alone invokes
Holder The session's other end, which holds the objects the caller invokes
Object What the holder holds and the caller invokes, which answers to names
Root The object a session reaches without naming one (Section 6.1)
Reference A plain ID word that names one object within its session (Section 6.2)
Name The text that says what an invoke asks for, or what an event reports (Section 7.1)
Implementation What the holder runs for one name of one object and one list of argument types
Class The holder's own kind of an object. It never appears on the wire (Section 7.3)
Invoke A frame that asks an object to run a name (Section 5.1)
Answer A frame that carries what an invoke returned (Section 5.2)
Exception A frame that reports an invoke matched nothing, or that its implementation failed (Section 5.3)
Release A frame that ends a reference (Section 5.4)
Event A frame from the holder that reports something of an object (Sections 5.5 and 8)

4. Carriage

4.1 Frames

A frame is a complete message in Level 1's encoding: SOM, a SYMBOL word, its ID and payload words, EOM, and its own tail for its BLOB and STRING words (Level 1 Section 7). Every word in it is a Level 1 word and is read as Level 1 reads it. A frame's grammar is Level 1's (Level 1 Appendix B), with its symbol rule replaced by this one:

symbol          = %s"K" / %s"k" / %s"X" / %s"F" / %s"N"
Symbol Frame Form of Carried in
K Invoke Call (Level 1 Section 11.3) A Call from the caller
k Answer A response, its request's symbol in lower case (Level 1 Section 8.1) The Call that answers an invoke
X Exception Exception (Level 1 Section 9.8) The Call that answers an invoke
F Release Finish (Level 1 Section 11.8) A Notify from the caller
N Event Notify (Level 1 Section 11.5) A Notify from the holder

A frame carries no SID, BID or MID. The Level 1 message that carries it names the session and its ends.

A frame that this grammar does not match, that the message the table names does not carry, or that breaks a rule of Section 5, is dropped, as Level 1 drops a malformed message (Level 1 Section 7.4). It moves no Level 1 state: to the instance it is a message that was lost, as Level 2 treats a record it refuses (Level 2 Section 8.3).

4.2 Calls and Notifies

In a session that runs the layer, every Call and every Notify carries exactly one frame, as the block of one BLOB word, and no other payload word. Under Level 2 the frame is the content of the record the Call or Notify carries (Level 2 Section 6.1).

The wrap message (Level 1 Section 11.1.2) is a Call with no payload. It carries no frame. Status, Locate, Resume, Finish, Exception and Scaleback carry none either.

An invoke is answered by Level 1's own rule: the holder's next Call answers the caller's Call (Level 1 Section 11.1.2), and carries the invoke's answer or exception.

4.3 Whether a Session Runs the Layer

That is fixed before a session's first Call, by what the two devices were built to do or by the capability an Accept took (Level 1 Section 10.3), as Level 2 fixes its own (Level 2 Section 7.1). It is never negotiated within the session.

The layer's capability is antheos-objects-1. A Query or Offer for it is in clear, like any other.


5. Frames

5.1 Invoke

An invoke is !K, then its ID words, then the name, then its arguments.

An invoke's arguments are its reference arguments, then its payload words after the name, in that order. An implementation therefore takes its object parameters before its other parameters, as Level 1's word order puts ID words before payload words (Level 1 Section 7.6).

Frame:   ☻↕!→K►↕~→newCart►♥

Invoke newCart on the root, with no arguments.

Frame:   ☻↕!→K►↕@→cart7►↕@→item2►↕~→add►♥

Invoke add on cart7, passing the object item2.

5.2 Answer

An answer is !k, then nothing, when the implementation returns nothing, or one word, the value it returns: a reference as a plain ID word, and any other value as a payload word. A NULL word is a returned NULL, not an absence (Level 1 Section 6.2.11).

Frame:   ☻↕!→k►↕@→cart7►♥

The invoke returned the object cart7.

Frame:   ☻↕!→k►♥

The invoke returned nothing.

5.3 Exception

An exception is !X, then one TEXT word, its reason: a Level 1 Exception's form (Level 1 Section 9.8) without its ID words. It answers an invoke in the place of an answer, and a caller tells the two apart by the symbol, never by the content.

Its reason is one of:

Frame:   ☻↕!→X►↕~→METHOD_UNKNOWN►♥

The invoke matched no implementation.

5.4 Release

A release is !F, then one plain ID word, the reference it ends (Section 6.3). Only the caller sends one, in a Notify. It is fire-and-forget, as a Finish is (Level 1 Section 11.8), and nothing answers it.

Frame:   ☻↕!→F►↕@→item2►♥

End the reference item2.

5.5 Event

An event is !N, then the reference of the object it reports on, then the references it carries, then its name, then its arguments, in the order of an invoke (Section 5.1). Only the holder sends one, in a Notify. Section 8 says when.

Frame:   ☻↕!→N►↕@→cart7►↕~→changed►↕#♦D•B→3►♥

cart7 reports changed, with the INTEGER 3.


6. Objects

6.1 The Root

A session's root is the object its holder exposes for the capability an Accept took (Level 1 Section 10.3), or, in a session opened directly (Level 1 Section 11.1.1), the object the two devices were built to expose. Its reference is root.

6.2 References

A reference is a plain ID word (Level 1 Section 5.7): one or more characters of printable ASCII other than space and colon. The holder chooses it, and it names one object within its session. To the caller it is opaque: the caller keeps it and sends it back, and reads nothing from it.

A holder MUST NOT derive a reference from its OID, DID or IID, and MUST NOT give any object but the root the reference root.

A reference names nothing outside its session. It names no BID, so a Resume, which moves a session to a new BID (Level 1 Section 11.7), leaves it naming the same object.

6.3 Lifetime

A reference lives until its session ends (Level 1 Section 11.1.4) or the caller releases it (Section 5.4), whichever comes first. A release that is lost leaves the object live until the session ends, as a session timeout covers a lost Finish (Level 1 Section 11.8).

An invoke whose target or reference argument names no live object, one released or never given, matches nothing (Section 7.4).


7. Dispatch

7.1 Names

A name is the sender's own text (Level 1 Section 7.7): non-empty, a TEXT word when it is printable ASCII and a STRING word otherwise, the two read alike. Two names are the same when their code points are, unnormalized, as two strings are (Level 1 Section 6.2.12). A symbol such as + is a name like any other, so an operator is overloaded as any other name is.

7.2 Matching

An object has, for each of its names, any number of implementations. Each takes a fixed number of arguments, each of one type.

An implementation matches an invoke when the invoke names it on its object, carries as many arguments as it takes, and each argument matches its parameter:

Two implementations of one name on one object MUST NOT take the same number of arguments of the same types in the same order, and two that differ only in a unit take the same types. So an invoke whose arguments are all payload words matches one implementation or none, and is never ambiguous.

7.3 Classes

Classes are the holder's. Only the caller invokes (Level 1 Section 11.1.2), so every reference an invoke carries names an object the holder holds, and the holder knows each object's class.

A reference argument matches a parameter by its object's class, under the holder's own rules: single or multiple inheritance, the most specific implementation or the exact class, as its implementation has them. An invoke those rules cannot resolve to one implementation matches none.

No class, and nothing of a class, appears on the wire.

7.4 No Match

An invoke that matches no implementation is answered with an exception whose reason is METHOD_UNKNOWN. That one reason covers a name the object does not have, a name with no implementation for the invoke's number and types of arguments, a value outside the unit its parameter takes, a reference that names no live object, and an invoke the holder's class rules cannot resolve. A caller learns only that this invoke has no implementation there.


8. Events

The holder sends an event (Section 5.5) only for an object and an event the caller asked for. How the caller asks, and how it stops asking, are the object's own names, invoked like any other. This layer has no verb for either. An event the caller did not ask for is not sent, as an Offer answers only a Query (Level 1 Section 10.2).

An event may be lost, and is never confirmed or answered, as a Notify is (Level 1 Section 11.5). A caller that must not miss a change reads the object's state by one of its names, which it may do whenever the holder's MIDs show a gap (Level 1 Section 11.1.2).

A release ends its object's events, and a session's end ends them all. A caller drops an event that names a reference it does not hold.


9. Level 1 Behaviour

One at a time: The caller has one invoke outstanding, since Level 1 has one request outstanding (Level 1 Section 11.1.2).

Retransmission: A Call sent again carries its frame unchanged. The holder answers it again with the answer or exception it kept, and does not run the implementation twice (Level 1 Section 11.1.2).

MID wrap: The wrap message carries no frame (Section 4.2).

Resume: References survive it (Section 6.2). The caller is the session's opener, so after a Resume the instance that resumed it invokes (Level 1 Section 11.7).

Reset: A holder that resets has lost its session and its objects. The caller's next Call draws SESSION_NOT_FOUND (Level 1 Section 11.1.1), and its references died with the session.

Scaleback: A frame is in a tail, so a Scaleback's tail size bounds it (Level 1 Section 9.2).


10. Conformance

An end that runs this layer MUST:

A holder implements the word types its implementations take. An argument of a type it does not implement matches no implementation (Section 7.4).


11. Cost

This section is non-normative.

A frame costs 7 bytes beyond its words: SOM, EOM and its SYMBOL word. Over Level 1 alone, the BLOB word that carries it costs 8 bytes and the digits of its size, so a frame of under 100 bytes costs 17 bytes in all beyond its words. Under Level 2, a record adds 21 bytes to its content (Level 2 Section 6.3).

The frames of Appendix A, in bytes:

Frame Bytes
Invoke newCart on the root 18
Answer cart7 16
Invoke watch on cart7 25
Answer nothing 7
Invoke add with a TEXT and an INTEGER 41
Event changed with an INTEGER 36
Invoke add with a REAL 34
Exception METHOD_UNKNOWN 25
Release cart7 16

12. Security Considerations

Over Level 1 alone: Every frame is in clear: names, arguments, references, answers, events and the reasons of exceptions. Anyone on the bus reads them. Anyone who has seen a SID can send an invoke, a release or an event in it, as any message of a session can be forged (Level 1 Section 15).

Under Level 2: Frames are content, sealed between the session's two ends (Level 2 Section 13). What Level 2 leaves visible stays visible: who talks to whom, when, and every message's size, so a frame's size can show which name was invoked. This layer does not pad.

What METHOD_UNKNOWN hides: Which names an object has, which references are live, and which types its implementations take. A prober learns only that one invoke matched nothing. A reason an implementation defines shows that its implementation ran.

Classes: None appears on the wire, so the protocol tells a peer nothing of the holder's classes. The names an object answers to, and the types they take, are visible to its caller.

Identity: No frame names a device, and no reference may be derived from an OID, DID or IID (Section 6.2). A reference links the invokes that use it, within its session only.

References are not secrets: The holder chooses them, and over Level 1 alone they are in clear. Nothing in this layer depends on a reference being hard to guess. Under Level 2, only the session's authenticated other end can invoke.

What can be lost: An event, and a release. A lost event goes unnoticed unless the caller reads the object's state (Section 8). A lost release leaves an object live until the session ends (Section 6.3).


13. Licensing

MIT License

Copyright (c) 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 A: Worked Example

This appendix is non-normative.

The holder 4T9X2 exposes a shopping cart as its root. The caller 7M3K9 has opened session A7K2M with it, over Level 1 alone, and the two devices were built to run this layer. Each step shows the Level 1 message, then the frame in the block of its BLOB. The holder's cart7 has one implementation of add, taking a TEXT and an INTEGER whose widest unit is D.

1. The caller invokes newCart on the root.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→1►↕*♦D•B→18►♥
Frame:   ☻↕!→K►↕~→newCart►♥

2. The holder answers with a reference to a new object.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→1►↕*♦D•B→16►♥
Frame:   ☻↕!→k►↕@→cart7►♥

3. The caller asks to watch cart7, by a name the cart has for it (Section 8).

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→2►↕*♦D•B→25►♥
Frame:   ☻↕!→K►↕@→cart7►↕~→watch►♥

4. The holder answers with nothing.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→2►↕*♦D•B→7►♥
Frame:   ☻↕!→k►♥

5. The caller invokes add with a TEXT and an INTEGER. The INTEGER is sent with unit B, and the parameter's widest unit is D, which holds 3, so the implementation matches.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→3►↕*♦D•B→41►♥
Frame:   ☻↕!→K►↕@→cart7►↕~→add►↕~→apple►↕#♦D•B→3►♥

6. The holder answers with nothing.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→3►↕*♦D•B→7►♥
Frame:   ☻↕!→k►♥

7. The cart has changed, and the caller asked to watch it, so the holder sends an event in a Notify. It answers nothing.

Wire:    ☻↕!→N►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→4►↕*♦D•B→36►♥
Frame:   ☻↕!→N►↕@→cart7►↕~→changed►↕#♦D•B→1►♥

8. The caller invokes add with one REAL. cart7 has no implementation of add that takes one argument, so the invoke matches nothing.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→4►↕*♦D•B→34►♥
Frame:   ☻↕!→K►↕@→cart7►↕~→add►↕$♦D•W→3.5►♥

9. The holder answers with an exception. Its MID is 5, since its event took 4.

Wire:    ☻↕!→K►↕@♦U→A7K2M►↕@♦U→4T9X2►↕@♦U→7M3K9►↕@♦D→5►↕*♦D•B→25►♥
Frame:   ☻↕!→X►↕~→METHOD_UNKNOWN►♥

10. The caller releases cart7 in a Notify, and nothing answers it. Its events end with it.

Wire:    ☻↕!→N►↕@♦U→A7K2M►↕@♦U→7M3K9►↕@♦U→4T9X2►↕@♦D→5►↕*♦D•B→16►♥
Frame:   ☻↕!→F►↕@→cart7►♥

Under Level 2, each frame above is the content of a record instead: an invoke's of a Request, an answer's or an exception's of an Answer, and an event's or a release's of an Event (Level 2 Section 6.2).


Appendix B: Revision History

This appendix is non-normative.

Version Date Changes
0.2 November 2026 The contents list Appendix A and Appendix B as items of their own, where they ran together as one line. No requirement changed.
0.1 November 2026 The first draft.