revert: removes --secrets - to be replaced with encryption

This commit is contained in:
Lewis Wynne 2026-02-10 23:22:06 +00:00
parent 34970ac9d9
commit 4509611185
27 changed files with 132 additions and 269 deletions

View file

@ -40,7 +40,6 @@ type dumpEntry struct {
Key string `json:"key"`
Value string `json:"value"`
Encoding string `json:"encoding,omitempty"`
Secret bool `json:"secret,omitempty"`
ExpiresAt *int64 `json:"expires_at,omitempty"`
}
@ -82,10 +81,6 @@ func dump(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot dump '%s': unsupported encoding '%s'", targetDB, mode)
}
includeSecret, err := cmd.Flags().GetBool("secret")
if err != nil {
return err
}
globPatterns, err := cmd.Flags().GetStringSlice("glob")
if err != nil {
return fmt.Errorf("cannot dump '%s': %v", targetDB, err)
@ -101,7 +96,6 @@ func dump(cmd *cobra.Command, args []string) error {
opts := DumpOptions{
Encoding: mode,
IncludeSecret: includeSecret,
Matchers: matchers,
GlobPatterns: globPatterns,
}
@ -110,7 +104,6 @@ func dump(cmd *cobra.Command, args []string) error {
func init() {
dumpCmd.Flags().StringP("encoding", "e", "auto", "value encoding: auto, base64, or text")
dumpCmd.Flags().Bool("secret", false, "Include entries marked as secret")
dumpCmd.Flags().StringSliceP("glob", "g", nil, "Filter keys with glob pattern (repeatable)")
dumpCmd.Flags().String("glob-sep", "", fmt.Sprintf("Characters treated as separators for globbing (default %q)", defaultGlobSeparatorsDisplay()))
rootCmd.AddCommand(dumpCmd)
@ -133,7 +126,6 @@ func encodeText(entry *dumpEntry, key []byte, v []byte) error {
// DumpOptions controls how a store is dumped to NDJSON.
type DumpOptions struct {
Encoding string
IncludeSecret bool
Matchers []glob.Glob
GlobPatterns []string
}
@ -159,16 +151,10 @@ func dumpDatabase(store *Store, dbName string, w io.Writer, opts DumpOptions) er
if !globMatch(opts.Matchers, string(key)) {
continue
}
meta := item.UserMeta()
isSecret := meta&metaSecret != 0
if isSecret && !opts.IncludeSecret {
continue
}
expiresAt := item.ExpiresAt()
if err := item.Value(func(v []byte) error {
entry := dumpEntry{
Key: string(key),
Secret: isSecret,
}
if expiresAt > 0 {
ts := int64(expiresAt)

View file

@ -74,7 +74,6 @@ func get(cmd *cobra.Command, args []string) error {
store := &Store{}
var v []byte
var meta byte
trans := TransactionArgs{
key: args[0],
readonly: true,
@ -84,7 +83,6 @@ func get(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
meta = item.UserMeta()
v, err = item.ValueCopy(nil)
return err
},
@ -94,14 +92,6 @@ func get(cmd *cobra.Command, args []string) error {
return fmt.Errorf("cannot get '%s': %v", args[0], err)
}
includeSecret, err := cmd.Flags().GetBool("secret")
if err != nil {
return fmt.Errorf("cannot get '%s': %v", args[0], err)
}
if meta&metaSecret != 0 && !includeSecret {
return fmt.Errorf("cannot get '%s': marked as secret, run with --secret", args[0])
}
binary, err := cmd.Flags().GetBool("include-binary")
if err != nil {
return fmt.Errorf("cannot get '%s': %v", args[0], err)
@ -238,13 +228,11 @@ var runFlag bool
func init() {
getCmd.Flags().BoolP("include-binary", "b", false, "include binary data in text output")
getCmd.Flags().Bool("secret", false, "display values marked as secret")
getCmd.Flags().BoolVarP(&runFlag, "run", "c", false, "execute the result as a shell command")
getCmd.Flags().Bool("no-template", false, "directly output template syntax")
rootCmd.AddCommand(getCmd)
runCmd.Flags().BoolP("include-binary", "b", false, "include binary data in text output")
runCmd.Flags().Bool("secret", false, "display values marked as secret")
runCmd.Flags().Bool("no-template", false, "directly output template syntax")
rootCmd.AddCommand(runCmd)
}

View file

@ -55,7 +55,6 @@ func (e *formatEnum) Type() string { return "format" }
var (
listBinary bool
listSecret bool
listNoKeys bool
listNoValues bool
listTTL bool
@ -142,7 +141,6 @@ func list(cmd *cobra.Command, args []string) error {
tw.AppendHeader(headerRow(columns))
}
placeholder := "**********"
var matchedCount int
trans := TransactionArgs{
key: targetDB,
@ -162,11 +160,9 @@ func list(cmd *cobra.Command, args []string) error {
continue
}
matchedCount++
meta := item.UserMeta()
isSecret := meta&metaSecret != 0
var valueStr string
if showValues && (!isSecret || listSecret) {
if showValues {
if err := item.Value(func(v []byte) error {
valueBuf = append(valueBuf[:0], v...)
return nil
@ -182,11 +178,7 @@ func list(cmd *cobra.Command, args []string) error {
case columnKey:
row = append(row, key)
case columnValue:
if isSecret && !listSecret {
row = append(row, placeholder)
} else {
row = append(row, valueStr)
}
case columnTTL:
row = append(row, formatExpiry(item.ExpiresAt()))
}
@ -310,7 +302,6 @@ func renderTable(tw table.Writer) {
func init() {
listCmd.Flags().BoolVarP(&listBinary, "binary", "b", false, "include binary data in text output")
listCmd.Flags().BoolVarP(&listSecret, "secret", "S", false, "display values marked as secret")
listCmd.Flags().BoolVar(&listNoKeys, "no-keys", false, "suppress the key column")
listCmd.Flags().BoolVar(&listNoValues, "no-values", false, "suppress the value column")
listCmd.Flags().BoolVarP(&listTTL, "ttl", "t", false, "append a TTL column when entries expire")

View file

@ -32,6 +32,7 @@ import (
"strings"
"github.com/dgraph-io/badger/v4"
"github.com/gobwas/glob"
"github.com/spf13/cobra"
)
@ -85,82 +86,21 @@ func restore(cmd *cobra.Command, args []string) error {
decoder := json.NewDecoder(bufio.NewReaderSize(reader, 8*1024*1024))
wb := db.NewWriteBatch()
defer wb.Cancel()
interactive, err := cmd.Flags().GetBool("interactive")
if err != nil {
return fmt.Errorf("cannot restore '%s': %v", displayTarget, err)
}
promptOverwrite := interactive || config.Key.AlwaysPromptOverwrite
entryNo := 0
var restored int
var matched bool
for {
var entry dumpEntry
if err := decoder.Decode(&entry); err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("cannot restore '%s': entry %d: %w", displayTarget, entryNo+1, err)
}
entryNo++
if entry.Key == "" {
return fmt.Errorf("cannot restore '%s': entry %d: missing key", displayTarget, entryNo)
}
if !globMatch(matchers, entry.Key) {
continue
}
if promptOverwrite {
exists, err := keyExistsInDB(db, entry.Key)
restored, err := restoreEntries(decoder, db, restoreOpts{
matchers: matchers,
promptOverwrite: promptOverwrite,
})
if err != nil {
return fmt.Errorf("cannot restore '%s': entry %d: %v", displayTarget, entryNo, err)
}
if exists {
fmt.Printf("overwrite '%s'? (y/n)\n", entry.Key)
var confirm string
if _, err := fmt.Scanln(&confirm); err != nil {
return fmt.Errorf("cannot restore '%s': entry %d: %v", displayTarget, entryNo, err)
}
if strings.ToLower(confirm) != "y" {
continue
}
}
}
value, err := decodeEntryValue(entry)
if err != nil {
return fmt.Errorf("cannot restore '%s': entry %d: %w", displayTarget, entryNo, err)
}
entryMeta := byte(0x0)
if entry.Secret {
entryMeta = metaSecret
}
writeEntry := badger.NewEntry([]byte(entry.Key), value).WithMeta(entryMeta)
if entry.ExpiresAt != nil {
if *entry.ExpiresAt < 0 {
return fmt.Errorf("cannot restore '%s': entry %d: expires_at must be >= 0", displayTarget, entryNo)
}
writeEntry.ExpiresAt = uint64(*entry.ExpiresAt)
}
if err := wb.SetEntry(writeEntry); err != nil {
return fmt.Errorf("cannot restore '%s': entry %d: %w", displayTarget, entryNo, err)
}
restored++
matched = true
}
if err := wb.Flush(); err != nil {
return fmt.Errorf("cannot restore '%s': %v", displayTarget, err)
}
if len(matchers) > 0 && !matched {
if len(matchers) > 0 && restored == 0 {
return fmt.Errorf("cannot restore '%s': No matches for pattern %s", displayTarget, formatGlobPatterns(globPatterns))
}
@ -198,6 +138,76 @@ func decodeEntryValue(entry dumpEntry) ([]byte, error) {
}
}
type restoreOpts struct {
matchers []glob.Glob
promptOverwrite bool
}
func restoreEntries(decoder *json.Decoder, db *badger.DB, opts restoreOpts) (int, error) {
wb := db.NewWriteBatch()
defer wb.Cancel()
entryNo := 0
restored := 0
for {
var entry dumpEntry
if err := decoder.Decode(&entry); err != nil {
if err == io.EOF {
break
}
return 0, fmt.Errorf("entry %d: %w", entryNo+1, err)
}
entryNo++
if entry.Key == "" {
return 0, fmt.Errorf("entry %d: missing key", entryNo)
}
if !globMatch(opts.matchers, entry.Key) {
continue
}
if opts.promptOverwrite {
exists, err := keyExistsInDB(db, entry.Key)
if err != nil {
return 0, fmt.Errorf("entry %d: %v", entryNo, err)
}
if exists {
fmt.Printf("overwrite '%s'? (y/n)\n", entry.Key)
var confirm string
if _, err := fmt.Scanln(&confirm); err != nil {
return 0, fmt.Errorf("entry %d: %v", entryNo, err)
}
if strings.ToLower(confirm) != "y" {
continue
}
}
}
value, err := decodeEntryValue(entry)
if err != nil {
return 0, fmt.Errorf("entry %d: %w", entryNo, err)
}
writeEntry := badger.NewEntry([]byte(entry.Key), value)
if entry.ExpiresAt != nil {
if *entry.ExpiresAt < 0 {
return 0, fmt.Errorf("entry %d: expires_at must be >= 0", entryNo)
}
writeEntry.ExpiresAt = uint64(*entry.ExpiresAt)
}
if err := wb.SetEntry(writeEntry); err != nil {
return 0, fmt.Errorf("entry %d: %w", entryNo, err)
}
restored++
}
if err := wb.Flush(); err != nil {
return 0, err
}
return restored, nil
}
func init() {
restoreCmd.Flags().StringP("file", "f", "", "Path to an NDJSON dump (defaults to stdin)")
restoreCmd.Flags().StringSliceP("glob", "g", nil, "Restore keys matching glob pattern (repeatable)")

View file

@ -76,10 +76,6 @@ func set(cmd *cobra.Command, args []string) error {
value = bytes
}
secret, err := cmd.Flags().GetBool("secret")
if err != nil {
return fmt.Errorf("cannot set '%s': %v", args[0], err)
}
ttl, err := cmd.Flags().GetDuration("ttl")
if err != nil {
return fmt.Errorf("cannot set '%s': %v", args[0], err)
@ -108,9 +104,6 @@ func set(cmd *cobra.Command, args []string) error {
sync: false,
transact: func(tx *badger.Txn, k []byte) error {
entry := badger.NewEntry(k, value)
if secret {
entry = entry.WithMeta(metaSecret)
}
if ttl != 0 {
entry = entry.WithTTL(ttl)
}
@ -127,7 +120,6 @@ func set(cmd *cobra.Command, args []string) error {
func init() {
rootCmd.AddCommand(setCmd)
setCmd.Flags().Bool("secret", false, "Mark the stored value as a secret")
setCmd.Flags().DurationP("ttl", "t", 0, "Expire the key after the provided duration (e.g. 24h, 30m)")
setCmd.Flags().BoolP("interactive", "i", false, "Prompt before overwriting an existing key")
}

View file

@ -41,10 +41,6 @@ type errNotFound struct {
suggestions []string
}
const (
metaSecret byte = 0x1
)
func (err errNotFound) Error() string {
if len(err.suggestions) == 0 {
return "No such key"

View file

@ -64,8 +64,8 @@ func sync(manual bool) error {
}
remoteAhead, behind, err := repoAheadBehind(repoDir, remoteInfo.Ref)
if err != nil {
return err
}
ahead = 1 // ref doesn't exist yet; just push
} else {
ahead = remoteAhead
if behind > 0 {
if ahead > 0 {
@ -95,6 +95,7 @@ func sync(manual bool) error {
return restoreAllSnapshots(store, repoDir)
}
}
}
if err := exportAllStores(store, repoDir); err != nil {
return err
@ -108,7 +109,9 @@ func sync(manual bool) error {
}
madeCommit := false
if !changed {
if manual {
fmt.Println("no changes to commit")
}
} else {
msg := fmt.Sprintf("sync: %s", time.Now().UTC().Format(time.RFC3339))
if err := runGit(repoDir, "commit", "-m", msg); err != nil {
@ -117,11 +120,16 @@ func sync(manual bool) error {
madeCommit = true
}
if manual || config.Git.AutoPush {
if remoteInfo.Ref != "" && (madeCommit || ahead > 0) {
return pushRemote(repoDir, remoteInfo)
}
if remoteInfo.Ref == "" {
if manual {
fmt.Println("no remote configured; skipping push")
}
} else if madeCommit || ahead > 0 {
return pushRemote(repoDir, remoteInfo)
} else if manual {
fmt.Println("nothing to push")
}
}
return nil
}

View file

@ -12,7 +12,6 @@ import (
"strconv"
"strings"
"github.com/dgraph-io/badger/v4"
gap "github.com/muesli/go-app-paths"
)
@ -63,7 +62,7 @@ func writeGitignore(repoDir string) error {
if err := runGit(repoDir, "add", ".gitignore"); err != nil {
return err
}
return runGit(repoDir, "commit", "--allow-empty", "-m", "generated gitignore")
return runGit(repoDir, "commit", "-m", "generated gitignore")
}
fmt.Println("Existing .gitignore found.")
return nil
@ -83,7 +82,6 @@ func snapshotDB(store *Store, repoDir, db string) error {
opts := DumpOptions{
Encoding: "auto",
IncludeSecret: false,
}
if err := dumpDatabase(store, db, f, opts); err != nil {
return err
@ -373,60 +371,7 @@ func restoreSnapshot(store *Store, path string, dbName string) error {
defer db.Close()
decoder := json.NewDecoder(bufio.NewReader(f))
wb := db.NewWriteBatch()
defer wb.Cancel()
entryNo := 0
for {
var entry dumpEntry
if err := decoder.Decode(&entry); err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("entry %d: %w", entryNo+1, err)
}
entryNo++
if entry.Key == "" {
return fmt.Errorf("entry %d: missing key", entryNo)
}
value, err := decodeEntryValue(entry)
if err != nil {
return fmt.Errorf("entry %d: %w", entryNo, err)
}
entryMeta := byte(0x0)
if entry.Secret {
entryMeta = metaSecret
}
writeEntry := badger.NewEntry([]byte(entry.Key), value).WithMeta(entryMeta)
if entry.ExpiresAt != nil {
if *entry.ExpiresAt < 0 {
return fmt.Errorf("entry %d: expires_at must be >= 0", entryNo)
}
writeEntry.ExpiresAt = uint64(*entry.ExpiresAt)
}
if err := wb.SetEntry(writeEntry); err != nil {
return fmt.Errorf("entry %d: %w", entryNo, err)
}
}
if err := wb.Flush(); err != nil {
_, err = restoreEntries(decoder, db, restoreOpts{})
return err
}
return nil
}
// hasMergeConflicts returns true if there are files with unresolved merge
// conflicts in the working tree.
func hasMergeConflicts(dir string) (bool, error) {
cmd := exec.Command("git", "diff", "--name-only", "--diff-filter=U")
cmd.Dir = dir
out, err := cmd.Output()
if err != nil {
return false, err
}
return len(bytes.TrimSpace(out)) > 0, nil
}

View file

@ -8,7 +8,7 @@ $ pda get foobar --secret --> FAIL
Error: cannot get 'foobar': Key not found
Error: cannot get 'foobar': Key not found
Error: cannot get 'foobar': Key not found
Error: unknown flag: --secret
Error: cannot get 'foobar': Key not found
Error: cannot get 'foobar': Key not found
Error: cannot get 'foobar': Key not found
Error: cannot get 'foobar': Key not found
Error: unknown flag: --secret
Error: unknown flag: --secret

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello
$ pda set foo < cmd
$ pda get foo --include-binary --run --secret
hello

View file

@ -1,6 +0,0 @@
$ fecho cmd echo hello
$ pda set a < cmd
$ pda get a
echo hello
$ pda get a --run --secret
hello

View file

@ -1,3 +0,0 @@
$ pda set foo bar
$ pda get foo --secret
bar

View file

@ -1,3 +0,0 @@
$ pda set a b --secret
$ pda get a --> FAIL
Error: cannot get 'a': marked as secret, run with --secret

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --include-binary --> FAIL
Error: cannot get 'a': marked as secret, run with --secret

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --include-binary --run --> FAIL
Error: cannot get 'a': marked as secret, run with --secret

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --run --> FAIL
Error: cannot get 'a': marked as secret, run with --secret

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --secret --run --include-binary
hello world

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --include-binary --secret
echo hello world

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --run --secret
hello world

View file

@ -1,4 +0,0 @@
$ fecho cmd echo hello world
$ pda set a --secret < cmd
$ pda get a --secret
echo hello world

View file

@ -13,7 +13,6 @@ Flags:
-g, --glob strings Filter keys with glob pattern (repeatable)
--glob-sep string Characters treated as separators for globbing (default "/-_.@: ")
-h, --help help for export
--secret Include entries marked as secret
Dump all key/value pairs as NDJSON
Usage:
@ -27,4 +26,3 @@ Flags:
-g, --glob strings Filter keys with glob pattern (repeatable)
--glob-sep string Characters treated as separators for globbing (default "/-_.@: ")
-h, --help help for export
--secret Include entries marked as secret

View file

@ -20,7 +20,6 @@ Flags:
-b, --include-binary include binary data in text output
--no-template directly output template syntax
-c, --run execute the result as a shell command
--secret display values marked as secret
Get the value of a key. Optionally specify a store.
{{ .TEMPLATES }} can be filled by passing TEMPLATE=VALUE as an
@ -41,4 +40,3 @@ Flags:
-b, --include-binary include binary data in text output
--no-template directly output template syntax
-c, --run execute the result as a shell command
--secret display values marked as secret

View file

@ -17,7 +17,6 @@ Flags:
-h, --help help for list
--no-keys suppress the key column
--no-values suppress the value column
-S, --secret display values marked as secret
-t, --ttl append a TTL column when entries expire
List the contents of a store
@ -36,5 +35,4 @@ Flags:
-h, --help help for list
--no-keys suppress the key column
--no-values suppress the value column
-S, --secret display values marked as secret
-t, --ttl append a TTL column when entries expire

View file

@ -20,7 +20,6 @@ Aliases:
Flags:
-h, --help help for set
-i, --interactive Prompt before overwriting an existing key
--secret Mark the stored value as a secret
-t, --ttl duration Expire the key after the provided duration (e.g. 24h, 30m)
Set a key to a given value or stdin. Optionally specify a store.
@ -42,5 +41,4 @@ Aliases:
Flags:
-h, --help help for set
-i, --interactive Prompt before overwriting an existing key
--secret Mark the stored value as a secret
-t, --ttl duration Expire the key after the provided duration (e.g. 24h, 30m)

View file

@ -1,7 +1,8 @@
$ pda set foo 1
$ pda set bar 2
$ pda ls
a **********
a echo hello
a1 1
a2 2
b1 3

View file

@ -1 +0,0 @@
$ pda set foo foobar --secret

View file

@ -1 +0,0 @@
$ pda set a b --secret --ttl 10m