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
- Variables
- 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 DHTpunchPool
- func (p *DHTpunchPool) ConnCount() int
- func (p *DHTpunchPool) EvictAll()
- func (p *DHTpunchPool) HandleIncomingPunch(ctx context.Context, callerNodeID a2al.NodeID, callerLogicalAddr a2al.Address, ...)
- func (p *DHTpunchPool) HasConn(nodeID a2al.NodeID) bool
- func (p *DHTpunchPool) InvalidateConn(nodeID a2al.NodeID)
- func (p *DHTpunchPool) ProbeAndPrune(timeout time.Duration) int
- func (p *DHTpunchPool) Punch(nodeID a2al.NodeID, er *protocol.EndpointRecord, priority int)
- func (p *DHTpunchPool) SendTo(ctx context.Context, nodeID a2al.NodeID, msg []byte) (bool, error)
- type DialOptions
- 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) AcceptShared(ctx context.Context) (*AgentConn, error)
- func (h *Host) ActiveSignalURLs() []string
- func (h *Host) Address() a2al.Address
- func (h *Host) BuildEndpointPayload(ctx context.Context) (protocol.EndpointPayload, error)
- func (h *Host) BuildMailboxSignedRecord(ctx context.Context, agentAddr, recipient a2al.Address, msgType uint8, ...) (protocol.SignedRecord, 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, _ bool, err error)
- func (h *Host) ConnectFromRecordFor(ctx context.Context, localAgent, expectRemote a2al.Address, ...) (_ quic.Connection, _ bool, err error)
- func (h *Host) DHTLocalAddr() *net.UDPAddr
- func (h *Host) DHTpunchPool() *DHTpunchPool
- func (h *Host) DebugHTTPHandler() http.Handler
- func (h *Host) DecryptMailboxRecordFor(agentAddr a2al.Address, sr protocol.SignedRecord) (protocol.MailboxMessage, error)
- func (h *Host) DecryptMailboxRecords(agentAddr a2al.Address, recs []protocol.SignedRecord) ([]protocol.MailboxMessage, error)
- func (h *Host) DeriveRoutingHubURLs(max int) []string
- func (h *Host) EffectiveICESignalBase() string
- func (h *Host) EffectiveICESignalURLs() []string
- func (h *Host) FetchMailboxRawForAgent(ctx context.Context, agentAddr a2al.Address) ([]protocol.SignedRecord, error)
- func (h *Host) FindRecords(ctx context.Context, target a2al.Address, recType uint8) ([]protocol.SignedRecord, error)
- func (h *Host) InvalidateNetworkCaches()
- 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) ObserveFromRouting(ctx context.Context, n int) int
- 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) PublishEndpointBuilt(ctx context.Context, ep protocol.EndpointPayload, seq uint64, ttl uint32) error
- func (h *Host) PublishEndpointForAgent(ctx context.Context, agentAddr a2al.Address, seq uint64, ttl uint32) error
- func (h *Host) PublishMailboxRecord(ctx context.Context, recipient a2al.Address, rec protocol.SignedRecord) 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) ResolveInboundAgent(ctx context.Context, conn quic.Connection) (a2al.Address, error)
- func (h *Host) RoutingHubCandidates() []string
- func (h *Host) RunNATProbe(ctx context.Context)
- 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) SetActiveSignalURLs(urls []string)
- func (h *Host) SetBeaconStatsProvider(fn func() map[string]any)
- func (h *Host) SetBootstrapHubURLs(urls []string)
- func (h *Host) SetDHTPushHandler(fn func(key a2al.NodeID, rec protocol.SignedRecord) bool)
- func (h *Host) SetRoutingHubCandidates(urls []string)
- func (h *Host) SetSignalStatsProvider(f func() map[string]any)
- func (h *Host) StartDebugHTTP(addr string) (stop func(), err error)
- func (h *Host) SymmetricNATReachabilityHint() string
- func (h *Host) UnregisterAgent(addr a2al.Address)
- type TURNCredentialType
- type TURNServer
Constants ¶
const ALPNQuic = "a2al-quic-1"
const DefaultConnectStagger = 250 * time.Millisecond
DefaultConnectStagger is the delay between starting each QUIC dial (Happy Eyeballs).
const DefaultICEStagger = 2 * time.Second
DefaultICEStagger is the delay before starting the ICE path when racing direct QUIC against ICE in parallel. This gives direct QUIC a head start: on open networks (FullCone / public IP) the handshake completes well under 1 s, so the ICE path is never needed. On NAT-restricted networks where direct QUIC cannot succeed, ICE kicks in after this delay and the overall connection time is stagger + ICE setup (~2 s) rather than HandshakeIdleTimeout (10 s) + ICE setup.
Variables ¶
var ErrControlExchangeIncomplete = errors.New("a2al/host: control exchange incomplete")
ErrControlExchangeIncomplete is wrapped inside errors returned when the QUIC+TLS handshake succeeded but the Stream 0 B→A exchange did not complete. The connection has been closed. Callers that implement connection pools should not apply a dial-failure backoff penalty for this error — the remote is reachable; the incomplete exchange only means the connection must not be cached for reuse.
var ErrNoAgent = errors.New("a2al/host: target agent not registered")
ErrNoAgent is returned by runICESession when the signaling hub reports that the target agent is not currently registered ("noagent" frame). Callers may retry after a brief delay to ride out a callee reconnect window.
var ErrRelayRequired = errors.New("a2al/host: relay required but not available")
ErrRelayRequired is returned when a direct connection could not be established and relay was either explicitly disabled (DisableRelay=true) or not available (no TURN servers configured). The application layer may use this to prompt the user or retry with relay enabled.
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
// IsRelayed reports whether the underlying ICE path uses a TURN relay on
// either side. False for direct (host/srflx) and DCUtR punch paths.
IsRelayed bool
}
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".
// Wildcard addresses (":port", "0.0.0.0:port") are promoted to dual-stack
// ([::]:port on Unix; udp4+udp6 on Windows). Explicit IPv4 addresses
// (e.g. "1.2.3.4:5001") bind an IPv4-only socket, preserving existing
// RunNATProbe and candidate-collection behaviour for those deployments.
// Override with DisableIPv6 = true to force IPv4-only for all addresses.
ListenAddr string
// QUICListenAddr, if non-empty, is a separate UDP bind for QUIC.
// Same dual-stack rules as ListenAddr apply.
QUICListenAddr string
PrivateKey ed25519.PrivateKey
MinObservedPeers int
FallbackHost string
// DisableUPnP skips IGD port mapping for the QUIC UDP port (Phase 2b).
DisableUPnP bool
// DisableIPv6, when true, forces all sockets to IPv4-only (udp4).
// Use as an emergency escape hatch if dual-stack causes problems on a
// specific platform or network environment. Default: false (dual-stack).
DisableIPv6 bool
// ICESignalURL is the primary WebSocket base URL for ICE signaling (single URL, backward compat).
// Superseded by ICESignalURLs when that field is non-empty.
ICESignalURL string
// ICESignalURLs lists WebSocket base URLs for ICE signaling (multi-center support).
// When non-empty, supersedes ICESignalURL. The first URL is also written to
// EndpointPayload.Signal (CBOR key 3) for backward compatibility with old nodes.
ICESignalURLs []string
// ICESTUNURLs lists stun: URIs for ICE gathering; empty means default public STUN when no TURN is configured.
ICESTUNURLs []string
// ICETURNURLs lists turn: URIs with embedded credentials for ICE relay (legacy format).
// Use TURNServers for new deployments; both fields are processed when set.
ICETURNURLs []string
// TURNServers lists TURN relay servers with structured credential configuration.
// Supports Static, HMAC (coturn use-auth-secret), and REST API credential types.
// Credentials are resolved per ICE session and never published to the DHT.
TURNServers []TURNServer
// ICEPublishTurns is retained for decoding old records; new nodes do not publish turns[].
// Deprecated: callee-pays TURN relay addresses are exchanged via trickle ICE, not the DHT.
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
// SeenPeersPath is forwarded to the DHT node for seenPeers persistence (spec §7.3).
// Empty disables persistence.
SeenPeersPath string
// LearnedPathFirst enables learned-path outbound selection in the DHT node.
LearnedPathFirst bool
// ICENetworkTypes lists the ICE network types used for candidate gathering.
// Defaults to {ice.NetworkTypeUDP4, ice.NetworkTypeUDP6} (dual-stack) when
// nil or empty. ICE opens its own sockets independently of the main QUIC
// socket, so IPv6 ICE candidates are collected even on IPv4-only QUIC
// bindings. Set to {ice.NetworkTypeUDP4} to restrict to IPv4 ICE only.
ICENetworkTypes []ice.NetworkType
}
Config wires DHT + QUIC.
IPv6 note: Host binds dual-stack sockets by default (transport.ListenDualUDP). 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. Set Config.DisableIPv6 = true to force IPv4-only sockets. v6 candidate collection (candidates.go ③④) is the next step (Layer 1 M3).
type DHTpunchPool ¶ added in v0.1.8
type DHTpunchPool struct {
// contains filtered or unexported fields
}
DHTpunchPool implements dht.PunchTransport for the host layer.
Lifecycle: create with newDHTpunchPool before dht.NewNode, inject into dht.Config.PunchTransport, then call bind(host) after the Host is fully initialised. This two-phase setup avoids the dht.Node / Host circular init.
func (*DHTpunchPool) ConnCount ¶ added in v0.1.8
func (p *DHTpunchPool) ConnCount() int
ConnCount returns the number of active Mode B connections (for diagnostics).
func (*DHTpunchPool) EvictAll ¶ added in v0.2.8
func (p *DHTpunchPool) EvictAll()
EvictAll closes and evicts all Mode B QUIC connections. Called when the network topology changes: ICE-negotiated paths are bound to the old interface/NAT mapping and must be treated as invalid wholesale. Re-punch happens naturally on the next DHT RPC via deferICE / replication-probe.
func (*DHTpunchPool) HandleIncomingPunch ¶ added in v0.1.8
func (p *DHTpunchPool) HandleIncomingPunch(ctx context.Context, callerNodeID a2al.NodeID, callerLogicalAddr a2al.Address, signalBase, room string)
HandleIncomingPunch is called by the daemon ICE listener when a Mode B punch incoming is received (fr.Target == nodeAddr). It runs ICE as controlled, accepts a QUIC connection, and calls OnPunchComplete on the DHT node.
func (*DHTpunchPool) HasConn ¶ added in v0.1.8
func (p *DHTpunchPool) HasConn(nodeID a2al.NodeID) bool
HasConn implements dht.PunchTransport. Returns true if an active Mode B QUIC connection exists for nodeID.
func (*DHTpunchPool) InvalidateConn ¶ added in v0.2.8
func (p *DHTpunchPool) InvalidateConn(nodeID a2al.NodeID)
InvalidateConn implements dht.PunchTransport. Closes and evicts the Mode B QUIC connection for nodeID. Called by the DHT layer when a QUIC-routed RPC times out.
func (*DHTpunchPool) ProbeAndPrune ¶ added in v0.2.8
func (p *DHTpunchPool) ProbeAndPrune(timeout time.Duration) int
ProbeAndPrune tests each cached Mode B connection and evicts dead ones. Called at the start of handleNetworkChangeCascade to eliminate stale HasConn entries before they can pollute send-plan decisions.
For each entry:
- If qc.Context().Err() != nil: already dead, evict immediately (no I/O).
- Otherwise: attempt OpenStreamSync; failure → evict. A successful probe opens and immediately closes an empty stream, which is harmless to the remote's runReadLoop (empty data → handler returns).
All probes run concurrently; total elapsed ≤ timeout regardless of pool size. Returns the number of evicted connections.
func (*DHTpunchPool) Punch ¶ added in v0.1.8
func (p *DHTpunchPool) Punch(nodeID a2al.NodeID, er *protocol.EndpointRecord, priority int)
Punch implements dht.PunchTransport. Spawns a goroutine that dials ICE → QUIC (Mode B) and calls OnPunchComplete.
type DialOptions ¶ added in v0.2.2
type DialOptions struct {
// DisableRelay prevents relay (TURN) candidates from being used for this
// connection attempt. When true and direct connectivity fails,
// ErrRelayRequired is returned so the caller can decide whether to retry
// with relay enabled.
DisableRelay bool
}
DialOptions carries per-call dialing preferences for outbound connections. The zero value represents the default behaviour (relay allowed).
type Host ¶
type Host struct {
// contains filtered or unexported fields
}
Host is the Phase 2a runtime.
Identity layering strategy:
- DHT and signaling transport use node identity.
- QUIC mutual-TLS uses agent identity certificates (including delegated agent cert extensions when applicable).
- Each QUIC connection represents a (localAgent, remoteAgent) pair.
- The gateway relies on AgentConn.Remote as the authenticated caller AID.
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.
Stream 0 is NOT consumed here. Callers must pass the returned AgentConn through the full daemon.serveGatewayConn (not serveResolvedGatewayConn).
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.
Stream 0 (a2r1/a2r2 agent-route exchange) is consumed internally by acceptICEToQUIC's onConn callback for ICE-vs-punch winner selection. Callers that feed the returned AgentConn into the gateway MUST use daemon.serveResolvedGatewayConn (not serveGatewayConn) to avoid a duplicate AcceptStream that would swallow the first data stream.
Same lifetime semantics as connectViaICESignal — resources are freed when the AgentConn's underlying QUIC connection closes.
func (*Host) AcceptShared ¶ added in v0.2.7
AcceptShared returns the next AgentConn established over a shared ICE transport — i.e. from a caller that reused an existing hole-punched path for a subsequent agent on the same remote node. Blocks until a connection arrives or ctx is cancelled.
Run this in parallel with Accept (e.g. in a separate goroutine) to serve both the main QUIC listener and the shared-transport path.
Stream 0 is NOT consumed here. Callers must pass the returned AgentConn through the full daemon.serveGatewayConn (not serveResolvedGatewayConn).
func (*Host) ActiveSignalURLs ¶ added in v0.2.7
ActiveSignalURLs returns a snapshot of the currently connected signal hub URLs. Returns nil when no hub is connected.
func (*Host) BuildEndpointPayload ¶
BuildEndpointPayload builds ordered, deduplicated quic:// candidates (Phase 2b). UPnP discovery and external IP probing (STUN + HTTP) run concurrently.
func (*Host) BuildMailboxSignedRecord ¶ added in v0.2.4
func (h *Host) BuildMailboxSignedRecord(ctx context.Context, agentAddr, recipient a2al.Address, msgType uint8, body []byte) (protocol.SignedRecord, error)
BuildMailboxSignedRecord encrypts and signs a mailbox message for recipient, returning the SignedRecord without publishing it to the DHT. Use PublishMailboxRecord to store it, or SendMailboxForAgent for the combined path.
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, _ bool, err error)
ConnectFromRecord dials expectRemote using the following strategy:
- When both direct QUIC targets and a Signal URL are available and the NAT type does not mandate skipping direct: race direct QUIC against ICE in parallel (see connectRace). Direct gets a DefaultICEStagger head start.
- When only direct targets are available (no Signal): Happy Eyeballs only.
- When direct must be skipped or no targets exist (Signal required): ICE only.
The host's default agent identity is used for mutual TLS.
On any failure the locally-cached endpoint record for expectRemote is transparently invalidated so the next Resolve fetches fresh data.
func (*Host) ConnectFromRecordFor ¶
func (h *Host) ConnectFromRecordFor(ctx context.Context, localAgent, expectRemote a2al.Address, er *protocol.EndpointRecord, opts DialOptions) (_ quic.Connection, _ bool, err error)
ConnectFromRecordFor dials as localAgent (must be registered) toward expectRemote. Uses the same race/direct/ICE strategy as ConnectFromRecord.
On failure the locally-cached endpoint record for expectRemote is transparently invalidated (same as ConnectFromRecord).
func (*Host) DHTLocalAddr ¶
func (*Host) DHTpunchPool ¶ added in v0.1.8
func (h *Host) DHTpunchPool() *DHTpunchPool
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) DecryptMailboxRecordFor ¶ added in v0.2.4
func (h *Host) DecryptMailboxRecordFor(agentAddr a2al.Address, sr protocol.SignedRecord) (protocol.MailboxMessage, error)
DecryptMailboxRecordFor decrypts a single verified SignedRecord for agentAddr.
func (*Host) DecryptMailboxRecords ¶ added in v0.1.7
func (h *Host) DecryptMailboxRecords(agentAddr a2al.Address, recs []protocol.SignedRecord) ([]protocol.MailboxMessage, error)
DecryptMailboxRecords decrypts the given raw SignedRecords using the private key of agentAddr. Callers that obtained records from an out-of-band source (e.g. fallback infrastructure) can use this to reuse the standard decryption path without triggering a DHT query.
func (*Host) DeriveRoutingHubURLs ¶ added in v0.2.6
DeriveRoutingHubURLs derives signal hub ws:// URLs from the DHT routing table, selecting peers with high inbound TCP reachability (NATFullCone or v6 GUA). Returns up to max candidates with bucket-index diversity.
func (*Host) EffectiveICESignalBase ¶ added in v0.1.4
EffectiveICESignalBase returns the first hub URL from the effective candidate list (config > routing-derived > bootstrap-derived). Used by callers that need a single hub URL for an outbound ICE session.
func (*Host) EffectiveICESignalURLs ¶ added in v0.1.7
EffectiveICESignalURLs returns the full ordered list of ICE signaling base URLs. Callee subscribers should open one /signal connection per URL. Callers should try each URL in order until one succeeds.
func (*Host) FetchMailboxRawForAgent ¶ added in v0.2.4
func (h *Host) FetchMailboxRawForAgent(ctx context.Context, agentAddr a2al.Address) ([]protocol.SignedRecord, error)
FetchMailboxRawForAgent returns raw verified SignedRecords for agentAddr without decrypting them. The caller is responsible for decryption and deduplication. This is the low-level counterpart to PollMailboxForAgent used by the daemon's persistent mailbox store.
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) InvalidateNetworkCaches ¶ added in v0.1.4
func (h *Host) InvalidateNetworkCaches()
InvalidateNetworkCaches clears short-lived external-network caches so subsequent endpoint building uses fresh probes after network changes.
func (*Host) LocalUDPAddr ¶
func (*Host) ObserveFromPeers ¶
func (*Host) ObserveFromRouting ¶ added in v0.1.4
ObserveFromRouting samples current routing-table candidates and performs passive observed_addr collection from them. Both address families are sampled: for each peer, a v4 address and a v6 address are each collected if available, giving natsense observed_addr votes on both families.
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) PublishEndpointBuilt ¶ added in v0.1.3
func (h *Host) PublishEndpointBuilt(ctx context.Context, ep protocol.EndpointPayload, seq uint64, ttl uint32) error
PublishEndpointBuilt publishes the node endpoint using a pre-built EndpointPayload. Use this when the payload has already been constructed (e.g. to avoid a redundant probe).
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) PublishMailboxRecord ¶ added in v0.2.4
func (h *Host) PublishMailboxRecord(ctx context.Context, recipient a2al.Address, rec protocol.SignedRecord) error
PublishMailboxRecord stores a pre-built SignedRecord to the DHT under recipient's key.
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) ResolveInboundAgent ¶ added in v0.3.0
ResolveInboundAgent runs the a2r1/a2r2 control-stream exchange on an accepted QUIC connection and returns the resolved local agent address. ctx must carry a deadline; a short timeout (10–15 s) is recommended so that a slow or malicious peer cannot block the caller's goroutine indefinitely. Returns an error if the exchange fails or the resolved address is not a registered agent on this host.
func (*Host) RoutingHubCandidates ¶ added in v0.2.6
RoutingHubCandidates returns the current routing-table-derived hub candidate list.
func (*Host) RunNATProbe ¶ added in v0.1.4
RunNATProbe performs an AutoNAT-style active reachability test for both IP families. At most one probe runs at a time (TryLock); concurrent callers return immediately.
IPv4 classification:
QUIC bind IP is a public v4 WAN address → sense.RecordV4BindPublic(true); skip echo probe echo received from ≥1 v4 candidate → sense.RecordV4ProbeResult(true) no echo despite known v4 external address → sense.RecordV4ProbeResult(false) mapping probe (majority port rule) → sense.RecordMappingProbeV4(symmetric)
IPv6 classification:
no GUA interface address → sense.RecordV6GUABind(false); v6 probe skipped GUA present; echo from ≥1 v6 candidate → sense.RecordV6ProbeResult(true) GUA present; no echo → sense.RecordV6ProbeResult(false)
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). For direct delivery (QUIC), use BuildMailboxSignedRecord + the daemon's three-tier path.
func (*Host) SetActiveSignalURLs ¶ added in v0.1.8
SetActiveSignalURLs records the set of signal hub URLs with live connections. Called by ice_listener when the active set changes; BuildEndpointPayload uses this list (instead of all candidates) to avoid publishing stale hub addresses.
func (*Host) SetBeaconStatsProvider ¶ added in v0.1.7
SetBeaconStatsProvider registers fn to supply optional extra key-value fields merged into GET /debug/stats (e.g. metrics for the high-capacity auxiliary DHT path). Omitted when fn is nil; fields omitted when the callback returns nil or an empty map.
func (*Host) SetBootstrapHubURLs ¶ added in v0.2.6
SetBootstrapHubURLs sets signal hub URLs derived from bootstrap/DNS infrastructure. These serve as a stable fallback when routing-table candidates are unavailable. Explicit config (ICESignalURLs / ICESignalURL) always wins and is not overwritten.
func (*Host) SetDHTPushHandler ¶ added in v0.2.4
SetDHTPushHandler registers fn as the handler for incoming MsgDHTPush messages (oneShot push from DHT nodes). fn(key, record) returns true when the record is new, which causes the ACK to renew the subscription for future deliveries. Works in both single-port (UDPMux) and dual-port modes.
func (*Host) SetRoutingHubCandidates ¶ added in v0.2.6
SetRoutingHubCandidates sets signal hub candidates derived from the routing table. These are preferred over bootstrap URLs and are refreshed periodically. Explicit config (ICESignalURLs / ICESignalURL) always wins and is not overwritten.
func (*Host) SetSignalStatsProvider ¶ added in v0.1.4
SetSignalStatsProvider merges hub stats into GET /debug/stats under "signal".
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).
type TURNCredentialType ¶ added in v0.1.4
type TURNCredentialType int
TURNCredentialType selects how TURN credentials are obtained per session.
const ( // TURNCredentialStatic uses a fixed username and password stored in config. TURNCredentialStatic TURNCredentialType = iota // TURNCredentialHMAC generates time-limited credentials from a shared secret // using the coturn use-auth-secret mechanism: // username = strconv.FormatInt(unix_expiry, 10) + ":" + Username // password = base64(HMAC-SHA1(Credential, username)) TURNCredentialHMAC // TURNCredentialRESTAPI fetches short-lived credentials from an HTTP endpoint // before each ICE session. The response must be JSON with "username" and // "password" (or "credential") fields. Covers Twilio, Metered.ca, etc. TURNCredentialRESTAPI )
type TURNServer ¶ added in v0.1.4
type TURNServer struct {
// URL is the TURN server address without embedded credentials,
// e.g. "turn:turn.example.com:3478?transport=udp".
URL string
// CredentialType selects the credential acquisition method.
CredentialType TURNCredentialType
// Username is the static username (Static) or base username prefix (HMAC).
Username string
// Credential is the static password (Static), the HMAC shared secret (HMAC),
// or the Authorization header value for the REST API request (RESTAPI).
Credential string
// CredentialURL is the HTTP endpoint that returns short-lived credentials (RESTAPI only).
CredentialURL string
}
TURNServer describes a TURN relay server and how to obtain credentials for it. Credentials are resolved fresh per ICE session; they are never published to the DHT.