GLP Networking API Specification
Analyze & explore related research in Conceptio
Connect this record directly into your research workflow or AI agent. Explore citations across open repositories, query full text, and export structured proof bundles.
arXiv:2610.32583v1 [cs.NI] 23 Oct 2026
GLP Networking API Specification Dan Bachar
Ehud Shapiro
Technical University of Munich and Technion–Israel Institute of Technology
London School of Economics and Weizmann Institute of Science
Abstract Multiagent deterministic GLP (madGLP) [7] runs Grassroots Logic Programs (GLP) [5] as agents that communicate solely by asynchronous message passing, and assumes fair delivery: every message sent is eventually delivered. Each agent runs on a person’s smartphone and is named by the person’s public key. A grassroots platform depends on no global resource other than the network [4, 6], so the agents must reach one another face to face over Bluetooth Low Energy, on a local network, and across the Internet from behind NAT, with rendezvous servers that anyone can run for connection establishment. What madGLP requires of such a network, and what the network requires of the platform above it, has not been specified. Here, we specify the networking layer beneath madGLP. The guiding principle is that anything that can be done in GLP is done in GLP: the layer provides what GLP cannot reach by itself — the network stack, the platform’s location service, the platform’s attestation of the application it runs, and cryptographic primitives — and everything above that line is GLP code: the social graph, friendship, introduction, the distribution of addresses and the reconnection of peers whose address changed, and the rendezvous protocol. We specify three transport underlay realizations, all following one interface. Over the PAN and LAN transport underlays, an agent advertises a rotating recognition token derived from its key and the current time slot, so that a peer who knows the key recognises it before any contact and nobody else can track it; whether an agent answers an unrecognised contact is a trust level GLP sets per underlay, raised on entering a place GLP declared and lowered on leaving it. Over the WAN underlay, a rendezvous server is a host with a static address running the headless layer and a GLP rendezvous agent, which observes addresses and coordinates hole punching, relays no payload, and belongs to no social graph. On every underlay, the peers establish a mutually authenticated Noise session from their Ed25519 keys, IK when the initiator already knows the responder’s key and XX only for a first contact under Open trust, and then attest to each other, per session and bound to the session’s handshake hash, that each runs the application published under its developer’s key, verified against Apple’s and Google’s public roots with no third party contacted during a session; we state what each platform’s attestation proves and what it does not, and the runtime reports to the program whether a peer is attested. One message protocol carries payloads on every underlay, over UDP datagrams and over GATT writes, with fair, unordered, atomic and authenticated delivery, which is what madGLP assumes.
1
1
Introduction
Multiagent deterministic GLP (madGLP) [7] is a distributed runtime for Grassroots Logic Programs (GLP) [5] in which autonomous agents communicate solely by message passing, each agent on a person’s smartphone and named by the person’s public key. This document specifies the networking layer beneath madGLP: the interface that enables madGLP agents to run on separate devices communicating peer-to-peer, and the layer functions behind the system predicates by which GLP programs reach it. madGLP and the runtime’s side of the seam are specified in the implementation paper [7]; the seam predicates are in the language paper’s catalogue [5]; the operating system above the runtime, the Grassroots Super-App with its social graph (in preparation), is what decides whom to befriend and what an attested or unattested peer may be used for. Layering. The guiding principle is that anything that can be done in GLP is done in GLP: the networking layer provides only what GLP cannot reach by itself—the network stack (PAN, LAN and WAN), the platform’s location service (geofencing), the platform’s remote attestation of the application it runs, and cryptographic primitives (Ed25519 signing and verification, the Noise handshake). Everything above this line is GLP agent code. In particular, the social graph [6], friendship, introduction, the distribution of addresses, the reconnection of peers whose address changed, and the rendezvous protocol are implemented entirely in GLP and are not part of this specification; to the networking layer, all madGLP messages are opaque payloads. The interface between the two consists of the API defined here together with ten system predicates of the language [5], whose layer functions Section 5 specifies. One overlay, three underlays. The API is underlay-agnostic at its core: madGLP issues the same calls regardless of how a message travels. Three underlays realize the core, the overlay being the one address space of public keys over them: • PAN (Bluetooth Low Energy, Section 3.3): communication between devices in direct radio range, enabling offline, infrastructure-free operation. The PAN is the bootstrap underlay where agents first meet face-to-face. • LAN (a local network, Section 3.2): communication between devices on the same local network, found by DNS-SD over multicast DNS and carried over IP, with no radio and no server. • WAN (Internet Protocol, Section 4): communication between remote devices at arbitrary distances, using UDP datagrams, with rendezvous servers for signaling and NAT traversal. Some control operations are inherently underlay-specific—observing the agent’s own public address is IP-only—and are specified with their underlay rather than in the agnostic core, and called on that underlay’s sub-object (Section 2.2). PAN and LAN discovery—finding agents within radio range or on the same local network—spans both underlays.
2
Outline. Section 2 specifies the underlay-agnostic API the madGLP runtime calls. Section 3 specifies proximity PAN/LAN-based discovery and communication: its principles, then its realizations. Section 4 specifies the WAN realization. Section 5 specifies the layer functions behind the ten system predicates through which GLP agent code reaches the layer. Section 6 specifies the rendezvous server as a separate process, by its networking requirements only. Section 7 summarizes the complete API and its correspondence to madGLP.
2
Networking API
This section specifies the underlay-agnostic interface between the madGLP runtime and the networking layer. The madGLP runtime issues these calls without regard to how a message travels; the PAN (Section 3.3), LAN (Section 3.2) and WAN (Section 4) realizations carry them. Throughout, a peer is named by its public key. An Underlay is one of PAN, LAN or WAN; a peer may be reachable over more than one underlay at a time. The proximity underlays are PAN and LAN, on which a nearby agent is discovered (Section 3). A LAN connection and a WAN connection are both IP connections; the layer tells them apart by whether the peer’s address is on the agent’s own local network (Section 2.8). An IP connection is a LAN connection when the layer runs LAN and the peer’s address is local; otherwise it is a WAN connection when the layer runs WAN; otherwise the layer neither makes nor accepts it. The underlays are independent realizations: an agent may implement any subset of them and remains a conforming agent—identity, known peers, sessions, point-to-point delivery, trust, and the networking assumptions (Section 2.11) are unchanged, and an unimplemented underlay is simply absent from discovery and peerUnderlays, and has no sub-object (Section 2.2). In particular, a server-side agent—a rendezvous server (Section 6), or a service co-hosted with one—realizes the WAN underlay alone, headless, with proximity discovery absent.
2.1
Identity
Each agent has an Ed25519 key pair. The 32-byte public key is the agent’s globally unique identity, used for addressing, authentication, and signing, and is persistent across sessions. The layer holds the private key, signs with it and authenticates the sender of every message, and is part of the application the platform attests (Section 2.6). The key pair is generated and persisted above the networking layer and is supplied to the layer’s constructor when it is instantiated; it is immutable for the layer’s lifetime. Key rotation or a move to another device is realized by instantiating the layer with another pair. Function
Signature
Description
Retrieve identity
getIdentity() → PubKey
Return the agent’s public key.
3
2.2
Instantiation
The layer is created once per agent, with everything it needs to run; nothing in it changes for the layer’s lifetime. Parameter
Type
Description
Key pair
Ed25519 key pair
Underlays
Underlay[]
Server address
Address?
Rendezvous server
boolean
The agent’s identity (Section 2.1). The underlays the layer runs: a subset of PAN, LAN, WAN; a headless server runs WAN alone (Section 2). A rendezvous server’s listening address (Section 6); null on a smartphone. Whether the agent is a rendezvous server (Section 6).
The layer exposes one sub-object for each underlay it runs, pan, lan and wan, and null for an underlay it does not run. The functions of this section are called on the layer itself, as they span all underlays: the layer chooses the underlay that carries each message, and reports reachability over all of them, so the callbacks that concern a peer carry the Underlay they refer to. The functions that belong to one underlay are called on its sub-object, and take no underlay parameter: discovery and the trust level on pan and lan (Section 3.1), and the public address, peer addresses, hole punching, the rendezvous servers and the local network identity on wan (Section 4). Thus, a call that makes no sense on an underlay, for example a trust level on the WAN, cannot be written, and a call on an underlay the layer does not run finds no sub-object.
2.3
Connection and Reachability
The networking layer notifies the runtime when peers become reachable or unreachable, over any underlay, and reports the underlay(s) by which a peer is currently reachable. Reporting the underlay lets GLP make proximity-aware decisions—for example, GLP computes which of its friends are physically nearby by intersecting its friends list with the peers currently reachable over PAN. Which underlay carries a given message, and which path or rendezvous server is used, remain the networking layer’s choice. The connection report says whether the peer’s identity is attested: it carries the attested application, or null for an unattested identity (Section 2.6), and the runtime reports it to the program [7]; what an unattested peer may be used for is GLP’s decision.
4
2.4
Function
Signature
Description
Peer connected
onPeerConnected(pk, underlay, application?)
Peer disconnected
onPeerDisconnected(pk, underlay)
Check reachability
isPeerReachable(pk) → bool
Reachable underlays
peerUnderlays(pk) → List<Underlay>
Callback when a peer becomes reachable for communication. Receives the peer’s public key, the Underlay over which it connected, and, for an attested identity, the attested application: its developer, its name and its version number; null for an unattested identity (Section 2.6). Callback when a peer is no longer reachable over a given Underlay. Receives the peer’s public key and that underlay. Whether the given peer is currently reachable via any underlay. The underlays by which the given peer is currently reachable (empty if unreachable).
Known Peers
The layer holds no social graph. It holds a known-peer set: the public keys GLP has supplied, each optionally carrying a last recorded address. This set is what Closed trust (Section 2.8) recognizes, what unsolicited inbound WAN contact is checked against, what reconnection attempts range over, and where putPeerAddress (Section 4.2) records addresses. Whether a known peer is a friend is GLP’s interpretation; removing a key removes the layer’s recognition of it. Function
Signature
Description
Add known peer
putKnownPeer(pk)
Remove known peer
removeKnownPeer(pk)
Add the public key to the known-peer set. Remove the key and any recorded address from the known-peer set.
5
2.5
ANNOUNCE and Liveness
ANNOUNCE is the protocol’s identity beacon and heartbeat. It is a self-contained signed record whose payload carries: • the sender’s full 32-byte public key; • the protocol version (2 bytes); • a trailing Ed25519 signature over all preceding bytes. The signature is verified against the carried sender key before the record is accepted, and repeated ANNOUNCEs from the same key are idempotent. Role per underlay. ANNOUNCE serves a different mix of purposes on each underlay: • Over the PAN it is, first, the identity bootstrap of a first contact under Open trust: a scan match of an unrecognized presence carries no public key (Section 3.3.2), so ANNOUNCE supplies the key that the Noise XX handshake that follows checks (Section 2.6). A recognized presence needs no ANNOUNCE before the handshake, as its token already names the key the IK handshake expects. Second, it is a second liveness check : link death is normally reported by the link layer itself, and the announce-driven sweep covers cases where no disconnect is ever surfaced, which would otherwise leave the peer attached indefinitely. The staleness clock counts any authenticated traffic; ANNOUNCE guarantees a floor of traffic on an idle link. When this check marks a connection as stale, the agent severs both GATT legs before synthesizing the disconnect. • Over LAN and WAN it is the liveness signal : a UDP path has no connection state; a session silent for two consecutive intervals is torn down, feeding onPeerDisconnected (Section 2.3). Over the WAN, identity plays no bootstrap role: a WAN connection is initiated by address with the peer’s key already expected (Section 2.6); on the LAN, ANNOUNCE bootstraps identity only for a first contact under Open trust, as on the PAN. An inbound connection is identified by its completed handshake. Heartbeat. Once the session is established, ANNOUNCE is sent inside it, and repeats periodically (every 10 s in the implementation) on every live connection of every underlay; only the ANNOUNCE of a first contact under Open trust travels before the handshake. A peer silent for two consecutive intervals is stale on that underlay, independently per underlay, with the per underlay consequences above. The same heartbeat tick drives reconnection attempts towards unreachable known peers and rendezvous servers and the replay of queued messages once a recipient is live again (fair delivery, Section 2.11). Trust gating. Under Closed trust on a proximity underlay (Section 2.8), no ANNOUNCE is sent or answered before the handshake: the agent contacts a recognized peer with the IK handshake, and an ANNOUNCE arriving before a handshake is ignored and its connection dropped. Unsolicited WAN contact is gated by the known-peer set instead (Section 2.8). Address distribution is not ANNOUNCE’s role: peer addresses reach the layer via 6
putPeerAddress (Section 4.2), fed by GLP. Over LAN and WAN every connected peer is already authenticated.
2.6
Session Establishment
Once a transport-level connection to a peer exists—a GATT connection on the PAN (Section 3.3) or a UDP path on the LAN or WAN (Section 4)—the peers perform a mutual authentication handshake using their Ed25519 keys via Noise (Curve25519 for key exchange, ChaCha20-Poly1305 for encryption, SHA-256 for hashing). The handshake proves that each peer holds the private key of its static public key, and derives forward-secret session keys. The Noise pattern depends on what the initiator already knows. When the initiator expects a specific responder key, it runs Noise IK: it sends its own static key in the first message, encrypted to the expected key, so only the holder of that key can read or answer it, and the responder never transmits its static key. The initiator expects a key for a known peer (Section 2.4), for a recognized proximity token (Section 3.1), for a rendezvous server, whose key GLP declared (Section 4.2), and on every WAN connection, since the layer connects over the WAN only to known peers (Section 4.4). Only a first contact with an unrecognized presence under Open trust (Section 2.8) has no expected key: there the peers exchange ANNOUNCE first (Section 2.5) and run Noise XX, in which each side transmits its static key, and each verifies the delivered key against the key ANNOUNCE claimed; a mismatch aborts the connection. The responder enforces the trust level: under Closed trust, and on the WAN except on a rendezvous server, it answers an IK first message only from a key in the knownpeer set, and answers no XX first message, so a stranger never learns its key. A recorded IK first message can be replayed, and only the responder can answer it, so its recorder can test whether the same responder is still present, though it learns no key. The Curve25519 static key carried by Noise is the birational image of the agent’s Ed25519 identity key, so the expected static is computable from the peer’s Ed25519 public key alone; no separate key distribution is required. The handshake needs no ordered carrier: it is a strict request–response exchange, self-ordering by construction: each message is sent only after the previous one arrived; over a lossy carrier each handshake message is retransmitted on a timer until the next one arrives, and the final one, which has no reply (message 2 of IK, message 3 of XX), until the other side’s attestation message arrives (below), transport messages are sent only after the handshake completes, and the session’s replay protection makes duplicated packets harmless. Sessions are per underlay: one handshake per underlay per pair, so a pair reachable over more than one underlay holds a session on each. A peer becomes reachable over an underlay (Section 2.3) once its session on that underlay is established and authenticated, and the attestation exchange on it has completed, with the peer either attested or declared unattested. Attestation. The handshake is followed by a mutual attestation exchange, and neither peer is reported reachable until it completes. The exchange has two parts, one long-lived and one per session. Once, the layer asks the platform to generate an attestation key in its secure element and to attest it over a challenge naming the agent’s identity public key pk: App Attest on iOS, hardware key attestation on Android. For each session, the layer asks the platform to sign with that key the digest H(“glp attest” | pk | v | h), where v is the application’s version 7
number and h is the final Noise handshake hash. Each side sends one attestation message per session, as a message of the Message Protocol (Section 2.10) after the final handshake message: either its attestation and this signature, or a statement that its platform provides no attestation. The receiver verifies the attestation against the platform’s root, and the signature against the key the attestation carries; either failing tears the session down, while the identity of a peer that sends the statement is reported unattested: onPeerConnected carries null for the application. Since any agent can declare itself unattested, including one running a repackaged application or holding a copied identity key, attestation protects only the uses GLP restricts to attested peers. The receiver waits for the peer’s attestation message as long as the sender’s attempts to deliver it last, then tears the session down; an attestation message is never queued for the next session. A payload arriving on a session whose exchange has not completed is dropped unacknowledged, so the sender’s retransmission delivers it once the exchange has completed. The attestation binds the attestation key to the application and to the identity key, and the signature binds both, with the version number, to this session. We bind the signature to the handshake hash because an identity key is long-lived: an attestation over the key alone could be replayed onto every session the agent opens, while h is unique per session, identical at both ends, and chosen by neither side alone. The attestation itself is long-lived, since a platform fixes the challenge when it generates the key, and a per-session challenge would need a new secure-element key per session. On iOS the attestation key remains valid through updates of the application, so an updated application needs no new attestation and signs its new version number in each new session; reinstalling the application, migrating it to another device or restoring the device from a backup needs a new attestation key [1]. Nothing of the session is cached, so a peer that restarts proves itself again before it is reachable. The operating system above the runtime relies on two properties of this exchange: an attestation cannot be replayed onto another session, and the layer only verifies attestations, while GLP decides what an unattested peer, such as a rendezvous server (Section 6), may be used for. The key the platform holds is the attestation key, not the identity key. The identity key is Ed25519 with the Curve25519 static above, and hardware key stores hold other curves— Apple’s Secure Enclave holds P-256 only—so the identity key lives in the application’s own store. The attestation key is generated in the secure element and never leaves it; the attestation binds the identity key to it once, and the signature binds it to each session. A copy of an identity key is therefore worth less, but not nothing: on a machine that does not run the attested application it opens only unattested sessions, and a genuine application that imports keys, for example to move an identity to a new smartphone [2], attests it like any key it generated. Attestation proves that a genuine application holds the key, not that the key’s owner does. The two platforms do not prove the same thing, and the difference bounds the claim above. Both attestations are verified by the peer against a public root—Apple’s App Attest root, Google’s hardware attestation root—so no third party is contacted at the moment two agents establish a session, which is the property the protocol needs. Google’s Play Integrity is not used: its verdict is opened only by Google’s servers, so proving oneself to a peer would require reaching a global service on every session. What Android’s certificate chain establishes is that the attestation key lives in the device’s secure hardware and that the application holding it carries the developer’s signing certificate the attestation names; on a 8
device whose operating system is modified that certificate can be forged. The claim above therefore holds against a repackaged application on both platforms, and against a modified operating system on neither. Only the device’s hardware root can prove which application the device runs, and the platform’s vendor holds that root; neither code the application ships nor a server can prove it, since each sees only what the device sends. What the platforms do require of a third party lies outside the session, and the two differ. On Android the attestation certificates are provisioned to the device by Google and, from Android 16, only so, and revocation is published by Google as a status list a verifier fetches; so a device that has never reached Google cannot attest, and a verifier that never reaches it does not learn that a key has been revoked. On iOS the corresponding contact is on the producing device alone: attesting a key reaches an Apple server once, and Apple documents no revocation, so an iOS verifier contacts nobody at all. Neither platform is contacted during a session, and an agent that cannot attest is reported unattested (below) rather than refused, so a session between two agents remains a matter for the two of them. What the attestation names is the application published under its developer’s key, not the code. To report the same on both platforms, we take the attested application to be its developer, its name and its version number. Android’s certificate names the digest of the developer’s signing certificate, the package and its version; App Attest names the application identifier the key was attested under, the developer’s team identifier and the bundle identifier, and no version. On both platforms the version number is the one the application signs in the per-session digest, which only the genuine application can sign. Neither platform measures the running code, so that one build runs under one name and version number is the developer’s undertaking, which the operating system above assumes. A headless server profile (Section 2) has no attestation to offer: it sends the statement that its platform provides no attestation, and its peers report it unattested rather than refusing it; whether to transact with an unattested peer is a GLP-level decision. An attestation offered and found invalid tears the session down.
2.7
Geofencing
The location service is the platform’s geofencing, and the layer uses it for one purpose: to tell GLP when the smartphone enters or leaves a place GLP has declared. A place is a geofence, a circle of a given radius in metres about the location the device is at when the place is declared, registered with the platform under a name GLP chooses. With declarePlace(place, radius) the layer registers the geofence, and a second declaration of the same place replaces the first; with removePlace(place) it unregisters it (Section 5). The platform then watches the device’s position and reports each crossing of a registered geofence to the layer, which passes it on through onPlaceEvent(place, event) as entered or exited. The event carries the name of the place and the direction of the crossing, and no coordinates: nothing of the device’s location reaches the runtime or a peer through the layer. What a place means, for example that the person declared the place they are in trusted, so PAN contact there is open (Section 2.8), is decided by GLP, and the layer makes no decision about it. The platform does not always report crossings: it stops when the application moves to the background or when the person withdraws the location permission, and resumes when either 9
is reversed. Without knowing this, GLP could not tell a device that stayed inside a place from one whose exit went unreported, so the layer reports unobservable when the platform stops reporting and observable when it resumes. Observability belongs to the platform’s reporting and not to one geofence, so both events are delivered for every standing place at once, and a place declared while reporting is stopped is told so as soon as it is declared. A crossing that occurs while the device is unobservable is lost, and on resumption the layer replays nothing. On iOS, the platform reports crossings to an application in the background only under the permission to use location at all times, which we do not request, so the platform supplies the position only in the foreground: the layer decides the crossing itself, and reports the device unobservable whenever the application is not in the foreground. A headless server has no location service: its layer is built without one, and refuses declarePlace and removePlace.
2.8
Cold Calls
A cold call is a first-contact connection attempt between two agents that do not already share an active communication channel. A cold call is realized in one of three ways: by an encounter on the PAN (Section 3.3.2), by a LAN encounter (Section 3.2), or over the WAN between agents whose programs have declared each other’s keys (Section 4.4). What a cold call means is a GLP-level interpretation and is not part of the networking layer. Trust levels. Whether the networking layer answers an unsolicited proximity encounter from an unknown key (Section 3.1) is governed by a trust level set by GLP and enforced by the layer: • Open: The agent completes the ANNOUNCE exchange and the Noise XX handshake with any unknown contact (Section 2.6), thereby revealing its full public key and allowing first contact. • Closed: The agent sends no ANNOUNCE, answers no XX handshake, and answers an IK handshake only from a known key: unmatched proximity PAN and LAN encounters are ignored, and the agent reveals its key only to peers that already hold it. A level is held per proximity underlay, and is set with setTrustLevel(level) on that underlay’s sub-object, pan or lan (Section 2.2); both levels default to Closed. WAN contact is governed by no level (below). GLP sets the two levels independently, for example opening PAN contact while the smartphone is inside a place the person declared trusted (Section 2.7), and LAN contact while it is attached to a local network the person declared trusted (Section 4.3). Unsolicited inbound WAN contact from outside the agent’s own local network is not governed by trust levels: it is refused unless the peer is in the known-peer set (Section 2.4): an IK handshake from an unknown key is not answered (Section 2.6). A rendezvous server, marked so when its layer is instantiated (Section 2.2), answers unsolicited WAN contact from any key, since it cannot know its clients in advance (Section 6). LAN contact arrives on the same path, LAN and WAN connections being both IP connections, so the layer tells the two apart by the source address alone: an unsolicited inbound contact whose source address lies within one of the agent’s own local address prefixes is LAN contact, governed by the LAN level. 10
2.9
Point-to-Point Communication
The networking layer provides reliable delivery of arbitrary byte payloads between peers, identified by public key. Function
Signature
Description
Send payload
send(pubKey, payload)
Receive payload
onMessageReceived(sender, payload, messageId, underlay)
Send a byte payload to the peer identified by pubKey. Register a callback for incoming payloads. Receives the sender’s public key, the byte payload, a layer-assigned messageId, and the Underlay over which the message was delivered.
2.10
Message Protocol
One message protocol carries send payloads, and the attestation exchange of Section 2.6, on every underlay. Every packet bears the five-byte frame: a packet-type byte and a 4-byte big-endian payload length. A message is assigned its messageId and split into fragments sized to the carrier, each fragment carrying the messageId, its index, and the fragment count, encrypted under the session of Section 2.6. The receiver acknowledges fragments; the sender retransmits unacknowledged fragments with backoff, under a bounded send window per peer. When all fragments have arrived the message is reassembled and delivered whole— atomic delivery (Section 2.11). A re-send after a lost acknowledgment, possibly over another underlay, may deliver a message multiple times, and madGLP dedupes repeated deliveries [7]. When a message’s attempts are exhausted the session is torn down and the message queues for the next path (Section 2.5). No order is kept or promised across messages (Section 2.11); a lost fragment delays only its own message. Over LAN and WAN, a fragment rides a UDP datagram, sized within the path MTU, and retransmission is the reliability mechanism; over BLE, a fragment rides a GATT write without response, sized to the negotiated MTU, and retransmission is the reliability mechanism on both underlays.
2.11
Networking Assumptions
The networking layer must satisfy the following assumptions for madGLP correctness: Fair message delivery: Every message passed to send() is eventually delivered to the recipient at least once, assuming the recipient eventually becomes reachable. Messages to temporarily-unreachable peers are queued and delivered when connectivity is restored; the queue survives the termination of the application. 11
Unordered delivery: No delivery-order assumption is made: messages between two agents may be delivered in any order, and the madGLP runtime tolerates arbitrary delivery order. The networking layer is free to route concurrent messages to the same peer over different underlays. Atomic message delivery: Each message is delivered as a complete unit. If a message is fragmented across multiple packets, the networking layer reassembles them before delivery. Integrity: Messages are not corrupted in transit. The networking layer provides encryption and integrity checks. Authentication: The sender’s identity (public key) is authenticated. A received message from public key pk was indeed sent by the agent holding the corresponding private key.
3
Proximity Underlays: PAN and LAN
An agent discovers and contacts agents in its vicinity—on the same local network, or within radio range—without any infrastructure and without becoming trackable. This section specifies the shared design, then its two realizations: PAN and LAN.
3.1
Concepts and Principles
Recognition before contact. Each proximity underlay gives an agent a public, passively observable presence—a BLE advertisement, an mDNS service instance. That presence must let a peer who already knows the agent recognize it before any contact, yet give everyone else nothing stable to track. Both realizations therefore advertise the same construction: a rotating 8-byte recognition token, the first 8 bytes of H(L | pk | T ), where H is SHA-256, pk the advertiser’s public key, T the current 15-minute time slot, and L a per-underlay label. Anyone who knows pk computes the token for the current and adjacent slots and matches it against observed presences; adjacent-slot matching absorbs clock skew. To anyone else the token is an identifier that changes every slot. The advertisement carries the token and no public key. Contact and identity. A recognized presence is contacted on its underlay with the IK handshake of Section 2.6, whose expected key is the recognized peer’s. Under Open trust any other Grassroots presence is contacted too: the peers exchange ANNOUNCE (Section 2.5), binding the contact to a claimed public key, and run the XX handshake, which checks the claim. Where either endpoint may initiate the connection, the deterministic initiator is the endpoint with the lexicographically smaller public key, as everywhere (Section 4.1). Addresses and contacts observed by proximity discovery feed the layer’s connection attempts directly; GLP distributes addresses (Section 4.2). Cold-call trust levels. The trust levels of Section 2.8 govern proximity encounters as unsolicited contact, one level per proximity underlay: under Open the agent contacts or answers unmatched presences on that underlay, revealing its full public key in ANNOUNCE; under Closed the advertised token serves only to recognize already-known peers (Section 2.4) 12
and unmatched encounters are ignored. The PAN and LAN underlays carry independent trust levels, so an agent may answer strangers over one underlay while ignoring them over the other. Discovery. Proximity discovery is exposed to the runtime by the same functions on both proximity sub-objects, pan and lan (Section 2.2), each acting on its own underlay: Function
Signature
Description
Scan for peers
getPeers() → List<Peer>
Peer discovered
onPeerDiscovered(pk)
Set trust level
setTrustLevel(level)
Look for nearby Grassroots agents on this underlay: on pan a BLE scan for the Grassroots prefix, on lan an mDNS browse for the service type. Callback for each nearby Grassroots agent found on this underlay. Set the cold-call trust level of this underlay to Open or Closed (Section 2.8).
3.2
LAN
Two agents on the same local network can find each other and communicate directly—no BLE radio and no rendezvous server. Discovery uses DNS-SD over multicast DNS: each agent advertises an instance of the service type _grassroots._udp on the local domain and browses for instances of it. The advertised instance name is the recognition token of Section 3.1, hexencoded, with label "glp lan suffix"; the instance’s SRV record carries the agent’s LAN address and bound UDP port. A matched instance yields an address, and contact proceeds as over the WAN: a UDP path with the deterministic initiator (Section 4.1) and the session of Section 2.6, preceded by ANNOUNCE only for an unrecognized instance under Open trust; reachability reports the underlay LAN, and communication is that of Section 4.5.
3.3
PAN
Bluetooth Low Energy enables offline, infrastructure-free operation between devices in direct radio range of each other, and is the bootstrap underlay where agents first meet face-to-face. 3.3.1
Advertising and Recognition
Each agent advertises a BLE GATT service whose 128-bit (16-byte) UUID has two parts: a fixed 8-byte Grassroots prefix common to all Grassroots agents, enabling general discovery of any nearby Grassroots agent, and the rotating 8-byte recognition token of Section 3.1 with 13
label "glp ble suffix". When the slot time is over, the API rotates the new token, and advertising is restarted with the new discovery token. When an app is not in the foreground, iOS advertises the service only to a scanner for that exact UUID, so a friend who computes the token still finds the agent; discovery by strangers needs the app in the foreground. 3.3.2
Connection and Identity
Upon BLE connection to a recognized presence, the peers establish the IK session of Section 2.6 over the GATT connection directly; to an unrecognized presence under Open trust, they first exchange ANNOUNCE, which carries the agent’s full 32-byte public key, and then run the XX handshake. No Bluetooth bonding or pairing confirmation is required. Where either endpoint may initiate—both run central and peripheral—the deterministic initiator is the endpoint with the lexicographically smaller public key, as over the WAN (Section 4.1). 3.3.3
Communication
send(pubKey, payload) transmits the byte payload over the established GATT connection via the message protocol of Section 2.10, GATT writes as the carrier; fragmentation and reassembly satisfy atomic delivery (Section 2.11). onMessageReceived delivers reassembled payloads to the runtime.
4
WAN Underlay
This section specifies how the networking API (Section 2) is realized over the WAN, the Internet at large. Communication uses datagrams carrying the message protocol of Section 2.10 over UDP sockets. NAT traversal is coordinated by a rendezvous agent, on a server or on a well-connected friend’s smartphone (Section 6). The rendezvous protocol is GLP code (Section 6).
4.1
WAN Connection
Once a UDP path is established (directly or by hole-punching), the peers establish the session of Section 2.6 over it. When both endpoints might initiate—mutual LAN discovery, simultaneous reconnection—the deterministic initiator is the endpoint with the lexicographically smaller public key. UDP has no connections, so ANNOUNCE is the liveness check (Section 2.5): a peer silent for two intervals is disconnected.
4.2
Connectivity and Address
Mobile agents typically run behind NAT without static IP addresses, and an agent cannot directly observe its own public address. When its address changes (e.g., due to roaming), all its IP-based paths break. The networking layer reports address changes to the runtime, and exposes the agent’s current public address once known (an agent learns it from a rendezvous server, which observes the source address of the agent’s packets; see Section 6). GLP code 14
uses these to share the agent’s address with its peers and to trigger reconnection. The address is GLP data and travels over any underlay: GLP can hand a fresh address to a BLE-adjacent friend, which can relay it to remote friends and coordinate their reconnection via the system predicates (Section 5), serving as an edge rendezvous point without a globallyroutable address. Address-change recovery. When an agent’s own address changes, its established UDP paths break and their sessions are torn down by liveness (Section 2.5), but its ability to initiate outbound connections is unimpaired: the recorded addresses of its known peers did not change. The heartbeat sweep therefore re-initiates connections to the known peers and the declared rendezvous servers; the rendezvous connection always succeeds (globally-routable target), restoring the RV channel and, through it, the agent’s fresh public address. Recovery is thus asymmetric by design: the endpoint that moved reconnects; a stationary peer is reconnected to, and needs no action beyond accepting. Distributing the fresh address to peers that cannot be reached directly (both behind NAT) is GLP’s: GLP learns the address from onConnectivityStatusChanged/getPublicAddress, sends it to friends over whatever channels remain live—rendezvous-mediated signaling, a BLE-adjacent friend, surviving sessions—and it enters each peer’s layer via putPeerAddress, whereupon that peer’s own sweep connects to it, punching if coordinated (Section 5). Rendezvous servers. The rendezvous servers the layer connects to are those GLP has declared, each by its public key and its address, and GLP adds and removes them while the layer runs. GLP knows which of its peers are rendezvous servers by the keys it declared. The functions below are called on the layer’s wan sub-object (Section 2.2). Function
Signature
Description
Connectivity changed
onConnectivityStatusChanged (oldAddress?, newAddress?)
Public address
getPublicAddress() → Address?
Callback when the networking layer detects a change in the agent’s public IP address. Receives (oldAddress?, newAddress?) as ip:port strings, either of which may be null when no public address is available (e.g. startup: old=null; loss: new=null; update: both present and different). Fired on startup and on every address change. The agent’s current public ip:port, or null if not yet known.
15
Function
Signature
Description
Feed peer address
putPeerAddress(pk, address)
Observed address
observedPeerAddress(pk) → Address?
Punch
punchUdp(address)
Declare server
putRendezvousServer(pk, address)
Remove server
removeRendezvousServer(pk)
Inform the networking layer of a peer’s current public ip:port, learned at the GLP level; the layer records it with the known peer (Section 2.4) for send and reconnection. One WAN address is recorded per peer; the latest, whether GLP supplied it or the layer observed it, replaces the earlier. The public ip:port at which the layer observes the connected peer, or null if none is observed; behind peer_address/2, which reports address(S) or none [5]. Send toward the address a short burst of UDP datagrams to open the NAT mapping; behind punch_udp/1. Add the rendezvous server with public key pk at address to the servers the layer connects to; a further declaration of the same pk replaces its address; behind rendezvous_declare/2. Remove the server from those the layer connects to, and end the layer’s session with it; a key not declared does nothing; behind rendezvous_remove/1.
4.3
Local Network Identity
The wan sub-object (Section 2.2) reports an opaque fingerprint of the local network the agent is attached to, and notifies the runtime when it changes. The fingerprint is derived from whichever of these the platform provides without additional permission: the local address 16
prefix, the default gateway address, and the wireless network name. This is not onConnectivityStatusChanged (Section 4.2). The two are independent in both directions: the attached local network can change while the public address does not, and the public address can change while the attached local network does not. The fingerprint is best-effort: two distinct local networks may yield the same one, and a platform may make none of its constituents readable. A match is evidence that the agent is attached to the same local network as before, not proof of it; an agent compares a fingerprint only against its own earlier values. Function
Signature
Description
Network identity
networkIdentity() → NetworkId?
Network changed
onNetworkChanged(oldId?, newId?)
An opaque fingerprint of the local network the agent is currently attached to, or null when it is attached to none or no constituent is readable. Callback when the fingerprint changes. Receives (oldId?, newId?), either of which may be null.
4.4
WAN Cold-Call
Over the WAN, a cold call is a connection between two agents whose programs have each declared the other’s key to their layer (Section 2.4). How the two learn each other’s keys—by a signed one-time invitation link redeemed at a rendezvous server—belongs to the operating system above the runtime (the Grassroots Super-App, in preparation) and is out of scope here. To the layer each is a known peer of the other: the two connect over UDP, punching where needed (Section 4.2), and establish the session of Section 2.6; only then does the caller send the madGLP cold-call payload to the callee’s index-0 serializer.
4.5
WAN Communication
send(pubKey, payload) transmits the byte payload to the peer as session-encrypted fragments in UDP datagrams, per the message protocol of Section 2.10. onMessageReceived delivers complete, reassembled messages to the runtime.
5
The Seam: Layer Functions behind the System Predicates
The GLP runtime realizes ten system predicates over the networking-layer functions this section specifies. These predicates give GLP agents the abilities of address observation, UDP 17
hole punching, declaration of places as geofences, trust level setting, known-peer set management including agent addresses, and declaration of rendezvous servers. The predicates— peer_address/2, punch_udp/1, place_declare/3, place_remove/1, trust_declare/2 with the values pan and lan, peer_declare/1, peer_remove/1, address_declare/2, rendezvous_declare/2 and rendezvous_remove/1—are defined in the language paper’s catalogue [5], and their clauses in the implementation paper [7]; only the functions behind them are specified here. If an underlay changes—for example, UDP datagrams replaced by QUIC, or hole-punching by another traversal mechanism—only the layer’s implementation of these functions changes; the GLP code that uses the predicates is unaffected. The layer functions. The functions behind the predicates are specified with their underlay or subject: observedPeerAddress and punchUdp with the WAN, on the wan sub-object (Section 4.2), setTrustLevel with the trust levels, on the pan and lan sub-objects (Section 2.8), putKnownPeer and removeKnownPeer with the known peers, on the layer (Section 2.4), and putPeerAddress, putRendezvousServer and removeRendezvousServer with the WAN, on the wan sub-object (Section 4.2). The place functions are specified here: Function
Signature
Description
Declare place
declarePlace(place, radius)
Remove place
removePlace(place)
Place events
onPlaceEvent(place, event)
Register with the platform a geofence of radius metres about the device’s current location, named place; a further declaration of the same place replaces the first; behind place_declare/3. Unregister the geofence of place; a place not declared does nothing; behind place_remove/1. Callback with the place and an event, one of entered and exited, as the platform reports the device crossing the geofence, unobservable when the platform stops reporting—the application in the background, a permission withdrawn—and observable when it resumes; feeds the stream of place_declare/3.
Signatures. The layer provides sign(bytes) and verify(pk, bytes, signature), Ed25519 signing and verification under the agent’s key and against a public key. The 18
runtime realizes the language’s sign/3 and signature/2 over them [5], adding the source identity of the signing program, so the layer never learns about programs, as it never learns about “friend”.
6
Rendezvous Server
A rendezvous server is a separate process—a host that provides a static, globally-routable IP address and runs a lightweight GLP rendezvous agent. Its purpose is to give NATbound agents, which cannot otherwise reach one another, a fixed point they can always reach outbound, from which they discover their own public addresses and coordinate UDP hole-punching. This section specifies the rendezvous server only by its networking requirements—what a machine must provide to host one. The rendezvous agent, and the reconnection and first-contact protocols that use it, belong to the operating system above the runtime (the Grassroots Super-App, in preparation) and are out of scope here. Networking requirements. • Static, globally-routable address. The host has a fixed public ip:port that any agent can reach outbound, traversing NAT in the outbound direction without assistance. • Address observation. When an agent connects, the rendezvous server observes the source ip:port of the agent’s packets and exposes it via observedPeerAddress (Section 4.2). This is the address-discovery mechanism that lets a NAT-bound agent learn its own public address; no external STUN infrastructure is required. • Hole-punch coordination. The rendezvous server relays observed addresses between two agents, and each sends punch traffic toward the other with punchUdp (Section 4.2) so a direct UDP path can form. Once the path is open the peers run the Noise handshake and the message protocol directly (Sections 4.1 and 2.6); the rendezvous server sees signaling metadata and observed addresses, never the encrypted payload. • No relay. The rendezvous server is not a TURN relay; it never forwards madGLP payloads. When direct connectivity cannot be established (e.g., symmetric NAT), relaying is an application-level concern handled in GLP by a connected common peer, not by the networking layer. • Any key. A rendezvous server cannot know its clients in advance, so its layer, marked so when it is instantiated (Section 2.2), answers unsolicited WAN contact from any key (Section 2.8); it belongs to no social graph. • Unattested. Having no platform attestation to offer (Section 2.6), a rendezvous server is reported unattested, and this costs nothing: a false address fails to connect, and it cannot make an unattested peer appear attested, attestation being verified between the two agents themselves. GLP knows a rendezvous server by the key it declared (Section 4.2). What an unattested channel may then be used for is a GLP-level decision. 19
Any number of servers. Any individual or organization can run a rendezvous server; the servers do not communicate with one another. An agent may use several, which GLP declares (Section 4.2); the system operates with none when agents are on the same PAN or LAN (Section 3). In the terminology of the ICE framework [3], a rendezvous server combines the roles of STUN server (address discovery via observedPeerAddress) and applicationspecific signaling server (candidate exchange and hole-punch coordination, performed by the rendezvous agent); it does not serve as a TURN relay.
7
Complete API Summary
The following table summarizes the complete networking API. The first five groups are underlay-agnostic and called on the layer; the Proximity group is called on the pan and lan sub-objects, and the WAN group on the wan sub-object (Section 2.2); the system predicates are the language’s [5], and the layer functions behind them are in Section 5. Category
Function / Predicate
Direction
madGLP Role
getIdentity()
Runtime ← Network
Retrieve agent public key
Add
putKnownPeer(pk)
Remove
removeKnownPeer(pk)
Runtime → Network Runtime → Network
Supply a key the layer recognizes Withdraw recognition and recorded address Peer reachable on an underlay; pk, underlay, and the attested application, or null for an unattested identity Peer unreachable on an underlay; queue messages Check before send
Identity Query Known peers
Connection & Reachability Connect
onPeerConnected(pk, underlay, application?)
Runtime ← Network
Disconnect
onPeerDisconnected(pk, underlay)
Runtime ← Network
Reachable
isPeerReachable(pk)
Transports
peerUnderlays(pk)
Runtime → Network Runtime → Network
20
Underlays now reaching pk (proximity)
Category
Function / Predicate
Direction
madGLP Role
Implements madGLP Send transaction Feeds madGLP Receive transaction
Communication Send
send(pubKey, payload)
Runtime → Network
Receive
onMessageReceived(sender, payload, messageId, underlay)
Runtime ← Network
Cryptography Sign
sign(bytes)
Runtime → Network
Verify
verify(pk, bytes, signature)
Runtime → Network
Ed25519 signature under the agent’s key Check an Ed25519 signature by a public key
Proximity underlays (PAN and LAN) Scan
{pan,lan}.getPeers()
Notify
{pan,lan}. onPeerDiscovered(pk)
Runtime → Network Runtime ← Network
Policy
{pan,lan}. setTrustLevel(level)
Runtime → Network
Find nearby agents on that underlay Nearby Grassroots agent on that underlay Set that underlay’s cold-call trust level
wan. onConnectivityStatusChanged (old?, new?) wan.getPublicAddress()
Runtime ← Network
Public IP change; (old?, new?)
Runtime ← Network
Local network
wan.networkIdentity()
Runtime ← Network
Network change
wan.onNetworkChanged(oldId?, newId?)
Runtime ← Network
Feed address
wan.putPeerAddress(pk, address)
Runtime → Network
Current public ip:port, share with peers Fingerprint of the attached local network Local network changed; (old?, new?) Hand the layer a GLP-learned peer address
WAN Connectivity Own address
21
Category
Function / Predicate
Direction
madGLP Role
Observed address
wan.observedPeerAddress(pk)
Runtime → Network
Punch
wan.punchUdp(address)
Declare server
wan.putRendezvousServer(pk, address)
Runtime → Network Runtime → Network
Remove server
wan. removeRendezvousServer(pk)
Runtime → Network
Public address at which a connected peer is observed UDP hole-punch toward an address A rendezvous server the layer connects to Withdraw it
Declare place
declarePlace(place, radius)
Runtime → Network
Remove place Place events
removePlace(place)
Runtime → Network Runtime ← Network
Places
onPlaceEvent(place, event)
Register a geofence about the device’s location Unregister it Crossings, and when they cannot be observed
Acknowledgments We thank Idit Keidar and Ohad Eitan for the productive discussions, and the helpful comments and reviews they provided to make this paper.
References [1] Apple. Establishing your app’s integrity. Apple Developer Documentation, https://developer.apple.com/documentation/devicecheck/ establishing-your-app-s-integrity, 2026. Accessed 22 October 2026. [2] Ohad Eitan, Idit Keidar, and Ehud Shapiro. Securing people and their machines against major faults (full version). Proceedings of The 28th International Symposium on Stabilization, Safety, and Security of Distributed Systems (SSS 2026), to appear, 2026. arXiv:2607.02304. [3] Ari Keranen, Christer Holmberg, and Jonathan Rosenberg. Interactive connectivity establishment (ICE): A protocol for network address translator (NAT) traversal. RFC 8445, 2018. [4] Ehud Shapiro. Grassroots distributed systems: Concept, examples, implementation and applications (brief announcement). In 37th International Symposium on Distributed Com22
puting (DISC 2023). (Extended version: arXiv:2301.04391), pages 47:1–47:7, Italy, 2023. LIPICS. [5] Ehud Shapiro. Glp: A grassroots, multiagent, concurrent, logic programming language for ai (full version). arXiv preprint arXiv:2510.15747, Summary in Proc. of ICLP’26, EPTCS 450, pp. 119–133, 2025. [6] Ehud Shapiro. Grassroots platforms with atomic transactions: Social graphs, cryptocurrencies, and democratic federations. In Proceedings of the 27th International Conference on Distributed Computing and Networking, pages 71–81, 2026. arXiv preprint arXiv:2502.11299. [7] Ehud Shapiro. Implementing grassroots logic programs with multiagent transition systems and ai (full version). arXiv:2602.06934. Summary to appear in Proc. of LOPSTR+PPDP’26, 2026.
23
Related documents
- GLP Networking API Specification#1447950
- Real-world Evaluation of GLP-1 Receptor Agonists (GLP-1RA) on Efficacy and Persistence, Adherence and Therapeutic Inertia Among Type 2 Diabetes Adults With Obesity#366569
- Implementation and Evaluation of BitNet Inference on a CGLA by Signed-Int4 Instructions#1063664
- First-in-Human Single Ascending and Multiple Dose of GLPG0634#853474
- Chaining Skills to Hijack LLM Agents#1160936
Retrieved via Conceptio: every document is proof-bundled with source, license, and retrieval metadata.
Read the Docs → · View CLI source on GitHub · Conceptio Open Knowledge Archive