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

hostcustody

package
v0.55.11 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package hostcustody is the node side of sealed host delivery (pkg/hostdelivery). It owns this server's recipient key and its generations, the delivering authority key pinned for each generation, the replay ledger, the registry of delivered secrets, detachment, and the local custody of delivered Wallet items and SSH access state.

A StackKit acts only on the server it is installed on: every record lives in the node's owner-signed private custody, the recipient private key and every delivered value are encrypted under the node's local custody key, and no value is ever written to a log, receipt or stored command. Callers hold the lifecycle mutation while they call a mutating function.

Index

Constants

This section is empty.

Variables

View Source
var ErrDetached = errors.New("hostcustody: this node is detached from host delivery; re-enroll with a recipient rotation")

ErrDetached reports that this node was detached from host delivery and must be re-enrolled (a recipient rotation) before it accepts a delivery again.

View Source
var ErrMissing = errors.New("hostcustody: no host custody record")

ErrMissing reports that a host custody record does not exist.

Functions

func EstablishRecipient

func EstablishRecipient(store Store) error

EstablishRecipient creates the node's encryption recipient key (generation 1) once. The private half is encrypted under the local custody key and never leaves this node; no signing key is ever used to decrypt.

func LoadWalletItem

func LoadWalletItem(store Store, tenantRef, itemRef string) (uint64, []byte, error)

LoadWalletItem returns the newest version of an item in custody and a caller-owned copy of its value. Callers clear the value after use.

func PinAuthority

func PinAuthority(store Store, authority AuthorityKey, now time.Time) error

PinAuthority pins the delivering authority key for the current generation. Pinning the same key again changes nothing; a different key is refused, and only a recipient rotation (re-enrollment) can replace it.

func PruneRetired

func PruneRetired(store Store, now time.Time) error

PruneRetired destroys every retired recipient key whose window has closed.

func RecipientStatement

func RecipientStatement(store Store, challenge string, now time.Time) (hostdelivery.Statement, error)

RecipientStatement proves the current recipient key belongs to this node's owner, names its generation and pinned authority, and answers the delivering authority's challenge. A detached node issues no statement.

func RotateRecipient

func RotateRecipient(store Store, authority *AuthorityKey, now time.Time) (uint64, error)

RotateRecipient re-enrolls the node under recipient generation n+1. The previous key stays openable for deliveries issued before this moment, for at most the maximum delivery lifetime, and is then destroyed; a detached node destroys it at once. A rotation ends detachment. A non-nil authority replaces the pinned delivering authority key for the new generation; otherwise the current pin carries over. It returns the new generation.

func SaveSSHAccess

func SaveSSHAccess(store Store, record SSHAccess) error

SaveSSHAccess records the SSH access this node must hold.

Types

type AuthorityKey

type AuthorityKey struct {
	KeyID     string `json:"keyId"`
	PublicKey string `json:"publicKey"`
	PinnedAt  string `json:"pinnedAt,omitempty"`
}

AuthorityKey is a delivering authority's Ed25519 public key pinned by the node.

func ParseAuthorityKey

func ParseAuthorityKey(keyID, encoded string) (AuthorityKey, error)

ParseAuthorityKey validates a base64 (raw or padded) Ed25519 public key.

func (AuthorityKey) Fingerprint

func (a AuthorityKey) Fingerprint() string

Fingerprint names the authority key in statements and status.

type DeliveredItem

type DeliveredItem struct {
	TenantRef     string `json:"tenantRef"`
	DeliveryRef   string `json:"deliveryRef"`
	ItemVersion   uint64 `json:"itemVersion"`
	Operation     string `json:"operation"`
	DeliveredAt   string `json:"deliveredAt"`
	NeedsRotation bool   `json:"needsRotation,omitempty"`
}

DeliveredItem names one secret that reached this node. It never carries the value.

type Delivery

type Delivery struct {
	Context   hostdelivery.Context
	Plaintext []byte
}

Delivery is one opened delivery. The caller owns Plaintext and clears it.

func OpenDelivery

func OpenDelivery(store Store, raw []byte, expect Expectation, now time.Time) (Delivery, error)

OpenDelivery opens one sealed delivery for this node and consumes it. It refuses a delivery for another tenant, Site, node or operation, a detached node, a retired or unknown recipient generation, an operation that needs an authority signature before one is pinned, any delivery whose signature is missing or not by the pinned key once one is pinned, an expired or oversized one, and a replay. The delivery is recorded as consumed (and a secret one in the delivered registry) before the plaintext is returned, so a replay, including one after a crash, is denied and the delivering authority must seal a fresh one.

type DetachReport

type DetachReport struct {
	Generation uint64          `json:"generation"`
	DetachedAt string          `json:"detachedAt"`
	Rotate     []DeliveredItem `json:"rotate"`
}

DetachReport names what the owner must rotate upstream after a detach. It carries references and versions only, never a value.

func Detach

func Detach(store Store, now time.Time) (DetachReport, error)

Detach ends host delivery for this node's current binding: every delivered secret is marked as needing rotation, every retired recipient key is destroyed, and no delivery opens until the node is re-enrolled with a recipient rotation. Values already in local custody stay usable here; the report lists the references to rotate where they came from. Detaching again keeps the first detach time and reports the same references.

type Expectation

type Expectation struct {
	TenantRef  string
	Operations []string
}

Expectation is what the opening node requires of a delivery beyond its own identity: the tenant the owner operates for, and the operations this caller can carry out.

type RetiredGeneration

type RetiredGeneration struct {
	Generation uint64 `json:"generation"`
	RetiredAt  string `json:"retiredAt"`
	OpenUntil  string `json:"openUntil"`
}

