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:
parent
9f0f5cc8a1
commit
1b8ea9695b
7
TODO.md
7
TODO.md
@ -15,13 +15,6 @@ This document outlines the bugs, issues, and improvements that need to be addres
|
||||
|
||||
- [ ] **4. No graceful handling of corrupted state**: If key files are corrupted or missing, the tool should provide clear error messages and recovery suggestions.
|
||||
|
||||
- [ ] **5.** When GODEBUG contains 'berlin.sneak.pkg.secret', output structured
|
||||
debug data to STDERR. use log/slog. if stderr is not a tty, output jsonl. if
|
||||
it is a tty, output colorized structured log data in a format similar to
|
||||
printf's %#v format. create a debug logging function that calls a helper
|
||||
function to see if the debug logging is enabled, and returns immediately if it
|
||||
is not.
|
||||
|
||||
### Core Functionality Bugs
|
||||
|
||||
- [ ] **5. Multiple vaults using the same mnemonic will derive the same long-term keys**: Adding additional vaults with the same mnemonic should increment the index value used. The mnemonic should be double sha256 hashed and the hash value stored in the vault metadata along with the index value (starting at zero) and when additional vaults are added with the same mnemonic (as determined by hash) then the index value should be incremented. The README should be updated to document this behavior.
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user