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

apply

package
v0.53.1 Latest Latest
Warning

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

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

Documentation

Overview

Package apply orchestrates post-deployment bootstrap of identity resources.

The single entry point Run wires together the building blocks created by Tasks 5–10 (PocketID client, owner provisioner, break-glass generator, TinyAuth static-cred generator, recovery-bundle builder) into the post-terraform sequence the CLI calls once the homelab containers are healthy.

Idempotency: PocketID's STATIC_API_KEY-based bootstrap is itself a no-op when the static admin user already exists, so re-running the orchestrator against an already-provisioned PocketID surfaces ErrAlreadyBootstrapped (treated as success). Owner / break-glass / TinyAuth-static creation are not idempotent at the API level — re-running on a node that already has an owner will error in CreateUser. Phase 1 keeps that strict so a partial bootstrap is loud rather than silently double-provisioning. Per-step error wrapping makes it possible to identify exactly where a re-run would need to resume.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type OwnerBootstrapInput

type OwnerBootstrapInput struct {
	// NodeName is the homelab firstnode identifier (used for synthetic
	// break-glass usernames and bundle filenames). Required.
	NodeName string

	// Hostname is the node's network hostname. Documentation-only field
	// embedded into the bundle so the recovery operator can match the
	// artifact to physical hardware. Defaults to NodeName when empty.
	Hostname string

	// PocketIDURL is the public origin of the PocketID instance, e.g.
	// "https://id.example.com". No trailing slash. Required.
	PocketIDURL string

	// PocketIDStaticAPIKey is the value rendered into the PocketID
	// container as STATIC_API_KEY (Task 12 will provision this via tfvars;
	// for now Run reads it from the caller). Required.
	PocketIDStaticAPIKey string

	// Owner is the daily-admin owner spec. Required.
	Owner identity.OwnerSpec

	// RecoveryPassphraseHash is the argon2id-PHC hash the operator chose
	// during init. Required. Run uses it to verify the operator types the
	// matching plaintext at bundle-encryption time (third factor).
	RecoveryPassphraseHash string

	// RecoveryPassphrasePlain is the plaintext passphrase, optional. When
	// set, Run skips the terminal prompt and verifies the value against
	// the hash directly. Empty value triggers the prompt.
	RecoveryPassphrasePlain string

	// BundleDir is where the .age and .txt bundle files get written.
	// Defaults to defaultBundleDir when empty.
	BundleDir string

	// TinyAuthEnvPath is the file the TinyAuth users env line is written
	// to. Defaults to defaultTinyAuthEnvPath when empty.
	TinyAuthEnvPath string

	// ClusterRole is mirrored into the bundle so the recovery operator
	// knows which role this node played. Defaults to "main" when empty.
	ClusterRole string

	// PocketIDClient is the optional injection point for tests. When nil,
	// Run constructs a real *pocketid.Client from PocketIDURL +
	// PocketIDStaticAPIKey.
	PocketIDClient pocketIDClientForBootstrap

	// TerminalPrompter overrides the third-factor passphrase prompt.
	// Production sets this to nil (uses ttyPassphrasePrompter); tests inject
	// a fake.
	TerminalPrompter PassphrasePrompter

	// Now is injectable for deterministic tests. Defaults to time.Now in
	// the bundle builder.
	Now func() time.Time
}

OwnerBootstrapInput is everything Run needs to provision the owner and break-glass artifacts. Validation rules: NodeName, PocketIDURL, PocketIDStaticAPIKey, and RecoveryPassphraseHash are required. Owner is validated by the underlying provisioner.

type OwnerBootstrapResult

type OwnerBootstrapResult struct {
	// OwnerUserID is the PocketID-assigned UUID for the owner record.
	OwnerUserID string

	// OwnerSetupURL is the one-time-access link the owner clicks to enroll
	// a WebAuthn credential. Single-use; expires per identity.setupTokenTTL.
	OwnerSetupURL string

	// BreakGlass is the materialized Layer-1 recovery credential.
	BreakGlass *identity.BreakGlassCredential

	// TinyAuthStatic is the materialized Layer-2 recovery credential.
	TinyAuthStatic *identity.TinyAuthStaticCredential

	// BundlePaths are the on-disk locations of the encrypted (.age) and
	// plaintext (.txt) recovery files.
	BundlePaths *identity.BundlePaths
}

OwnerBootstrapResult is what Run returns on success — the persisted IDs the controller wants to push back to TechStack later (Task 13+) plus the on-disk paths the CLI prints to the operator.

func Run

Run executes the full owner-and-break-glass bootstrap sequence:

  1. Wait for PocketID health (HTTP /healthz returns 2xx).
  2. BootstrapInitialAdmin — verifies STATIC_API_KEY is accepted. Treated as success when the instance is already bootstrapped.
  3. Ensure the "owners" and "admins" user-groups exist.
  4. Provision the daily-admin owner (CreateUser + group + setup URL).
  5. Generate the per-node break-glass admin (CreateUser + group + token).
  6. Generate the per-node TinyAuth static credential (random pwd + bcrypt).
  7. Write TINYAUTH_AUTH_USERS=username:bcrypt-hash into TinyAuthEnvPath.
  8. Confirm the recovery passphrase at the terminal (third factor).
  9. Build the recovery bundle and write the .age + .txt files.

The function is deliberately verbose with error wrapping — each step's error names which operation failed, so a partial-completion state is diagnosable from a single log line.

type PassphrasePrompter

type PassphrasePrompter interface {
	// PromptAndVerify reads the recovery passphrase from the operator,
	// compares it against expectedHash via crypto.VerifyPassphrase, and
	// returns the raw plaintext on success. It must give the operator at
	// most attempts tries before erroring out — empty plaintext on success
	// is not allowed.
	PromptAndVerify(expectedHash string, attempts int) (plaintext string, err error)
}

PassphrasePrompter gates the third-factor passphrase confirmation. Production uses ttyPassphrasePrompter (reads /dev/tty with echo off); tests inject a fake.

Jump to

Keyboard shortcuts

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