bootstrap

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsDBEncrypted

func IsDBEncrypted(dbPath string) bool

IsDBEncrypted checks whether a SQLite database file is encrypted. An encrypted DB will not have the standard "SQLite format 3" magic header.

func KMSConfigFromEnv added in v0.7.0

func KMSConfigFromEnv() (*config.KMSConfig, string)

KMSConfigFromEnv reads KMS KEK configuration from environment variables. Returns nil config and empty provider name if LANGO_KMS_PROVIDER is not set.

Provider-specific env vars:

aws-kms/gcp-kms: LANGO_KMS_KEY_ID, LANGO_KMS_REGION, LANGO_KMS_ENDPOINT
azure-kv:        + LANGO_KMS_AZURE_VAULT_URL, LANGO_KMS_AZURE_KEY_VERSION
pkcs11:          LANGO_KMS_PKCS11_MODULE, LANGO_KMS_PKCS11_SLOT_ID,
                 LANGO_KMS_PKCS11_KEY_LABEL, LANGO_PKCS11_PIN

func OpenDatabaseReadOnly added in v0.7.0

func OpenDatabaseReadOnly(dbPath, encryptionKey string, rawKey bool, cipherPageSize int) (*ent.Client, *sql.DB, error)

OpenDatabaseReadOnly opens the SQLite/SQLCipher database in read-only mode without invoking ent schema migration.

Contract:

  • Read-only: the connection uses `file:path?mode=ro`, so writes fail at the SQLite layer.
  • No schema migration: `client.Schema.Create` is not called, so this function can be used by commands that must not modify the DB.
  • No prompt: the caller is responsible for obtaining the encryption key non-interactively. Failure to open returns an error; callers must gracefully degrade (zero counts, "unavailable" fields).

rawKey semantics match openDatabase: rawKey=true uses `PRAGMA key = "x'<hex>'"`, rawKey=false uses `PRAGMA key = '<passphrase>'`, and an empty encryptionKey skips the PRAGMA entirely (plaintext).

The returned *ent.Client shares the underlying *sql.DB so callers should Close() the client (which closes the DB) when done.

Types

type Options

type Options struct {
	// LangoDir is the lango data directory (default: ~/.lango/).
	// When set, envelope.json, keyfile (if default), and skills/ are placed under this path.
	// Primarily used by tests to isolate state in t.TempDir().
	LangoDir string
	// DBPath is the SQLite database path (default: <LangoDir>/lango.db).
	DBPath string
	// KeyfilePath is the path to the passphrase keyfile (default: <LangoDir>/keyfile).
	KeyfilePath string
	// ForceProfile overrides the active profile selection.
	ForceProfile string
	// KeepKeyfile prevents the keyfile from being shredded after crypto initialization.
	// Default (false) shreds the keyfile for security.
	KeepKeyfile bool
	// DBEncryption configures SQLCipher transparent database encryption.
	DBEncryption config.DBEncryptionConfig
	// SkipSecureDetection disables secure hardware provider detection (biometric/TPM).
	// When true, the bootstrap falls back to keyfile or interactive prompt only.
	// Useful for testing and headless environments.
	SkipSecureDetection bool
	// KMSConfig provides KMS settings for KMS KEK slot unwrapping during bootstrap.
	// When set and the envelope has a hardware slot, bootstrap attempts KMS-based
	// MK unwrap before falling back to passphrase.
	KMSConfig *config.KMSConfig
	// KMSProviderName identifies which KMS backend to use for KEK unwrap (e.g., "aws-kms").
	KMSProviderName string
}

Options configures the bootstrap process.

type Phase

type Phase struct {
	Name    string
	Run     func(ctx context.Context, state *State) error
	Cleanup func(state *State) // called in reverse order if a later phase fails
}

Phase represents a single step in the bootstrap pipeline.

func DefaultPhases

func DefaultPhases() []Phase

DefaultPhases returns the standard bootstrap phase sequence (11 phases).

Envelope-aware order: envelope loads BEFORE DB open so recovery credentials (mnemonic) and MK-derived DB keys are available when we actually open SQLCipher. Legacy installations follow the same pipeline but land in MigrateEnvelope which performs a one-time upgrade.

type PhaseTimingEntry added in v0.7.0

type PhaseTimingEntry struct {
	Phase    string        `json:"phase"`
	Duration time.Duration `json:"duration"`
}

PhaseTimingEntry records the duration of a bootstrap phase.

