Documentation
¶
Overview ¶
Package host is the Phase 2 SDK: DHT + QUIC, nat-sense, multi-candidate endpoints (2b), optional UPnP, Happy Eyeballs dial, Publish/Resolve/Connect/Accept.
A Host manages the node-level resources (DHT, QUIC transport). One or more agents (each identified by a unique Address) share the same QUIC listener via TLS SNI routing (spec Phase 2a "mux" module).
When Config.QUICListenAddr is empty, DHT and QUIC share one UDP port via UDPMux (spec target). When QUICListenAddr is set, QUIC uses a separate socket — recommended until mux is hardened on all platforms.
Index ¶
- Constants
- func FirstQUICAddr(er *protocol.EndpointRecord) (*net.UDPAddr, error)
- func PeerAddressFromConn(tlsPeerCerts []*x509.Certificate) (a2al.Address, error)
- func QUICDialTargets(er *protocol.EndpointRecord) ([]*net.UDPAddr, error)
- type AgentConn
- type Config
- type Host
- func (h *Host) Accept(ctx context.Context) (*AgentConn, error)
- func (h *Host) AcceptICEViaSignal(ctx context.Context, localAgent, expectRemote a2al.Address, signalBase string) (*AgentConn, error)
- func (h *Host) Address() a2al.Address
- func (h *Host) BuildEndpointPayload(ctx context.Context) (protocol.EndpointPayload, error)
- func (h *Host) Close() error
- func (h *Host) Connect(ctx context.Context, expectRemote a2al.Address, udpAddr *net.UDPAddr) (quic.Connection, error)
- func (h *Host) ConnectFromRecord(ctx context.Context, expectRemote a2al.Address, er *protocol.EndpointRecord) (quic.Connection, error)
- func (h *Host) ConnectFromRecordFor(ctx context.Context, localAgent, expectRemote a2al.Address, ...) (quic.Connection, error)
- func (h *Host) DHTLocalAddr() *net.UDPAddr
- func (h *Host) DebugHTTPHandler() http.Handler
- func (h *Host) FindRecords(ctx context.Context, target a2al.Address, recType uint8) ([]protocol.SignedRecord, error)
- func (h *Host) LocalUDPAddr() *net.UDPAddr
- func (h *Host) Node() *dht.Node
- func (h *Host) ObserveFromPeers(ctx context.Context, seeds []net.Addr)
- func (h *Host) PollMailbox(ctx context.Context) ([]protocol.MailboxMessage, error)
- func (h *Host) PollMailboxForAgent(ctx context.Context, agentAddr a2al.Address) ([]protocol.MailboxMessage, error)
- func (h *Host) PublishEndpoint(ctx context.Context, seq uint64, ttl uint32) error
- func (h *Host) PublishEndpointForAgent(ctx context.Context, agentAddr a2al.Address, seq uint64, ttl uint32) error
- func (h *Host) PublishRecord(ctx context.Context, rec protocol.SignedRecord) error
- func (h *Host) QUICLocalAddr() *net.UDPAddr
- func (h *Host) RegisterAgent(addr a2al.Address, priv ed25519.PrivateKey) error
- func (h *Host) RegisterDelegatedAgent(addr a2al.Address, opPriv ed25519.PrivateKey, delegationCBOR []byte) error
- func (h *Host) RegisterTopic(ctx context.Context, topic string, entry protocol.TopicPayload, ttl uint32) error
- func (h *Host) RegisterTopicForAgent(ctx context.Context, agentAddr a2al.Address, topic string, ...) error
- func (h *Host) RegisterTopics(ctx context.Context, topics []string, base protocol.TopicPayload, ttl uint32) error
- func (h *Host) RegisterTopicsForAgent(ctx context.Context, agentAddr a2al.Address, topics []string, ...) error
- func (h *Host) RegisteredAgents() []a2al.Address
- func (h *Host) Resolve(ctx context.Context, target a2al.Address) (*protocol.EndpointRecord, error)
- func (h *Host) SearchTopic(ctx context.Context, topic string) ([]protocol.TopicEntry, error)
- func (h *Host) SearchTopics(ctx context.Context, topics []string) ([]protocol.TopicEntry, error)
- func (h *Host) SendMailbox(ctx context.Context, recipient a2al.Address, msgType uint8, body []byte) error
- func (h *Host) SendMailboxForAgent(ctx context.Context, agentAddr, recipient a2al.Address, msgType uint8, ...) error
- func (h *Host) Sense() *natsense.Sense
- func (h *Host) StartDebugHTTP(addr string) (stop func(), err error)
- func (h *Host) SymmetricNATReachabilityHint() string
- func (h *Host) UnregisterAgent(addr a2al.Address)
Constants ¶
const ALPNQuic = "a2al-quic-1"
const DefaultConnectStagger = 250 * time.Millisecond
DefaultConnectStagger is the delay between starting each QUIC dial (Happy Eyeballs).
Variables ¶
This section is empty.
Functions ¶
func FirstQUICAddr ¶
func FirstQUICAddr(er *protocol.EndpointRecord) (*net.UDPAddr, error)
FirstQUICAddr returns the first quic:// (or legacy udp://) endpoint as a UDP address.
func PeerAddressFromConn ¶
func PeerAddressFromConn(tlsPeerCerts []*x509.Certificate) (a2al.Address, error)
PeerAddressFromConn extracts the remote peer's AID from a QUIC connection's TLS state (works after mutual TLS handshake). For Phase 3 delegated agents, it returns the AID from the delegation extension rather than the op-key-derived address.
func QUICDialTargets ¶
func QUICDialTargets(er *protocol.EndpointRecord) ([]*net.UDPAddr, error)
QUICDialTargets returns ordered, deduplicated UDP addresses from quic:// / udp:// entries.
Types ¶
type AgentConn ¶
type AgentConn struct {
quic.Connection
// Local is the agent Address that was targeted (agent-route frame, or SNI fallback).
Local a2al.Address
// Remote is the connecting peer's Address (from mutual TLS client cert).
Remote a2al.Address
}
AgentConn wraps a QUIC connection with the resolved peer and local agent identities.
type Config ¶
type Config struct {
KeyStore crypto.KeyStore
// ListenAddr is the DHT UDP bind address, e.g. ":5001".
// Currently resolved as udp4; dual-stack (udp / "[::]:port") is planned.
ListenAddr string
// QUICListenAddr, if non-empty, is a separate UDP bind for QUIC.
// Same udp4 constraint as ListenAddr.
QUICListenAddr string
PrivateKey ed25519.PrivateKey
MinObservedPeers int
FallbackHost string
// DisableUPnP skips IGD port mapping for the QUIC UDP port (Phase 2b). TURN is deferred.
DisableUPnP bool
// ICESignalURL is the WebSocket base URL published in endpoint records for ICE trickle (optional).
ICESignalURL string
// ICESTUNURLs lists stun: URIs for ICE gathering; empty means default public STUN when no TURN is configured.
ICESTUNURLs []string
// ICETURNURLs lists turn: URIs (may include credentials) used locally for ICE relay; not published.
ICETURNURLs []string
// ICEPublishTurns lists credential-free turn: hints stored in EndpointPayload.Turns.
ICEPublishTurns []string
// Logger is forwarded to the DHT node for diagnostic logging (reply failures, RPC retries).
// If nil, slog.Default() is used.
Logger *slog.Logger
}
Config wires DHT + QUIC.
IPv6 note: currently Host binds udp4 sockets only. The Transport interface, protocol wire format (NodeInfo.IP 4/16 bytes, observed_addr 6/18 bytes), and endpoint URL model ("quic://[v6]:port") are all IPv6-ready. Dual-stack requires changing the socket setup in New() — either "udp" dual-stack or separate v4+v6 listeners — and adding v6 candidate collection in candidates.go. No interface or data-model changes are expected.
type Host ¶
type Host struct {
// contains filtered or unexported fields
}
Host is the Phase 2a runtime.
func (*Host) Accept ¶
Accept waits for an incoming QUIC connection and returns an AgentConn.
Agent routing priority:
- Agent-route control stream (first stream: magic + 21-byte Address) — canonical.
- TLS SNI (Address hex) — fast-path hint when not camouflaged.
- Default to the host's own Address.
Remote peer AID is extracted from the mutual TLS client certificate.
func (*Host) AcceptICEViaSignal ¶
func (h *Host) AcceptICEViaSignal(ctx context.Context, localAgent, expectRemote a2al.Address, signalBase string) (*AgentConn, error)
AcceptICEViaSignal is the controlled (callee) side: WebSocket ICE signaling on signalBase, then QUIC-over-ICE. expectRemote is the caller's agent address.
Same lifetime semantics as connectViaICESignal — resources are freed when the AgentConn's underlying QUIC connection closes.
func (*Host) BuildEndpointPayload ¶
BuildEndpointPayload builds ordered, deduplicated quic:// candidates (Phase 2b). UPnP discovery and external IP probing (STUN + HTTP) run concurrently.
func (*Host) Connect ¶
func (h *Host) Connect(ctx context.Context, expectRemote a2al.Address, udpAddr *net.UDPAddr) (quic.Connection, error)
Connect dials the remote agent over QUIC with mutual TLS. After the QUIC handshake, it opens a control stream and sends the agent-route frame (4-byte magic + 21-byte target Address) so the server can route the connection even when TLS SNI is camouflaged.
func (*Host) ConnectFromRecord ¶
func (h *Host) ConnectFromRecord(ctx context.Context, expectRemote a2al.Address, er *protocol.EndpointRecord) (quic.Connection, error)
ConnectFromRecord dials expectRemote using the three-layer strategy: ① Happy Eyeballs over quic:// candidates → ② ICE via signal (if record has Signal). The host's default agent identity is used for mutual TLS.
func (*Host) ConnectFromRecordFor ¶
func (h *Host) ConnectFromRecordFor(ctx context.Context, localAgent, expectRemote a2al.Address, er *protocol.EndpointRecord) (quic.Connection, error)
ConnectFromRecordFor dials as localAgent (must be registered) toward expectRemote. After Happy Eyeballs over quic:// candidates fails, if the record includes Signal, falls back to ICE+QUIC over WebSocket signaling.
func (*Host) DHTLocalAddr ¶
func (*Host) DebugHTTPHandler ¶
DebugHTTPHandler returns an http.Handler serving /debug/host (Phase 2 state) and delegates /debug/identity, /debug/routing, /debug/store, /debug/stats to the underlying DHT node.
func (*Host) FindRecords ¶
func (h *Host) FindRecords(ctx context.Context, target a2al.Address, recType uint8) ([]protocol.SignedRecord, error)
FindRecords runs iterative FIND_VALUE for the given RecType filter (0 = all types).
func (*Host) LocalUDPAddr ¶
func (*Host) ObserveFromPeers ¶
func (*Host) PollMailbox ¶
PollMailbox aggregates mailbox records for the host default AID (spec §4.4–4.6).
func (*Host) PollMailboxForAgent ¶
func (h *Host) PollMailboxForAgent(ctx context.Context, agentAddr a2al.Address) ([]protocol.MailboxMessage, error)
PollMailboxForAgent aggregates and decrypts mailbox records for agentAddr.
func (*Host) PublishEndpoint ¶
func (*Host) PublishEndpointForAgent ¶
func (h *Host) PublishEndpointForAgent(ctx context.Context, agentAddr a2al.Address, seq uint64, ttl uint32) error
PublishEndpointForAgent publishes an endpoint record signed by the given registered agent. For Phase 3 delegated agents (registered via RegisterDelegatedAgent), the record embeds the DelegationProof so DHT nodes can verify the operational key's authority.
func (*Host) PublishRecord ¶
PublishRecord pushes a signed sovereign record (RecType 0x01–0x0F) to the DHT. Returns an error if rec is not a sovereign-category record; use PublishTopicRecord / host mailbox APIs for other categories.
func (*Host) QUICLocalAddr ¶
func (*Host) RegisterAgent ¶
RegisterAgent adds an additional agent identity to this host's SNI router. Incoming connections with TLS ServerName matching addr will be served with the corresponding certificate. Returns an error if the address is already registered.
func (*Host) RegisterDelegatedAgent ¶
func (h *Host) RegisterDelegatedAgent(addr a2al.Address, opPriv ed25519.PrivateKey, delegationCBOR []byte) error
RegisterDelegatedAgent adds a Phase 3 agent whose operational key is authorized by a DelegationProof (delegationCBOR). The proof is embedded in endpoint records so DHT nodes can verify the authority of the operational key independently.
func (*Host) RegisterTopic ¶
func (h *Host) RegisterTopic(ctx context.Context, topic string, entry protocol.TopicPayload, ttl uint32) error
RegisterTopic publishes one topic registration for the host default identity (spec §5.7).
func (*Host) RegisterTopicForAgent ¶
func (h *Host) RegisterTopicForAgent(ctx context.Context, agentAddr a2al.Address, topic string, entry protocol.TopicPayload, ttl uint32) error
RegisterTopicForAgent signs and stores a topic record for a registered agent (delegation-aware).
func (*Host) RegisterTopics ¶
func (h *Host) RegisterTopics(ctx context.Context, topics []string, base protocol.TopicPayload, ttl uint32) error
RegisterTopics registers under multiple topic strings for the default identity (spec §5.7).
func (*Host) RegisterTopicsForAgent ¶
func (h *Host) RegisterTopicsForAgent(ctx context.Context, agentAddr a2al.Address, topics []string, base protocol.TopicPayload, ttl uint32) error
RegisterTopicsForAgent is RegisterTopics for a registered agent address.
func (*Host) RegisteredAgents ¶
RegisteredAgents returns the addresses of all registered agents.
func (*Host) SearchTopic ¶
SearchTopic runs AggregateRecords on the topic key and returns verified entries (spec §5.5).
func (*Host) SearchTopics ¶
SearchTopics returns agents registered on all given topics (intersection by AID) (spec §5.5). The returned TopicEntry values are taken from the first topic's results; fields from subsequent topics are not merged.
func (*Host) SendMailbox ¶
func (h *Host) SendMailbox(ctx context.Context, recipient a2al.Address, msgType uint8, body []byte) error
SendMailbox encrypts a message for recipient using the host default identity (spec §4.4–4.6).
func (*Host) SendMailboxForAgent ¶
func (h *Host) SendMailboxForAgent(ctx context.Context, agentAddr, recipient a2al.Address, msgType uint8, body []byte) error
SendMailboxForAgent encrypts and stores a mailbox record signed by the given registered agent. Delegated agents use SignRecordDelegated (same authority model as PublishEndpointForAgent).
func (*Host) StartDebugHTTP ¶
StartDebugHTTP listens on addr and serves /debug/* JSON for both DHT and Phase 2 host state. Returns a stop function.
func (*Host) SymmetricNATReachabilityHint ¶
SymmetricNATReachabilityHint returns a user-facing note when NAT looks symmetric. Phase 2b does not guarantee inbound QUIC from arbitrary peers; TURN is deferred.
func (*Host) UnregisterAgent ¶
UnregisterAgent removes an agent from the SNI router (the default agent created at New() cannot be unregistered).