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:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user