type Pipeline

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

Pipeline executes phases sequentially. If a phase fails, cleanup functions of all previously completed phases are called in reverse order.

func NewPipeline

func NewPipeline(phases ...Phase) *Pipeline

NewPipeline creates a pipeline from the given phases.

func (*Pipeline) Execute

func (p *Pipeline) Execute(ctx context.Context, opts Options) (*Result, error)

Execute runs all phases. On failure, cleans up in reverse order.

type Result

type Result struct {
	// Config is the decrypted, active configuration.
	Config *config.Config
	// ExplicitKeys tracks which context-related config keys the user explicitly set.
	// nil for legacy profiles or when explicit key tracking is unavailable.
	ExplicitKeys map[string]bool
	// AutoEnabled records which context subsystems were auto-enabled during config resolution.
	AutoEnabled config.AutoEnabledSet
	// DBClient is the shared ent.Client for the application database.
	DBClient *ent.Client
	// RawDB is the underlying *sql.DB for direct SQL operations (e.g., sqlite-vec).
	RawDB *sql.DB
	// Crypto is the initialized CryptoProvider for the session.
	Crypto security.CryptoProvider
	// ConfigStore provides encrypted profile CRUD operations.
	ConfigStore *configstore.Store
	// ProfileName is the name of the loaded profile.
	ProfileName string
	// LangoDir is the resolved data directory (Options.LangoDir or ~/.lango).
	// Downstream components (CLI, status, change-passphrase) use this to locate
	// the envelope file without reaching into bootstrap internals.
	LangoDir string
	// IdentityKey is the Ed25519 identity key derived from the Master Key (Phase 3).
	// nil when MK is unavailable (legacy mode).
	IdentityKey ed25519.PrivateKey
	// PQSigningKeySeed is the 32-byte HKDF seed for ML-DSA-65 PQ signing (Phase 5).
	// nil when MK is unavailable. Downstream calls mldsa65.NewKeyFromSeed to derive the key.
	PQSigningKeySeed []byte
	// KMSUnwrap indicates the MK was unwrapped via a KMS KEK slot (not passphrase/mnemonic).
	KMSUnwrap bool
	// PhaseTiming records the duration of each bootstrap phase.
	PhaseTiming []PhaseTimingEntry `json:"phaseTiming,omitempty"`
}

Result holds everything produced by the bootstrap process.

func Run

func Run(opts Options) (*Result, error)

Run executes the full bootstrap sequence using the phase pipeline:

  1. Ensure ~/.lango/ directory
  2. Detect DB encryption status
  3. Acquire passphrase
  4. Open SQLite/SQLCipher DB + ent schema migration
  5. Load security state (salt/checksum)
  6. Initialize crypto provider
  7. Load or create configuration profile

type State

type State struct {
	Options Options
	Result  Result

	// Internal state passed between phases.
	Home     string
	LangoDir string

	// Encryption detection.
	DBEncrypted bool
	NeedsDBKey  bool

	// Passphrase acquisition.
	Passphrase     string
	PassSource     passphrase.Source
	SecureProvider keyring.Provider
	SecurityTier   keyring.SecurityTier
	FirstRunGuess  bool

	// Database handles (set by phaseOpenDatabase).
	Client *ent.Client
	RawDB  *sql.DB

	// Security state from DB (legacy path).
	Salt     []byte
	Checksum []byte
	FirstRun bool

	// Crypto.
	DBKey  string
	Crypto security.CryptoProvider

	// Envelope-based crypto state.
	Envelope     *security.MasterKeyEnvelope
	MasterKey    []byte // unwrapped MK (zeroed on cleanup)
	LegacyMode   bool   // no envelope, not first run — needs legacy→envelope migration
	RecoveryMode bool   // reserved for future use; no longer set during bootstrap

	// Identity key derived from MK via HKDF (Phase 3).
	IdentityKey ed25519.PrivateKey

	// PQ signing key seed derived from MK via HKDF (Phase 5).
	// 32-byte seed for ML-DSA-65. Downstream code calls mldsa65.NewKeyFromSeed.
	PQSigningKeySeed []byte

	// KMS KEK state (Phase 6).
	KMSProvider security.CryptoProvider // transient, for KMS KEK unwrap only
	KMSUnwrap   bool                    // MK was unwrapped via KMS (not passphrase)
}

State carries data between pipeline phases. Each phase can read from and write to State.

Jump to

Keyboard shortcuts

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