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 ¶
- func Challenge(verifier string) string
- func HashToken(token string) string
- func NewSealKey() (*ecdh.PrivateKey, error)
- func OpenSealed(priv *ecdh.PrivateKey, sealed string) (string, error)
- func SealPublicKey(priv *ecdh.PrivateKey) string
- func Unseal(priv *ecdh.PrivateKey, v string) (string, error)
- type PG
- func (p *PG) CreateSession(id, challenge string, meta SessionMeta) error
- func (p *PG) FetchSession(id, verifier string) (Session, SessionStatus, error)
- func (p *PG) Priorities(ctx context.Context, scopes []string) (map[string]int, error)
- func (p *PG) ValidateToken(token string) ([]string, string, bool)
- func (p *PG) Webhook(ctx context.Context, id string) (secret, scope, name string, err error)
- type Session
- type SessionMeta
- type SessionStatus
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Challenge ¶
Challenge is the PKCE challenge for a verifier: hex sha256(verifier). The CLI sends this; FetchSession recomputes it from the presented verifier.
func HashToken ¶
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.
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 ¶
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
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 ¶
ValidateToken looks up an active enrollment token by its hash. A DB error fails closed (ok=false) so a blip rejects rather than admits.
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.