Skip to content

Go Packages

Module: github.com/a2al/a2al

LevelPackage / binaryUse when
Node runtimegithub.com/a2al/a2al/hostDHT + QUIC on one or two UDP ports, mutual TLS, publish/resolve/connect. Recommended for most applications.
DHT onlygithub.com/a2al/a2al/dhtYou provide transport and only need routing, bootstrap, and iterative FIND_VALUE / STORE.
Daemona2ald (cmd/a2ald)Local REST + Web UI + MCP + DHT debug JSON. Non-Go integrations only.

Lower-level packages (transport, routing, UDP mux internals) are building blocks; most applications should depend on host, dht, or the daemon only.


A Host runs a DHT Node, optional UDP demux for DHT+QUIC on a single port, a QUIC listener, and NAT/reflection hints via natsense.Sense.

FieldMeaning
KeyStoreRequired. Must list exactly one Address.
ListenAddrDHT UDP bind, e.g. ":4121" (default). Currently udp4.
QUICListenAddrIf non-empty, QUIC binds separately from DHT. If empty, QUIC shares the DHT UDP socket (mux).
PrivateKeyEd25519 key for QUIC/TLS. If nil, EncryptedKeyStore.Ed25519PrivateKey is used; otherwise set explicitly.
MinObservedPeersMinimum peers reporting the same reflected address before Sense trusts it (default 3).
FallbackHostOptional advertised host when bind address and reflection data are ambiguous (e.g. 0.0.0.0).
DisableUPnPSkip IGD UDP port mapping for the QUIC listen port.
ICESignalURLWebSocket base URL for ICE trickle signaling. ConnectFromRecord uses ICE as fallback when direct QUIC fails.
ICESTUNURLsstun: URIs for ICE gathering. Empty means default public STUN when no TURN is configured.
ICETURNURLsturn: URIs used locally for ICE relay. Not published to the DHT.
ICEPublishTurnsCredential-free turn: hints stored in EndpointPayload.Turns for remote peers.
Logger*slog.Logger. If nil, slog.Default() is used.
  1. host.New(cfg) — starts the DHT receive loop and QUIC listener.
  2. h.Node().BootstrapAddrs(ctx, []net.Addr{...}) — join the network.
  3. Optionally h.ObserveFromPeers(ctx, seeds) — seed observed-address sampling.
  4. h.PublishEndpoint, h.Resolve, h.Connect / h.ConnectFromRecord, h.Accept as needed.
  5. h.Close() — removes any UPnP mapping and shuts down.
MethodRole
PublishEndpoint(ctx, seq, ttl)Builds multi-candidate quic:// payload (reflection, public bind, fallback, optional UPnP), signs with the node identity, stores on the DHT.
PublishEndpointForAgent(ctx, agentAddr, seq, ttl)Same for a registered delegated agent.
Resolve(ctx, target Address)Iterative lookup; returns *protocol.EndpointRecord.
Connect(ctx, expectRemote Address, udpAddr)QUIC dial to one UDP address with mutual TLS + agent-route frame.
ConnectFromRecord(ctx, expectRemote Address, er)Happy Eyeballs over all endpoints in the record; ICE fallback if all fail and er.Signal is set.
ConnectFromRecordFor(ctx, localAgent, expectRemote, er)Same, using TLS credentials for localAgent (must be registered on this host).
Accept(ctx)Blocks for inbound QUIC; returns *AgentConn.
FirstQUICAddr(er)First quic:// or legacy udp:// entry as *net.UDPAddr.
QUICDialTargets(er)Ordered, deduplicated []*net.UDPAddr from an EndpointRecord.
BuildEndpointPayload(ctx)Same candidate list as publish; does not sign or store.
SymmetricNATReachabilityHint()Non-empty note when inferred NAT is symmetric.
RegisterAgent(addr, priv)Add an extra agent identity on the same QUIC listener.
RegisterDelegatedAgent(addr, opPriv, delegationCBOR)Register an agent published under a master-derived AID.
UnregisterAgent(addr) / RegisteredAgents()Remove or list extra agents.
Address, DHTLocalAddr, QUICLocalAddrIntrospection.
Node(), Sense()Access underlying DHT node or NAT/reflection state.
ObserveFromPeersTriggers ping-style contact to collect observed_addr for Sense.
SendMailbox / PollMailboxDHT mailbox for the default host identity.
SendMailboxForAgent / PollMailboxForAgentSame for a registered agent.
RegisterTopic / RegisterTopicsTopic rendezvous: publish RecType 0x10 at SHA-256("topic:"+string).
RegisterTopicForAgent / RegisterTopicsForAgentSame for a registered agent.
SearchTopic / SearchTopicsAggregateRecords on topic key. SearchTopics returns AIDs present in all listed topics.
StartDebugHTTP(addr) / DebugHTTPHandler()Read-only JSON debug endpoints.
Close()Shuts down QUIC, mux, and DHT.

Embeds quic.Connection. Fields:

  • Local — agent Address selected for this connection (agent-route frame, else SNI, else default)
  • Remote — peer Address from the mutual TLS peer certificate (inbound)

