enroll

package
v0.2.13 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package enroll handles agent authentication for the hosted control plane: per-agent enrollment tokens (validated against Neon) and the browser-session + PKCE handshake behind `ushr login`. See DESIGN-enrollment.md.

OSS single-host does not use this — it keeps the static shared token (or loopback with no token). Enrollment is the hosted, multi-tenant path.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Challenge

func Challenge(verifier string) string

Challenge is the PKCE challenge for a verifier: hex sha256(verifier). The CLI sends this; FetchSession recomputes it from the presented verifier.

func HashToken

func HashToken(token string) string

HashToken is the at-rest form of an enrollment token: hex sha256. Both this control plane and the hosted web app minter must hash identically.

func NewSealKey added in v0.2.11

func NewSealKey() (*ecdh.PrivateKey, error)

NewSealKey returns an ephemeral X25519 key pair for one login or setup flow. The CLI sends the public half so the web app stores secrets sealed to it.

func OpenSealed added in v0.2.11

func OpenSealed(priv *ecdh.PrivateKey, sealed string) (string, error)

OpenSealed decrypts "s1." + base64url(ephemeral public key || AES-256-GCM ciphertext). HKDF-SHA256 over the X25519 shared secret, salted with both public keys, yields the 32-byte key and 12-byte nonce; each seal uses a fresh ephemeral key.

func SealPublicKey added in v0.2.11

func SealPublicKey(priv *ecdh.PrivateKey) string

SealPublicKey is the wire form of a seal key's public half: base64url, no padding.

func Unseal added in v0.2.11

func Unseal(priv *ecdh.PrivateKey, v string) (string, error)

Unseal opens a sealed value and returns any other value as is. Plaintext comes only from a server without sealing, over the same TLS channel.

Types

type PG

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

PG is the Neon-backed enroll.Store. The hosted web app web app writes agent_tokens (mint) and approves cli_sessions; this reads/validates and serves the CLI poll. It shares the control plane's pgxpool (see NewWithPool).

func NewWithPool

func NewWithPool(ctx context.Context, pool *pgxpool.Pool) (*PG, error)

NewWithPool applies any unapplied enrollment migrations on an existing pool and returns a store sharing it (the control plane's dispatch store owns the pool's lifetime). An advisory lock serializes concurrent boots.

func (*PG) CreateSession

func (p *PG) CreateSession(id, challenge string, meta SessionMeta) error

CreateSession records a pending login session and prunes expired ones.

func (*PG) FetchSession

func (p *PG) FetchSession(id, verifier string) (Session, SessionStatus, error)

FetchSession consumes an approved session and returns its token. The delete and read are one statement, so a session is single-use: two racing fetches can't both receive the token. A wrong verifier fails the challenge match and reads as expired — no oracle for pending vs unknown.

func (*PG) Priorities added in v0.2.11

func (p *PG) Priorities(ctx context.Context, scopes []string) (map[string]int, error)

Priorities returns the policy priority for each scope that has one, keyed by the lower-cased scope. A row counts only while its workspace still owns the scope in org_installations, so one workspace cannot set another's priority.

func (*PG) ValidateToken

func (p *PG) ValidateToken(token string) ([]string, string, bool)

ValidateToken looks up an active enrollment token by its hash. A DB error fails closed (ok=false) so a blip rejects rather than admits.

func (*PG) Webhook added in v0.2.9

func (p *PG) Webhook(ctx context.Context, id string) (secret, scope, name string, err error)

Webhook follows the host's live token, so rotation keeps the connection and revocation ends it. An unknown or disconnected id returns an empty secret.

type Session

type Session struct {
	// Token is sealed to SessionMeta.PublicKey when the CLI sent one.
	Token string
	Orgs  []string
	Name  string
}

Session is the approved result the CLI writes into its config.

type SessionMeta

type SessionMeta struct {
	// AgentName is the enrolling host's own declared name (its agent config
	// name or hostname). The approval page displays it read-only.
	AgentName string
	// RequesterIP is the CLI's egress address as seen by the control plane.
	RequesterIP string
	// UserCode is the short confirmation code the CLI prints; the approver
	// checks it matches the page before approving (RFC 8628-style binding).
	UserCode string
	// PublicKey is the CLI's base64url X25519 key for this login. The web app
	// seals the token to it. Empty from CLIs v0.2.10 and older.
	PublicKey string
}

SessionMeta is requester context captured at session creation and shown on the browser approval page, so the approver can recognize (or reject) the machine asking to enroll — the defense against device-flow phishing.

type SessionStatus

type SessionStatus int

SessionStatus is the state of a CLI login session when the CLI polls for it.

const (
	// Pending: the browser side hasn't approved yet.
	Pending SessionStatus = iota
	// Ready: approved, token available.
	Ready
	// Expired: unknown or past its TTL.
	Expired
)

type Store

type Store interface {
	// ValidateToken reports the orgs an enrollment token may serve and the agent
	// name it is bound to. A token covers the set of orgs the enrolling account
	// selected (all verified by that account), so one agent can serve several.
	// ok is false for unknown or revoked tokens; errors are treated as not-ok.
	ValidateToken(token string) (orgs []string, name string, ok bool)
	// CreateSession records a pending login session (idempotent on id).
	CreateSession(id, challenge string, meta SessionMeta) error
	// FetchSession returns the approved Session iff verifier matches the stored
	// challenge, consuming it. Pending/expired sessions return no token.
	FetchSession(id, verifier string) (Session, SessionStatus, error)
}

Store authenticates enrollment tokens and drives the CLI login handshake. The web app (hosted web app) writes agent_tokens and approves cli_sessions; the control plane only reads/validates here.

Jump to

Keyboard shortcuts

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