RetiredGeneration is one previous recipient generation still openable.

type SSHAccess

type SSHAccess struct {
	APIVersion string              `json:"apiVersion"`
	CAKeys     []string            `json:"caKeys"`
	Principals map[string][]string `json:"principals"`
	// TrustRef and TrustVersion name the delivery the trust came from.
	TrustRef     string `json:"trustRef,omitempty"`
	TrustVersion uint64 `json:"trustVersion,omitempty"`
	// HostCertificate is one OpenSSH host certificate line.
	HostCertificate        string `json:"hostCertificate,omitempty"`
	HostCertificateRef     string `json:"hostCertificateRef,omitempty"`
	HostCertificateVersion uint64 `json:"hostCertificateVersion,omitempty"`
	// CleanupPending names target-side effects of a revocation that could not
	// be completed yet (for example an sshd reload).
	CleanupPending []string                                `json:"cleanupPending,omitempty"`
	UpdatedAt      string                                  `json:"updatedAt"`
	Signature      localevidence.OwnerPolicyStateSignature `json:"signature"`
}

SSHAccess is the SSH access this node was told to hold: the owner's user CA trust with the principals each local user accepts, and the host certificate. None of it is secret, but it is integrity-critical, so it lives in owner-signed custody and host security repair reinstalls it from here.

func LoadSSHAccess

func LoadSSHAccess(store Store) (SSHAccess, error)

LoadSSHAccess reads the recorded SSH access. A node that never received one is ErrMissing.

type Status

type Status struct {
	Generation           uint64              `json:"generation"`
	Fingerprint          string              `json:"fingerprint"`
	AuthorityKeyID       string              `json:"authorityKeyId,omitempty"`
	AuthorityFingerprint string              `json:"authorityFingerprint,omitempty"`
	Detached             bool                `json:"detached"`
	DetachedAt           string              `json:"detachedAt,omitempty"`
	Retired              []RetiredGeneration `json:"retired"`
	Delivered            []DeliveredItem     `json:"delivered"`
}

Status is the secret-free state of this node's host delivery custody.

func ReadStatus

func ReadStatus(store Store, now time.Time) (Status, error)

ReadStatus reports the recipient generation, retired generations still openable, the pinned authority, detachment and every delivered secret by reference. It changes nothing; a node without a recipient is ErrMissing.

type Store

type Store interface {
	// Workspace is the StackKit workspace root whose Owner custody signs records.
	Workspace() string
	// WrappingKey returns a fresh caller-owned copy of the local custody key,
	// establishing it once. Callers clear it after use.
	WrappingKey() ([]byte, error)
	// Read decodes one owner-signed private record by file name, strictly and
	// within maxBytes, and verifies its signature. A missing record is
	// ErrMissing.
	Read(name string, maxBytes int64, record any, signature *localevidence.OwnerPolicyStateSignature) error
	// Write signs and durably writes one private record. With create the
	// record must not exist yet.
	Write(name string, record any, signature *localevidence.OwnerPolicyStateSignature, create bool) error
	// Remove deletes one private record; a missing record is not an error.
	Remove(name string) error
}

Store is the node's owner-bound private storage. Backup custody provides it so the recipient key and the delivery ledger stay in the files and under the local custody key the node already holds.

type WalletItemReceipt

type WalletItemReceipt struct {
	TenantRef string   `json:"tenantRef"`
	ItemRef   string   `json:"itemRef"`
	Version   uint64   `json:"version"`
	Removed   []uint64 `json:"removedVersions,omitempty"`
	// CleanupPending names older version records that could not be removed;
	// the index no longer serves them.
	CleanupPending []string `json:"cleanupPending,omitempty"`
}

WalletItemReceipt reports a stored item by reference; it carries no value.

func StoreWalletItem

func StoreWalletItem(store Store, delivery Delivery, now time.Time) (WalletItemReceipt, error)

StoreWalletItem places an opened wallet-item-import or wallet-item-rotate delivery in encrypted local custody, keyed by tenant, item reference and version. Versions are immutable: an import must be newer than every version in custody, and a rotation must replace an item already held, after which every older version is removed.

type WalletItemRemoval added in v0.53.9

type WalletItemRemoval struct {
	TenantRef string   `json:"tenantRef"`
	ItemRef   string   `json:"itemRef"`
	Removed   []uint64 `json:"removedVersions"`
	// CleanupPending names value records that could not be deleted; the index
	// no longer serves them.
	CleanupPending []string `json:"cleanupPending,omitempty"`
}

WalletItemRemoval reports which versions of an item left local custody. It carries references and versions only. An item already absent is not an error: Removed is empty and the node holds nothing of it.

func RemoveWalletItem added in v0.53.9

func RemoveWalletItem(store Store, delivery Delivery) (WalletItemRemoval, error)

RemoveWalletItem applies an opened wallet-item-remove delivery: every version of the item it names leaves the index first, so nothing serves it any more, and then its encrypted value records are deleted. The delivered registry keeps the reference: the value did reach this node, so a later detach still lists it for rotation.

type WalletItemStatus

type WalletItemStatus struct {
	TenantRef     string   `json:"tenantRef"`
	ItemRef       string   `json:"itemRef"`
	Versions      []uint64 `json:"versions"`
	NeedsRotation bool     `json:"needsRotation"`
}

WalletItemStatus lists one item by reference and the versions in custody.

func ListWalletItems

func ListWalletItems(store Store) ([]WalletItemStatus, error)

ListWalletItems lists every item by tenant, reference and versions, with its rotation mark. It never reads a value.

Jump to

Keyboard shortcuts

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