After the TLS handshake, the client must open a stream and write 25 bytes: prefix a2r1 (ASCII) followed by the 21-byte binary Address of the intended server agent. The server uses this for routing when multiple agents share one listener.


Use when you implement your own stack and only need Kademlia-style RPCs and storage.

FieldMeaning
TransportRequired. DHT UDP (or mux) transport.
KeystoreRequired. Exactly one identity.
OnObservedAddrOptional callback when responses carry observed_addr.
RecordAuthOptional. After signature verification passes, called before accepting a STORE; enforce whether Pubkey may publish for the record Address. If nil, no authority check.
  1. dht.NewNode(dht.Config{...})
  2. Start()
  3. BootstrapAddrs / Bootstrap / StartWithBootstrap
  4. PublishEndpointRecord or NewQuery(n).Resolve / FindNode
  5. Close()
MethodRole
BootstrapAddrs(ctx, []net.Addr)Bootstrap: only ip:port required; identity learned from PONG.
PingIdentity(ctx, addr)Returns PeerIdentity{Address, NodeID}.
PublishEndpointRecord(ctx, rec)STORE signed record to closest peers.
PublishMailboxRecord(ctx, storeKey, rec)STORE mailbox record at recipient NodeID.
PublishTopicRecord(ctx, storeKey, rec)STORE topic record at TopicNodeID.
NewQuery(n).Resolve(ctx, NodeID)Iterative endpoint fetch.
NewQuery(n).FindNode(ctx, NodeID)Iterative FIND_NODE.

PackageItems
github.com/a2al/a2alAddress, NodeID, ParseAddress, NodeIDFromAddress
github.com/a2al/a2al/cryptoKeyStore, EncryptedKeyStore (optional Ed25519PrivateKey for QUIC), AddressFromPublicKey, GenerateEd25519, sign/verify helpers
github.com/a2al/a2al/identityScopeNetworkOps, SignDelegation, EncodeDelegationProof, ParseDelegationProof, VerifyDelegation, (DelegationProof).AgentAID()

ItemRole
SignedRecordOn-wire CBOR container; optional Delegation bytes for operational-key publishes.
EndpointPayloadEndpoints []string (use quic://host:port), NatType uint8, Signal string, Turns []string
EndpointRecordDecoded view after verify: Address, Endpoints, NatType, Signal, Turns, Seq, Timestamp, TTL
SignEndpointRecord(priv, addr, payload, seq, timestamp, ttl)Build SignedRecord when the signing key is the AID’s master key.
SignEndpointRecordDelegated(opPriv, delegationCBOR, addr, payload, seq, timestamp, ttl)Build SignedRecord when an operational key publishes for a master-derived addr.
ParseEndpointRecord(sr)Verify and decode to EndpointRecord.
VerifySignedRecord(sr, now)Cryptographic integrity + expiry check; does not enforce pubkey↔address authority.
NAT constantsNATUnknown, NATFullCone, NATRestricted, NATPortRestricted, NATSymmetric
ItemRole
RecTypeMailbox (0x80)Stored at NodeID(recipient); outer SignedRecord.Address = sender AID.
EncodeMailboxPayload, OpenMailboxRecordX25519 + HKDF + AES-256-GCM wire helpers.
MailboxMessageDecrypted view: Sender, MsgType, Body.
ItemRole
TopicNodeID(topic)SHA-256("topic:" || UTF-8 topic) as DHT key.
RecTypeTopic (0x10)TopicPayload CBOR in SignedRecord.payload (≤512 B); Address = registrant AID.
TopicEntryDecoded listing: AID, TopicPayload fields, Seq / Timestamp / TTL.
DiscoverFilter, FilterTopicEntriesOptional protocols / tags AND-style client filter.

Function / methodRole
Default()Spec default field values.
(*Config) Validate()Required fields and enums.
LoadFile(path) / Save(path, c)TOML read/write.
ApplyEnv(c)Overlay A2AL_* environment variables.
(*Config) KeyDirOrDefault(dataDir)Resolve key directory.

dht.DebugHTTPAddr (127.0.0.1:2634) is the suggested default for library-only Host or Node.

PathProvided by
/debug/identity, /debug/routing, /debug/store, /debug/statsdht.Node (and Host via combined handler)
/debug/hostHost only — QUIC bind, registered agents, NAT/reflection summary

All responses are read-only JSON.


When using Host, Sense() exposes consensus over reflected UDP endpoints:

  • MinAgreeing / SetMinAgreeing — lower to 1 for small test networks
  • TrustedUDP / TrustedWire / InferNATType — read trusted reflection and coarse NAT classification

  • TURN relay — config fields exist; pion/turn server-side relay not yet integrated for symmetric-NAT fallback
  • IPv6 dual-stack Host listener — wire format supports IPv6; New() currently uses udp4 only

Terminal window
go test -vet=off -count=1 ./...

Example programs under examples/ use separate go.mod files and import the parent module via replace.