Use the driver package to build services, custom agents, and integrations in Go.
Installation
go get github.com/pilot-protocol/common
The SDK is the driver package in the pilot-protocol/common module. Import it as:
import "github.com/pilot-protocol/common/driver"
Requires Go 1.25+ and a running daemon. The driver communicates with the daemon over a Unix socket at /tmp/pilot.sock.
Quick start
package main
import (
"fmt"
"github.com/pilot-protocol/common/driver"
)
func main() {
// Connect to the local daemon
d, err := driver.Connect("")
if err != nil {
panic(err)
}
defer d.Close()
// Get node info
info, _ := d.Info()
fmt.Println("Address:", info["address"])
// Dial a remote agent on port 1000
conn, err := d.Dial("0:0000.0000.0005:1000")
if err != nil {
panic(err)
}
defer conn.Close()
conn.Write([]byte("hello"))
buf := make([]byte, 4096)
n, _ := conn.Read(buf)
fmt.Println("Response:", string(buf[:n]))
}
Driver
The Driver is the main entry point. It connects to the local daemon via IPC and provides methods for all protocol operations.
Connect
func Connect(socketPath string) (*Driver, error)
Creates a new driver connected to the local daemon. Pass "" for the default socket path (/tmp/pilot.sock, or $XDG_RUNTIME_DIR/pilot.sock when that is set on Linux). Override by passing a custom path. The PILOT_SOCKET environment variable is read by the pilotctl CLI only — it has no effect on the SDK.
Dial
func (d *Driver) Dial(addr string) (*Conn, error)
Opens a stream connection to a remote address and port. Address format: "N:XXXX.YYYY.YYYY:PORT". Returns a *Conn that implements net.Conn.
DialAddr
func (d *Driver) DialAddr(dst protocol.Addr, port uint16) (*Conn, error)
Like Dial but takes a parsed protocol.Addr and port number directly. Applies a default dial timeout so a non-responsive daemon cannot block the caller indefinitely.
Lightweight health check. Returns basic status without the full info payload.
Close
func (d *Driver) Close() error
Disconnects from the daemon and releases resources.
Conn
Conn implements the standard net.Conn interface. It can be used with any Go library that works with net.Conn - including net/http, bufio, io.Copy, and TLS wrappers.
Read(b []byte) (int, error): Read data from the connection. Blocks until data arrives or deadline expires.
Write(b []byte) (int, error): Write data to the connection.
Close() error: Close the connection (sends FIN to remote).
LocalAddr() net.Addr: Returns the local pilot address.
RemoteAddr() net.Addr: Returns the remote pilot address.
SetDeadline(t time.Time) error: Sets both the read and write deadlines to t.
SetReadDeadline(t time.Time) error: Sets the read deadline. A zero value means no deadline.
SetWriteDeadline(t time.Time) error: Sets the write deadline. A passed deadline makes Write return os.ErrDeadlineExceeded; it is enforced before each chunk. Because Write only enqueues to the local daemon over IPC (it never blocks on the remote peer), this bounds a slow local write rather than a slow peer. A zero value clears it.
Listener
Listener accepts incoming connections on a bound port. It follows the standard net.Listener pattern.
Accept() (net.Conn, error): Blocks until a new connection arrives. Returns a *Conn.
Close() error: Stops accepting connections and unblocks any pending Accept call.
Addr() net.Addr: Returns the bound pilot address.
Datagrams
Unreliable, connectionless packets. Use for fire-and-forget data or broadcast to network members.
SendTo
func (d *Driver) SendTo(dst protocol.Addr, port uint16, data []byte) error
Sends an unreliable datagram to the given address and port.
Broadcast
func (d *Driver) Broadcast(netID uint16, port uint16, data []byte, adminToken string) error
Fans a datagram out to every member of netID. Requires an admin token. Best-effort delivery — no per-recipient ACK.
RecvFrom
func (d *Driver) RecvFrom() (*Datagram, error)
Receives the next incoming datagram. Blocks until a datagram arrives.
Trust & handshakes
Handshake(nodeID uint32, justification string): Send a trust request to a remote node.
ApproveHandshake(nodeID uint32): Approve a pending trust request.
RejectHandshake(nodeID uint32, reason string): Reject a pending trust request.
PendingHandshakes(): List pending trust requests.
WaitForTrust(nodeID uint32, timeoutMs uint32): Block until mutual trust with nodeID is live or the timeout elapses.
TrustedPeers(): List all trusted peers.
RevokeTrust(nodeID uint32): Remove a peer from the trusted set.
All trust methods return (map[string]interface{}, error) with JSON-decoded response data.
Admin methods
SetHostname(hostname string): Set or clear the daemon's hostname.
SetVisibility(public bool): Set visibility on the registry (public or private).
SetTags(tags []string): Set capability tags (max 3).
SetWebhook(url string): Set or clear the webhook URL. Empty string disables.
RotateKey(): Generate a new Ed25519 keypair and re-register with the registry. Replaces ~/.pilot/identity.json in place — no rollback.
ResolveHostname(hostname string): Resolve a hostname to node info.
Deregister(): Remove this node from the registry.
Disconnect(connID uint32): Close a connection by ID.
PreferDirect(nodeID uint32): Best-effort hint asking the daemon to prefer a direct path over a relay for nodeID (e.g. to unstick a beacon-mediated tunnel). An old daemon returns an "unknown command" error - treat that as a no-op and proceed.
Verified identity
Verified-address badges and recovery enrollment. The badge, badgeSig, enrollment, and enrollmentSig strings are produced out-of-band by the verifier sidecar; the daemon signs proof of the current key before forwarding to the registry.
SubmitBadge(badge, badgeSig string): Attach a verified-address badge to this node's registry entry. The registry verifies the badge offline against the pinned issuer key. Optional - nodes without a badge keep working.
EnrollRecovery(enrollment, enrollmentSig string): Record an opaque recovery commitment so the address can be recovered if the current key is lost. The raw external identity never leaves the verifier - only the commitment.
Request signing (reqsig)
Sign and verify request-signature envelopes (common/reqsig) so a peer can prove a request came from a given pilot address. The daemon constructs the envelope itself (its own address, a fresh timestamp and nonce) and signs only the canonical form - it never signs caller-supplied raw strings.
SignEnvelope(audience, bodyHash string): Sign a reqsig envelope for audience over bodyHash (64-hex sha256 of the request body). Returns {envelope, signature, address}.
VerifyEnvelope(envelope, sigB64 string, checkStanding bool): Verify a canonical envelope + base64 signature. The daemon resolves the signer's key from cache then the registry. With checkStanding it also reports the signer's registry standing. A failed check is not an error - the reply carries valid=false plus a reason.
VerifyEnvelopeMaxSkew(envelope, sigB64 string, checkStanding bool, maxSkewSecs uint32): Same as VerifyEnvelope with an explicit freshness window in seconds. 0 selects the daemon default.
Managed networks & policy
Inspect and drive managed-network engines and their programmable policy. Policy and member-tag writes are gated by an admin token.
ManagedStatus(networkID uint16): Return the status of a managed network engine.
ManagedForceCycle(networkID uint16): Force a prune/fill cycle in a managed network.
ManagedReconcile(networkID uint16): Ask the policy runner for networkID to poll the registry and refresh its peer set without running a policy cycle. Returns {network_id, peers}.
PolicyGet(networkID uint16): Retrieve the active policy for a network.
PolicySet(networkID uint16, policyJSON []byte, adminToken string): Send a policy document for immediate application. The adminToken authenticates the caller; managed-mode daemons reject an empty token.
MemberTagsGet(networkID uint16, nodeID uint32): Retrieve admin-assigned member tags for a node in a network.
MemberTagsSet(networkID uint16, nodeID uint32, tags []string): Set admin-assigned member tags for a node in a network.
Networks
NetworkList(): List all networks known to the registry.
NetworkJoin(networkID uint16, token string): Join a network. Pass empty string for open networks.
NetworkLeave(networkID uint16): Leave a network.
NetworkMembers(networkID uint16): List all members of a network.
NetworkInvite(networkID uint16, targetNodeID uint32): Invite a node to a network (requires admin token).
NetworkPollInvites(): Check for pending network invites.
NetworkRespondInvite(networkID uint16, accept bool): Accept or reject a network invite.
Examples
Echo server
d, _ := driver.Connect("")
defer d.Close()
ln, _ := d.Listen(3000)
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
break
}
go func(c net.Conn) {
defer c.Close()
io.Copy(c, c) // echo back
}(conn)
}
Send a message and get a response
d, _ := driver.Connect("")
defer d.Close()
conn, _ := d.Dial("0:0000.0000.0005:1000")
defer conn.Close()
conn.Write([]byte("what is your status?"))
buf := make([]byte, 4096)
n, _ := conn.Read(buf)
fmt.Println(string(buf[:n]))