handshake

package
v1.10.0-rc3 Latest Latest
Warning

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

Go to latest
Published: May 9, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package handshake implements the manual trust-handshake protocol on port 444 (the Pilot Protocol PortHandshake well-known port).

This is the higher-level "establish trust between two nodes" RPC, NOT the L5 wire-level key-exchange (which lives in pkg/daemon/tunnel.go). Two daemons that have not previously met exchange a JSON handshake message over a normal stream connection on port 444; both sides validate signatures, optionally consult an allowlist, and either auto-accept or stash the request for manual approval via pilotctl.

Lifecycle: this plugin sits at L11. The composition root (plugins/runtime or test harness) constructs a Service via NewService(rt Runtime), registers it on the plugin runtime, and the daemon's Start() spawns the listener on PortHandshake via rt.PortListener.

Index

Constants

View Source
const (
	HandshakeRequest = "handshake_request"
	HandshakeAccept  = "handshake_accept"
	HandshakeReject  = "handshake_reject"
	HandshakeRevoke  = "handshake_revoke"
)

Handshake message types.

Variables

This section is empty.

Functions

This section is empty.

Types

type HandshakeMsg

type HandshakeMsg struct {
	Type          string `json:"type"`
	NodeID        uint32 `json:"node_id"`
	PublicKey     string `json:"public_key"`    // base64 Ed25519 public key
	Justification string `json:"justification"` // why the sender wants to connect
	Signature     string `json:"signature"`     // Ed25519 sig over "handshake:<node_id>:<peer_id>"
	Reason        string `json:"reason"`        // rejection reason
	Timestamp     int64  `json:"timestamp"`
}

HandshakeMsg is the wire format for handshake protocol messages on port 444.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager handles the trust handshake protocol on port 444.

func NewManager

func NewManager(rt Runtime) *Manager

NewManager constructs a Manager bound to the given Runtime. Loads persisted trust state from disk if the runtime exposes an identity path; otherwise the manager is in-memory only.

func (*Manager) ApproveHandshake

func (hm *Manager) ApproveHandshake(peerNodeID uint32) error

ApproveHandshake approves a pending handshake request.

func (*Manager) IsTrusted

func (hm *Manager) IsTrusted(nodeID uint32) bool

IsTrusted returns whether a peer has been approved.

func (*Manager) PendingCount

func (hm *Manager) PendingCount() int

PendingCount returns the number of pending handshake requests.

func (*Manager) PendingRequests

func (hm *Manager) PendingRequests() []PendingHandshake

PendingRequests returns all pending handshake requests.

func (*Manager) ProcessRelayedApproval

func (hm *Manager) ProcessRelayedApproval(fromNodeID uint32)

ProcessRelayedApproval handles a handshake approval received via registry relay. Exported for daemon-side polling glue.

func (*Manager) ProcessRelayedRejection

func (hm *Manager) ProcessRelayedRejection(fromNodeID uint32)

ProcessRelayedRejection handles a handshake rejection received via registry relay. Exported for daemon-side polling glue.

func (*Manager) ProcessRelayedRequest

func (hm *Manager) ProcessRelayedRequest(fromNodeID uint32, justification string)

ProcessRelayedRequest handles a handshake request received via registry relay. Exported for daemon-side polling glue (Daemon.pollRelayedHandshakes).

func (*Manager) RejectHandshake

func (hm *Manager) RejectHandshake(peerNodeID uint32, reason string) error

RejectHandshake rejects a pending handshake request.

func (*Manager) RevokeTrust

func (hm *Manager) RevokeTrust(peerNodeID uint32) error

RevokeTrust removes a peer from the trusted set and notifies both the registry and the peer itself. Either party can revoke unilaterally.

func (*Manager) SendRequest

func (hm *Manager) SendRequest(peerNodeID uint32, justification string) error

SendRequest sends a handshake request to a remote node. First tries direct connection (port 444). If that fails (e.g. private node), falls back to relaying through the registry.

func (*Manager) Start

func (hm *Manager) Start() error

Start binds port 444 and begins handling handshake connections.

func (*Manager) Stop

func (hm *Manager) Stop()

Stop waits for all background RPCs to finish and stops the replay reaper.

func (*Manager) TrustedPeers

func (hm *Manager) TrustedPeers() []TrustRecord

TrustedPeers returns all trusted peers.

func (*Manager) WaitForTrust

func (hm *Manager) WaitForTrust(nodeID uint32, timeout time.Duration) bool

WaitForTrust blocks until the given peer transitions to trusted, or the timeout elapses. Returns true if trust was granted within the window, false on timeout. The fast path (already trusted) is a single map lookup under hm.mu — no goroutine, no channel allocation.

Self is always trusted: a daemon trivially trusts itself, and gating loopback operations behind a non-existent trust record would block pilotctl self-pings and any plugin operating against the local node.

type PendingHandshake

type PendingHandshake struct {
	NodeID        uint32
	PublicKey     string
	Justification string
	ReceivedAt    time.Time
}

