Documentation
¶
Overview ¶
Package device implements the peer/device domain service. The two invariants the product can never afford to break are enforced here inside BEGIN IMMEDIATE transactions (database.md): the per-user device limit and the unique per-interface IPv4 allocation. Keys arrive encrypted by the caller (secrets.KeyRing); this package never sees plaintext private keys in logs.
Index ¶
- type Device
- type KeyMaterial
- type Rotation
- type Service
- func (s *Service) CountForIfaces(ctx context.Context, ifaceIDs []string) (map[string]int, error)
- func (s *Service) CountForUser(ctx context.Context, userID string) (int, error)
- func (s *Service) CountForUsers(ctx context.Context, userIDs []string) (map[string]int, error)
- func (s *Service) Create(ctx context.Context, userID string, name string, keys KeyMaterial, ...) (*Device, error)
- func (s *Service) CreateTx(ctx context.Context, tx *sql.Tx, userID string, name string, keys KeyMaterial, ...) (*Device, error)
- func (s *Service) Delete(ctx context.Context, id string) error
- func (s *Service) Get(ctx context.Context, id string) (*Device, error)
- func (s *Service) ListForUser(ctx context.Context, userID string) ([]*Device, error)
- func (s *Service) ListForUsers(ctx context.Context, userIDs []string) (map[string][]*Device, error)
- func (s *Service) PresharedKey(ctx context.Context, d *Device) (string, error)
- func (s *Service) PrivateKey(ctx context.Context, d *Device) (string, error)
- func (s *Service) Regenerate(ctx context.Context, id string, keys KeyMaterial) error
- func (s *Service) Rename(ctx context.Context, id, name string) (*Device, error)
- func (s *Service) ReplaceUserCredentialsTx(ctx context.Context, tx *sql.Tx, userID string, rotations []Rotation) error
- func (s *Service) SetEnabled(ctx context.Context, id string, enabled bool) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Device ¶
type Device struct {
ID string
UserID string
InterfaceID string
Name string
IPv4 string // with /32
PublicKey string
PrivKeyEnc []byte
PSKEnc []byte // nil = no preshared key
Enabled bool
LastHandshake *time.Time
LastEndpoint string
RXBytes uint64
TXBytes uint64
LastRX uint64
LastTX uint64
CreatedAt time.Time
UpdatedAt time.Time
}
Device is a stored peer.
type KeyMaterial ¶
type KeyMaterial struct {
PublicKey string
PrivateKeyEnc []byte // AES-GCM envelope from secrets.KeyRing
}
KeyMaterial is the caller-provided keypair for a new device.
func GenerateKeys ¶ added in v0.1.10
func GenerateKeys(ring *secrets.KeyRing, withPSK bool) (*KeyMaterial, error)
GenerateKeys is the shared provisioning boundary for device credentials. Only sealed material and its public identity leave it; it performs no storage, rendering or host operation. API/web parsing and principal checks stay outside.
type Rotation ¶
type Rotation struct {
DeviceID string
Keys KeyMaterial
}
Rotation pairs a stored device identity with replacement key material. It is used by coordinated access-rotation workflows that must replace every credential for one user in the same database transaction.
type Service ¶
type Service struct {
// Recorder (optional, satisfied by *webhook.Recorder) emits durable
// device events inside the state-changing transaction (webhooks.md).
Recorder interface {
RecordTx(tx *sql.Tx, eventType string, data map[string]any) error
}
// contains filtered or unexported fields
}
Service holds device rules.
func (*Service) CountForIfaces ¶
CountForIfaces returns device counts per interface id (batch for the interface list). Counts all devices — including those of soft-deleted users — to mirror the interface Delete guard.
func (*Service) CountForUser ¶
CountForUser returns the live device count.
func (*Service) CountForUsers ¶
CountForUsers returns device counts for a batch of users (one query per page, not per row). Missing IDs count 0.
func (*Service) Create ¶
func (s *Service) Create(ctx context.Context, userID string, name string, keys KeyMaterial, interfaceID string) (*Device, error)
Create provisions a device for a user inside one immediate transaction: limit check → free-IP allocation → insert. Concurrent creates serialize on the write lock, so the limit can never be exceeded by a race (tested).
func (*Service) CreateTx ¶ added in v0.1.3
func (s *Service) CreateTx(ctx context.Context, tx *sql.Tx, userID string, name string, keys KeyMaterial, interfaceID string) (*Device, error)
CreateTx provisions a peer in an existing write transaction. The caller can commit the user, device and operation result together; allocation and device-limit checks retain their serialized transaction boundary.
func (*Service) ListForUser ¶
ListForUser returns the user's devices, newest first.
func (*Service) ListForUsers ¶
ListForUsers batch-loads the devices of a page of users (one query per page for the list-view quick-share menus), newest first within each user.
func (*Service) PresharedKey ¶
PresharedKey decrypts the stored preshared key ("" when none).
func (*Service) PrivateKey ¶
PrivateKey decrypts the stored private key (config generation and peer apply only — never logged, never serialized to API responses).
func (*Service) Regenerate ¶
Regenerate replaces the device keys (caller supplies the new encrypted keypair; the public key change propagates to the backend via reconcile).
func (*Service) ReplaceUserCredentialsTx ¶
func (s *Service) ReplaceUserCredentialsTx(ctx context.Context, tx *sql.Tx, userID string, rotations []Rotation) error
ReplaceUserCredentialsTx replaces the credentials for exactly all devices currently owned by userID. The exact-set check prevents a concurrent or incomplete caller snapshot from leaving one old client credential valid. The caller owns tx and may atomically rotate another access capability in the same transaction.