// Command passv is a Vault-backed, drop-in replacement for the standard // password-store (`pass`). It keeps the exact on-disk layout — a tree of // `.gpg` binary OpenPGP files under $PASSWORD_STORE_DIR — but performs all // encryption and decryption through a vault-plugin-secrets-gpg engine mount, so // the private key never lives on the client. A store's `.gpg-id` holds a Vault // key reference ("/", e.g. "gpg/app") in place of a GPG fingerprint. package main import ( "errors" "fmt" "os" ) var version = "dev" // usageError is returned by commands for bad invocations (exit code 1 with the // command's own usage line already included in the message). type usageError struct{ msg string } func (e *usageError) Error() string { return e.msg } func main() { if err := run(os.Args[1:]); err != nil { var ue *usageError if errors.As(err, &ue) { fmt.Fprintln(os.Stderr, ue.msg) } else { fmt.Fprintln(os.Stderr, "passv: "+err.Error()) } os.Exit(1) } } func run(args []string) error { if len(args) == 0 { return cmdShow(nil) // default: list the whole store } switch args[0] { case "init": return cmdInit(args[1:]) case "ls", "list": return cmdShow(args[1:]) // show handles the dir-vs-entry split case "show", "cat": return cmdShow(args[1:]) case "find", "search": return cmdFind(args[1:]) case "grep": return cmdGrep(args[1:]) case "insert", "add": return cmdInsert(args[1:]) case "generate": return cmdGenerate(args[1:]) case "edit": return cmdEdit(args[1:]) case "rm", "remove", "delete": return cmdRm(args[1:]) case "mv", "rename": return cmdMv(args[1:]) case "cp", "copy": return cmdCp(args[1:]) case "git": return cmdGit(args[1:]) case "help", "-h", "--help": usage() return nil case "version", "--version", "-v": fmt.Printf("passv %s\n", version) return nil default: // pass semantics: a bare name is an implicit `show`. return cmdShow(args) } } func usage() { fmt.Print(`passv — a Vault-backed drop-in for pass Usage: passv [ls] [subfolder] list the store (or a subfolder) passv [show] pass-name [-c|--clip[=N]] decrypt an entry (optionally to clipboard) passv insert [-e|-m] [-f] pass-name add a new entry passv generate [-n] [-c] [-i|-f] name [length] generate and store a random password passv edit pass-name edit an entry in $EDITOR passv rm [-r] [-f] pass-name remove an entry or directory passv mv|cp [-f] old new move/copy (re-encrypts across gpg-ids) passv find term... list entries matching term passv grep [-i] regexp search decrypted content passv git args... run git in the store passv init [-p subfolder] / point a (sub)store at a Vault gpg key Environment: PASSWORD_STORE_DIR store location (default ~/.password-store) VAULT_ADDR, VAULT_TOKEN standard Vault connection settings PASSWORD_STORE_CLIP_TIME seconds before the clipboard is cleared (45) PASSWORD_STORE_GENERATED_LENGTH default generated length (25) `) } // fatalf builds a plain error (no "passv:" doubling for already-formatted msgs). func errf(format string, a ...interface{}) error { return fmt.Errorf(format, a...) }