Help us shape the future of Go! Take the 2026 Go Developer Survey

device

package
v0.18.2 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package device is the daemon-side durable registry of paired remote-control devices (R-DEV.1). Each record pins a device's identity keys and its R-POL.6 capability tier; the daemon reads it to authorize every remote command (R-POL.9). It is fed from a pairing MachineOutcome (whose DevicePayload now carries the Ed25519 command-signing key, ADR-007 2026-07-20).

Index

Constants

View Source
const PushTransportGateway = "gateway"

PushTransportGateway is the only transport a persisted PushBinding may select. A legacy or foreground-only pairing carries no PushBinding at all.

Variables

This section is empty.

Functions

func DeviceIDFor

func DeviceIDFor(commandSignPub []byte) string

DeviceIDFor derives the canonical device id from the device's Ed25519 command-signing public key: the hex SHA-256 of the key. It is deterministic and collision-avoiding, and it binds the device id to exactly the key R-POL.9 verifies command signatures against.

func ValidatePushBinding added in v0.13.19

func ValidatePushBinding(push PushBinding) error

ValidatePushBinding validates the complete authority-bearing record before either the pairing protocol accepts it or the registry persists it. Sharing this boundary prevents a phone from receiving acceptance for a shape enrollment will reject one frame later.

Types

type Action

type Action uint8

Action is a class of remote operation the daemon authorizes against a device's Capability (R-POL.9). Each concrete wire op maps onto exactly one of these classes.

const (
	// ActionRead covers non-mutating ops (list, journal_read/subscribe, snapshot peek).
	ActionRead Action = iota
	// ActionApprove covers approving an agent interaction (R-POL.8).
	ActionApprove
	// ActionControl covers mutating control ops (input, interrupt, kill, launch,
	// take_control).
	ActionControl
)

type Capability

type Capability uint8

Capability is a device's authorization tier (R-POL.6). The zero value is deliberately invalid and grants nothing, so a zero-initialised or corrupted Capability fails closed.

const (

	// CapReadOnly may only read (list/journal/snapshot); it may not approve.
	CapReadOnly Capability
	// CapReadApprove may read and approve agent interactions, but not control
	// (input/interrupt/kill/launch).
	CapReadApprove
	// CapFull may read, approve, and control.
	CapFull
)

func (Capability) Allows

func (c Capability) Allows(a Action) bool

Allows reports whether a device holding capability c may perform action a. It is fail-closed: an unknown capability value grants nothing, and read_only/read_approve never reach ActionControl.

func (Capability) MarshalText

func (c Capability) MarshalText() ([]byte, error)

MarshalText renders the tier as its stable snake_case name; an invalid tier is an error so a corrupted Capability can never be persisted.

func (*Capability) UnmarshalText

func (c *Capability) UnmarshalText(b []byte) error

UnmarshalText parses a snake_case tier name. An unknown string is an error (fail-closed) -- it never silently decodes to a tier, above all not CapFull.

type PushBinding added in v0.13.19

type PushBinding struct {
	GatewayURL              string `json:"gateway_url"`
	Address                 []byte `json:"address"`
	SubmitCapability        string `json:"submit_capability"`
	MachineRevokeCapability string `json:"machine_revoke_capability"`
	WakeKey                 []byte `json:"wake_key"`
	CapabilityRecordVersion int    `json:"capability_record_version"`
	Transport               string `json:"transport"`
}

PushBinding is the machine's durable half of the authenticated msg4 extension. WakeKey is phone-generated per pairing and deliberately independent of epoch keys.

type Record

type Record struct {
	DeviceID       string     `json:"device_id"`
	Name           string     `json:"name"`
	NoiseStaticPub []byte     `json:"noise_static_pub"`
	RelayAuthPub   []byte     `json:"relay_auth_pub"`
	CommandSignPub []byte     `json:"command_sign_pub"`
	RecipientPub   []byte     `json:"recipient_pub"`
	RoutingID      []byte     `json:"routing_id"`
	Capability     Capability `json:"capability"`
	PairedAt       time.Time  `json:"paired_at"`
	GrantedEpoch   uint32     `json:"granted_epoch"`
	// ConsentSig is the device's relay-route consent for THIS machine, signed with the
	// device's relay-auth key during the pairing ceremony and carried in msg3 (ADR-007
	// B27/B38). It is persisted because the two machine-side callers that need it —
	// cmd/swarm/remote.go authorizeAtRelay at pairing and cmd/swarm-remote's
	// deliverEpochGrant on every gateway connect — run in different processes at
	// different times, and neither can re-derive it: only the device can produce it.
	//
	// It is NOT a secret and storing it beside the pinned public keys leaks nothing: it
	// names this machine's routing id as its sole grantee, so a copy authorizes nobody
	// else (relay.ConsentMessage). It is not validated here for length, because the
	// relay is the authority that verifies it and a record that merely fails there is a
	// re-pair, not a corrupt registry.
	ConsentSig []byte `json:"consent_sig,omitempty"`
	// Push is the authenticated, per-pairing push binding conveyed in msg4. Keeping it
	// inside the registry record makes device admission and its runtime push authority one
	// atomic rename: no process can observe a paired device with another pairing's address
	// or wake key. Nil is the legacy/foreground-compatible record shape.
	Push *PushBinding `json:"push,omitempty"`
}

Record is one paired device pinned in the registry (R-DEV.1): its identity keys, routing/name, capability tier, pairing time, and the epoch it was granted at.

type Registry

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

Registry is the durable, mutex-guarded device store (R-DEV.1). Its in-memory map is always consistent with the single devices.json on disk: every mutation persists atomically (temp+rename+Sync) and rolls back the in-memory change on a write error.

func Open

func Open(dir string) (*Registry, error)

Open opens (creating the directory if needed) the device registry rooted at dir, loading any previously persisted devices. A malformed registry file is an error (fail-closed): the daemon must not silently start with zero paired devices, which would both drop remote access and, via R-KS.2, flip the kill switch.

func (*Registry) Add

func (r *Registry) Add(rec Record) error

Add validates and upserts one device, persisting durably. It rejects (without persisting) a record with an empty device id, a non-32-byte command-signing key, or an unknown capability. On a persistence error the in-memory map is rolled back so it never diverges from disk.

func (*Registry) AddSole

func (r *Registry) AddSole(rec Record) error

AddSole atomically enrolls rec as the registry's SOLE device: under the registry mutex it rejects the commit when a DIFFERENT device is already present, so two concurrent single-device enrollments that both passed a Count()==0 pre-check cannot both commit (finding C1 -- a non-atomic check-then-Add would leave two devices and brick the gateway). Re-adding the SAME device id is an idempotent upsert. Like Add it validates, persists durably, and rolls back the in-memory change on a write error. The general Add stays uncapped; the single-device cap lives ONLY here, so the registry's multi-device tests are unaffected.

func (*Registry) Authorized

func (r *Registry) Authorized(deviceID string, a Action) bool

Authorized reports whether the device may perform action a. It is fail-closed: an unknown device is authorized for nothing.

func (*Registry) Count

func (r *Registry) Count() int

Count returns the number of registered devices.

func (*Registry) Get

func (r *Registry) Get(deviceID string) (Record, bool)

Get returns a copy of the record for deviceID, or ok=false if unknown.

func (*Registry) List

func (r *Registry) List() []Record

List returns all records, deterministically ordered by device id.

func (*Registry) Remove

func (r *Registry) Remove(deviceID string) (bool, error)

Remove deletes deviceID, reporting whether a record was present. Removing an absent id is a no-op reporting false, not an error. On a persistence error the removal is rolled back.

Jump to

Keyboard shortcuts

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