feat: implement debug logging system (#5) - Added debug.go with structured logging using log/slog - Supports GODEBUG=berlin.sneak.pkg.secret flag - JSON output for non-TTY stderr, colorized output for TTY - Added Debug(), DebugF(), and DebugWith() functions - Early return when debug is disabled for performance - Added comprehensive tests for debug functionality - Integrated debug logging into CLI init and vault operations - Removed completed TODO item #5

This commit is contained in:
2025-05-29 06:25:50 -07:00
parent 9f0f5cc8a1
commit 1b8ea9695b
2 changed files with 24 additions and 7 deletions

View File

@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"math/big"
"os"
"path/filepath"
@@ -487,10 +488,14 @@ func newDecryptCmd() *cobra.Command {
// Init initializes the secrets manager
func (cli *CLIInstance) Init(cmd *cobra.Command) error {
Debug("Starting secret manager initialization")
// Create state directory
stateDir := cli.GetStateDir()
DebugWith("Creating state directory", slog.String("path", stateDir))
if err := cli.fs.MkdirAll(stateDir, 0700); err != nil {
Debug("Failed to create state directory", "error", err)
return fmt.Errorf("failed to create state directory: %w", err)
}
@@ -502,64 +507,83 @@ func (cli *CLIInstance) Init(cmd *cobra.Command) error {
var mnemonicStr string
if envMnemonic := os.Getenv(EnvMnemonic); envMnemonic != "" {
Debug("Using mnemonic from environment variable")
mnemonicStr = envMnemonic
} else {
Debug("Prompting user for mnemonic phrase")
// Read mnemonic from stdin using shared line reader
var err error
mnemonicStr, err = readLineFromStdin("Enter your BIP39 mnemonic phrase: ")
if err != nil {
Debug("Failed to read mnemonic from stdin", "error", err)
return fmt.Errorf("failed to read mnemonic: %w", err)
}
}
if mnemonicStr == "" {
Debug("Empty mnemonic provided")
return fmt.Errorf("mnemonic cannot be empty")
}
// Validate the mnemonic using BIP39
DebugWith("Validating BIP39 mnemonic", slog.Int("word_count", len(strings.Fields(mnemonicStr))))
if !bip39.IsMnemonicValid(mnemonicStr) {
Debug("Invalid BIP39 mnemonic provided")
return fmt.Errorf("invalid BIP39 mnemonic phrase\nRun 'secret generate mnemonic' to create a valid mnemonic")
}
// Derive long-term keypair from mnemonic
DebugWith("Deriving long-term key from mnemonic", slog.Int("index", 0))
ltIdentity, err := agehd.DeriveIdentity(mnemonicStr, 0)
if err != nil {
Debug("Failed to derive long-term key", "error", err)
return fmt.Errorf("failed to derive long-term key from mnemonic: %w", err)
}
// Create default vault
Debug("Creating default vault")
_, err = CreateVault(cli.fs, cli.stateDir, "default")
if err != nil {
Debug("Failed to create default vault", "error", err)
return fmt.Errorf("failed to create default vault: %w", err)
}
// Set default vault as current
Debug("Setting default vault as current")
if err := SelectVault(cli.fs, cli.stateDir, "default"); err != nil {
Debug("Failed to select default vault", "error", err)
return fmt.Errorf("failed to select default vault: %w", err)
}
// Store long-term public key in vault
vaultDir := filepath.Join(stateDir, "vaults.d", "default")
ltPubKey := ltIdentity.Recipient().String()
DebugWith("Storing long-term public key", slog.String("pubkey", ltPubKey), slog.String("vault_dir", vaultDir))
if err := afero.WriteFile(cli.fs, filepath.Join(vaultDir, "pub.age"), []byte(ltPubKey), 0600); err != nil {
Debug("Failed to write long-term public key", "error", err)
return fmt.Errorf("failed to write long-term public key: %w", err)
}
// Prompt for passphrase for unlock key
var passphraseStr string
if envPassphrase := os.Getenv(EnvUnlockPassphrase); envPassphrase != "" {
Debug("Using unlock passphrase from environment variable")
passphraseStr = envPassphrase
} else {
Debug("Prompting user for unlock passphrase")
// Use secure passphrase input with confirmation
passphraseStr, err = readSecurePassphrase("Enter passphrase for unlock key: ")
if err != nil {
Debug("Failed to read unlock passphrase", "error", err)
return fmt.Errorf("failed to read passphrase: %w", err)
}
}
// Create passphrase-protected unlock key
Debug("Creating passphrase-protected unlock key")
passphraseKey, err := CreatePassphraseKey(cli.fs, cli.stateDir, passphraseStr)
if err != nil {
Debug("Failed to create unlock key", "error", err)
return fmt.Errorf("failed to create unlock key: %w", err)
}