host

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2026 License: MPL-2.0 Imports: 36 Imported by: 0

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

View Source
const ALPNQuic = "a2al-quic-1"
View Source
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 New

func New(cfg Config) (*Host, error)

New creates a Host with one initial agent identity from cfg.KeyStore.

func (*Host) Accept

func (h *Host) Accept(ctx context.Context) (*AgentConn, error)

Accept waits for an incoming QUIC connection and returns an AgentConn.

Agent routing priority:

  1. Agent-route control stream (first stream: magic + 21-byte Address) — canonical.
  2. TLS SNI (Address hex) — fast-path hint when not camouflaged.
  3. 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) Address

func (h *Host) Address() a2al.Address

func (*Host) BuildEndpointPayload

func (h *Host) BuildEndpointPayload(ctx context.Context) (protocol.EndpointPayload, error)

BuildEndpointPayload builds ordered, deduplicated quic:// candidates (Phase 2b). UPnP discovery and external IP probing (STUN + HTTP) run concurrently.

func (*Host) Close

func (h *Host) Close() error

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 (h *Host) DHTLocalAddr() *net.UDPAddr

func (*Host) DebugHTTPHandler

func (h *Host) DebugHTTPHandler() http.Handler

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 (h *Host) LocalUDPAddr() *net.UDPAddr

func (*Host) Node

func (h *Host) Node() *dht.Node

func (*Host) ObserveFromPeers

func (h *Host) ObserveFromPeers(ctx context.Context, seeds []net.Addr)

func (*Host) PollMailbox

func (h *Host) PollMailbox(ctx context.Context) ([]protocol.MailboxMessage, error)

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 (h *Host) PublishEndpoint(ctx context.Context, seq uint64, ttl uint32) error

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

func (h *Host) PublishRecord(ctx context.Context, rec protocol.SignedRecord) error

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 (h *Host) QUICLocalAddr() *net.UDPAddr

func (*Host) RegisterAgent

func (h *Host) RegisterAgent(addr a2al.Address, priv ed25519.PrivateKey) error

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

func (h *Host) RegisteredAgents() []a2al.Address

RegisteredAgents returns the addresses of all registered agents.

func (*Host) Resolve

func (h *Host) Resolve(ctx context.Context, target a2al.Address) (*protocol.EndpointRecord, error)

func (*Host) SearchTopic

func (h *Host) SearchTopic(ctx context.Context, topic string) ([]protocol.TopicEntry, error)

SearchTopic runs AggregateRecords on the topic key and returns verified entries (spec §5.5).

func (*Host) SearchTopics

func (h *Host) SearchTopics(ctx context.Context, topics []string) ([]protocol.TopicEntry, error)

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) Sense

func (h *Host) Sense() *natsense.Sense

func (*Host) StartDebugHTTP

func (h *Host) StartDebugHTTP(addr string) (stop func(), err error)

StartDebugHTTP listens on addr and serves /debug/* JSON for both DHT and Phase 2 host state. Returns a stop function.

func (*Host) SymmetricNATReachabilityHint

func (h *Host) SymmetricNATReachabilityHint() string

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

func (h *Host) UnregisterAgent(addr a2al.Address)

UnregisterAgent removes an agent from the SNI router (the default agent created at New() cannot be unregistered).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL