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

hostdelivery

package
v0.55.0 Latest Latest
Warning

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

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

Documentation

Overview

Package hostdelivery is the public wire contract for sealed host delivery of one input to the StackKit node that owns it. A delivering authority such as Techstack or the kombify Wallet seals the value to a recipient key that the node generated and proved; only that node opens it into its own custody. The authority and every transport between them carry ciphertext and opaque references only.

The sealing scheme is HPKE (RFC 9180): DHKEM(X25519, HKDF-SHA256) with HKDF-SHA256 and ChaCha20-Poly1305. The canonical Context is the HPKE info, so a ciphertext opens only under the exact delivery, tenant, server, binding generation, operation, recipient key and expiry it was sealed for.

HPKE base mode does not authenticate the sender: anyone holding the public recipient key can seal to it. Operations whose effect is integrity-critical (Wallet items, SSH CA trust and host certificates) therefore also carry an Ed25519 authority signature over the whole envelope, made with a key the node pinned at enrollment and echoes in its recipient statement. Each operation declares its own plaintext limit and whether that signature is required; see LookupOperation.

Index

Constants

View Source
const (
	EnvelopeAPIVersion  = "stackkit.sealed-host-delivery/v1"
	StatementAPIVersion = "stackkit.host-recipient-statement/v1"

	// OperationBackupTargetImport delivers the owner S3 target material that
	// `stackkit backup target import` writes into encrypted local custody.
	OperationBackupTargetImport = "backup-target-import"
	// OperationBackupTargetRebind renews Plan authority over identical custody
	// when a node-issued binding has expired.
	OperationBackupTargetRebind = "backup-target-rebind"
	// OperationWalletItemImport delivers one Wallet item version (for example a
	// stack secret input) into the node's encrypted local custody. The
	// plaintext is the item value itself; DeliveryRef names the item and
	// ItemVersion its immutable version.
	OperationWalletItemImport = "wallet-item-import"
	// OperationWalletItemRotate delivers a newer version of an item already in
	// custody; the node removes every older version once it holds the new one.
	OperationWalletItemRotate = "wallet-item-rotate"
	// OperationWalletItemRemove tells the node to drop every version of the
	// item DeliveryRef names from its local custody (WalletItemRemove). It
	// carries no value; the Wallet sends it when the owner takes the item off
	// the server or the server is withdrawn.
	OperationWalletItemRemove = "wallet-item-remove"
	// OperationSSHCATrust delivers the owner's SSH user CA public keys and the
	// principals each local user accepts (SSHCATrust). An empty key list
	// revokes the trust like OperationSSHCARevoke.
	OperationSSHCATrust = "ssh-ca-trust"
	// OperationSSHCARevoke removes the owner's SSH CA trust (SSHCARevoke).
	OperationSSHCARevoke = "ssh-ca-revoke"
	// OperationSSHHostCertificate delivers an SSH host certificate for one of
	// this server's host keys (SSHHostCertificate).
	OperationSSHHostCertificate = "ssh-host-certificate"

	// MaxLifetime bounds how long one sealed delivery stays openable.
	MaxLifetime = 15 * time.Minute
	// MaxPlaintextBytes bounds the sealed material of the backup-target and SSH
	// operations.
	MaxPlaintextBytes = 16 << 10
	// MaxWalletItemPlaintextBytes bounds one delivered Wallet item value.
	MaxWalletItemPlaintextBytes = 64 << 10
	// MaxEnvelopeBytes bounds one encoded envelope, the largest operation's
	// sealed material and its context included.
	MaxEnvelopeBytes = 128 << 10
)

Variables

This section is empty.

Functions

func Fingerprint

func Fingerprint(public []byte) string

Fingerprint names a recipient public key.

func NodeIdentityRef added in v0.53.19

func NodeIdentityRef(nodeRef string, ownerPublic []byte) string

NodeIdentityRef is the node reference a server reports for host delivery. A Site/node pair names a place in one StackSpec (every basement kit is home/main), so the pair alone does not tell one owner's servers apart. The suffix comes from the server's local Owner public key, which each installation generates once: unique per server, stable for its lifetime.

func Operations added in v0.53.1

func Operations() []string

Operations lists every known operation in a stable order.

func Seal

func Seal(recipientPublic []byte, context Context, plaintext []byte, now time.Time) ([]byte, error)

Seal encrypts plaintext to a recipient public key under the exact context. The plaintext must fit the operation's limit. An AuthoritySigned operation additionally needs SignEnvelope before it is delivered.

func SignEnvelope added in v0.53.1

func SignEnvelope(raw []byte, authority ed25519.PrivateKey, keyID string) ([]byte, error)

SignEnvelope adds the delivering authority's signature to a sealed envelope.

func StatementDigest

func StatementDigest(payload []byte) []byte

StatementDigest is the digest the owner key signs for a statement payload.

func VerifyStatement

func VerifyStatement(statement Statement, pinnedOwnerPublic ed25519.PublicKey, challenge string, now time.Time, maxAge time.Duration) error

VerifyStatement checks the owner signature, key binding and freshness of a statement against the owner public key the caller pinned at enrollment. It proves the recipient key belongs to the holder of that owner key; it does not establish that the pinned owner key is genuine.

func VerifyStatementAuthority added in v0.53.1

func VerifyStatementAuthority(statement Statement, authority ed25519.PublicKey, keyID string) error

VerifyStatementAuthority checks that a statement, already verified with VerifyStatement, names exactly the authority key the caller signs with. An authority seals AuthoritySigned operations only to a node that pinned it.

Types

type AuthoritySignature added in v0.53.1

type AuthoritySignature struct {
	KeyID string `json:"keyId"`
	Value string `json:"value"`
}

AuthoritySignature is an Ed25519 signature by the delivering authority over the envelope without this field.

type Context

type Context struct {
	TenantRef            string `json:"tenantRef"`
	DeliveryRef          string `json:"deliveryRef"`
	ItemVersion          uint64 `json:"itemVersion"`
	SiteRef              string `json:"siteRef"`
	NodeRef              string `json:"nodeRef"`
	BindingGeneration    uint64 `json:"bindingGeneration"`
	Operation            string `json:"operation"`
	RecipientFingerprint string `json:"recipientFingerprint"`
	OperationID          string `json:"operationId"`
	IssuedAt             string `json:"issuedAt"`
	NotAfter             string `json:"notAfter"`
}

Context is the exact scope a delivery is sealed for. Every field is bound into the ciphertext; none is secret.

func Open

func Open(recipientPrivate []byte, raw []byte, now time.Time) (Context, []byte, error)

Open decrypts an envelope with the recipient private key. Callers must have checked the context, and for an AuthoritySigned operation the authority signature (Envelope.VerifyAuthority), against their own expectations first; Open re-binds the context cryptographically, so a changed field fails to decrypt.

type Envelope

type Envelope struct {
	APIVersion string  `json:"apiVersion"`
	Context    Context `json:"context"`
	Sealed     []byte  `json:"sealed"`
	// Authority is the delivering authority's signature over the envelope. It
	// is required for operations whose spec is AuthoritySigned.
	Authority *AuthoritySignature `json:"authority,omitempty"`
}

Envelope is the only thing that crosses the control plane and transport.

func ParseEnvelope added in v0.53.1

func ParseEnvelope(raw []byte) (Envelope, error)

ParseEnvelope decodes one envelope strictly and checks its bounds. It does not open the envelope or judge its context.

func (Envelope) VerifyAuthority added in v0.53.1

func (e Envelope) VerifyAuthority(pinned ed25519.PublicKey, keyID string) error

VerifyAuthority checks the envelope's authority signature against the authority key the node pinned.

type OperationSpec added in v0.53.1

type OperationSpec struct {
	// MaxPlaintextBytes bounds the sealed material of this operation.
	MaxPlaintextBytes int
	// AuthoritySigned requires an envelope signature by the delivering
	// authority key the node pinned for its recipient generation.
	AuthoritySigned bool
	// Secret marks material the node must treat as a delivered secret: it is
	// listed for rotation when the node is detached.
	Secret bool
}

OperationSpec is what the contract fixes for one operation.

func LookupOperation added in v0.53.1

func LookupOperation(name string) (OperationSpec, bool)

LookupOperation returns the contract of a known operation. An unknown operation is neither sealed nor opened.

type SSHCARevoke added in v0.53.1

type SSHCARevoke struct{}

SSHCARevoke is the plaintext of OperationSSHCARevoke, encoded as {}.

type SSHCATrust added in v0.53.1

type SSHCATrust struct {
	CAKeys     []string            `json:"caKeys"`
	Principals map[string][]string `json:"principals"`
}

SSHCATrust is the plaintext of OperationSSHCATrust. CAKeys are OpenSSH authorized-key lines of the owner's user CA (several during a CA rotation); Principals maps each local user to the certificate principals it accepts.

type SSHHostCertificate added in v0.53.1

type SSHHostCertificate struct {
	Certificate string `json:"certificate"`
}

SSHHostCertificate is the plaintext of OperationSSHHostCertificate: one OpenSSH host certificate line for a key this server already holds.

type Signature

type Signature struct {
	OwnerRef string `json:"ownerRef"`
	KeyID    string `json:"keyId"`
	Value    string `json:"value"`
}

Signature is the node owner's signature over a recipient statement.

type Statement

type Statement struct {
	APIVersion   string `json:"apiVersion"`
	SiteRef      string `json:"siteRef"`
	NodeRef      string `json:"nodeRef"`
	RecipientKey string `json:"recipientKey"`
	Fingerprint  string `json:"fingerprint"`
	Generation   uint64 `json:"generation"`
	Challenge    string `json:"challenge"`
	IssuedAt     string `json:"issuedAt"`
	OwnerKeyID   string `json:"ownerKeyId"`
	OwnerPublic  string `json:"ownerPublicKey"`
	// AuthorityKeyID and AuthorityFingerprint name the delivering authority
	// key the node pinned for this generation. They are empty until one is
	// pinned, so a statement without them is byte-identical to a v1 statement
	// from before authority signatures existed.
	AuthorityKeyID       string    `json:"authorityKeyId,omitempty"`
	AuthorityFingerprint string    `json:"authorityFingerprint,omitempty"`
	Signature            Signature `json:"signature"`
}

Statement is the node's proof that a recipient key belongs to it. The Challenge makes it fresh: the delivering authority supplies it and rejects a statement that does not echo it.

func (Statement) SigningBytes

func (s Statement) SigningBytes() ([]byte, error)

SigningBytes is the canonical payload the owner signs, without the signature.

type WalletItemRemove added in v0.53.9

type WalletItemRemove struct{}

WalletItemRemove is the plaintext of OperationWalletItemRemove, encoded as {}.

Jump to

Keyboard shortcuts

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