PendingHandshake is an unapproved incoming request.

type RegistryClient

type RegistryClient interface {
	Lookup(nodeID uint32) (map[string]interface{}, error)
	ReportTrust(nodeID, peerID uint32) (map[string]interface{}, error)
	RevokeTrust(nodeID, peerID uint32) (map[string]interface{}, error)
	RequestHandshake(fromNodeID, toNodeID uint32, justification, signatureB64 string) (map[string]interface{}, error)
	RespondHandshake(nodeID, peerID uint32, accept bool, signatureB64 string) (map[string]interface{}, error)
	PollHandshakes(nodeID uint32) (map[string]interface{}, error)
}

RegistryClient is the subset of pkg/registry/client.Client the handshake manager uses. Defined here as an interface so the manager stays free of import cycles and tests can supply a fake.

Method signatures match *registry/client.Client exactly, so the daemon adapter can return the underlying client directly.

type Runtime

type Runtime interface {
	// NodeID returns this daemon's stable node ID. Returns 0 if
	// identity has not been initialized yet.
	NodeID() uint32

	// HasIdentity reports whether the daemon has loaded an Ed25519
	// identity. SendRequest / signing paths early-return when false.
	HasIdentity() bool

	// PublicKey returns the local Ed25519 public key (for inclusion in
	// outbound HandshakeMsg.PublicKey). Returns nil when HasIdentity is
	// false.
	PublicKey() ed25519.PublicKey

	// Sign signs the given payload with the local Ed25519 private key.
	// Returns nil when HasIdentity is false; callers handle that as
	// "no signature attached".
	Sign(msg []byte) []byte

	// IdentityPath returns the configured on-disk identity path; the
	// trust.json store sits in the same directory. Empty when the
	// daemon runs in-memory only (no persistence).
	IdentityPath() string

	// TrustAutoApprove reports whether the daemon was configured to
	// auto-approve all incoming handshake requests (trust-on-first-use
	// stance for friendly fleets).
	TrustAutoApprove() bool

	// IsTrusted consults the trustedagents allowlist. Returns the
	// agent's display name and ok=true on a match. Returns "", false
	// when no checker has been registered (treated as "not trusted").
	IsTrusted(nodeID uint32) (name string, ok bool)

	// PublishEvent emits a topic+payload event onto the daemon's event
	// bus. Non-blocking; events may be dropped under bus pressure.
	PublishEvent(topic string, payload map[string]any)

	// PortListener binds the given well-known port and returns a
	// coreapi.Listener. The handshake plugin uses this for PortHandshake.
	// Closing the returned listener stops the accept loop.
	PortListener(port uint16) (coreapi.Listener, error)

	// DialAndSend establishes a stream to (peerNodeID, port), writes
	// the JSON-encoded message, and closes after a brief grace period
	// to let the data flush.
	DialAndSend(peerNodeID uint32, port uint16, data []byte) error

	// RemoveTunnelPeer tears down the encrypted tunnel for the given
	// peer. Called from RevokeTrust so a revoked peer's tunnel state
	// (session keys, retransmit queues) is purged immediately.
	RemoveTunnelPeer(nodeID uint32)

	// Registry returns the L8 registry-side-channel client used for
	// peer lookups, trust-pair reporting, and relay-based handshake
	// when direct dial fails. Returns nil when the daemon is offline
	// or running without a registry connection — callers must nil-check.
	Registry() RegistryClient
}

Runtime is the primitives-only contract the handshake manager needs from the surrounding daemon. Every dependency that crossed the old `hm.daemon.X` boundary now flows through one of these methods.

The concrete adapter (plugins/runtime.HandshakeRuntime) wraps a *daemon.Daemon to satisfy this interface. Tests can substitute a fake runtime to exercise the manager in isolation.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the L11 plugin adapter for the manual trust-handshake runtime (port 444). Wraps a Manager and bridges between the coreapi.Service lifecycle (Start/Stop with deps) and the Runtime-interface accept loop.

func NewService

func NewService(rt Runtime) *Service

NewService constructs a handshake plugin Service over the given Runtime. The composition root (plugins/runtime or test harness) builds the Runtime and registers the resulting Service.

func (*Service) Manager

func (s *Service) Manager() *Manager

Manager returns the underlying Manager (test access + composition glue).

func (*Service) Name

func (s *Service) Name() string

func (*Service) Order

func (s *Service) Order() int

func (*Service) Start

func (s *Service) Start(_ context.Context, _ coreapi.Deps) error

func (*Service) Stop

func (s *Service) Stop(_ context.Context) error

type TrustRecord

type TrustRecord struct {
	NodeID     uint32
	PublicKey  string // base64 Ed25519 pubkey
	ApprovedAt time.Time
	Mutual     bool   // true if both sides initiated
	Network    uint16 // non-zero if trust is via network membership
}

TrustRecord holds information about a trusted peer.

Jump to

Keyboard shortcuts

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