apply

package
v0.43.0 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 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