Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

stash is a command-line manager for notes and URLs, stored in an encrypted local database. Each item is identified by a short name and versioned: every edit is archived so you can review the full history at any time.

Features

  • Two item types — note, url
  • Encrypted at rest — whole-database SQLCipher encryption (AES-256-CBC); Argon2id key derivation
  • Titles — an optional human-readable title on every item, tracked through version history
  • Tags — attach multiple tags to any item; filter and search by tag
  • Version history — every edit is archived; nothing is silently overwritten
  • Editor integration — $EDITOR opens for composing or editing any item
  • Browser integration — open URL items directly, with optional private/incognito mode; store a per-item preferred browser and a per-item always-private preference
  • Export / import — dump the vault to a portable JSON file and restore it to any vault
  • Self-contained — bundles SQLite; no external database or service required

Disclaimer

Although the cryptographic primitives implemented in this program are believed to be sound, they have not undergone a security review. The encryption provided will protect against snooping family members, and probably colleagues (depending on where you work). But if your notes and URLs need protection from serious and determined attackers, you may want to store them elsewhere. See Security Notes before relying on stash for anything sensitive.

How this manual is organized

Installation

Pre-built binaries

Download the latest release for your platform from the releases page:

PlatformArchive
Linux x86_64 (glibc)secretstash-linux-x86_64.tar.gz
Linux x86_64 (musl / static)secretstash-linux-x86_64-musl.tar.gz
macOS aarch64 (Apple Silicon)secretstash-macos-aarch64.tar.gz
Windows x86_64secretstash-windows-x86_64.zip

Extract the archive and place the stash binary somewhere on your $PATH.

From source

Requires Rust 1.85+.

cargo install --path secretstash-cli

Shell completions

stash can generate completion scripts for bash, zsh, fish, PowerShell, and Elvish via stash completions:

# bash — add to ~/.bashrc
source <(stash completions bash)

# zsh — add to ~/.zshrc
source <(stash completions zsh)

# fish — save to the completions directory
stash completions fish > ~/.config/fish/completions/stash.fish

Configuration

Config file

stash reads ~/.config/stash/stash.toml on startup (macOS: ~/Library/Application Support/stash/stash.toml). Create the directory and file if they do not exist.

# ~/.config/stash/stash.toml
db = "/mnt/usb/stash.db"
session_timeout_minutes = 60   # default: 15; set to 0 to disable timeout
clipboard_clear_seconds = 30   # clear clipboard N seconds after --copy; default: 0 (disabled)
browser = "firefox"            # preferred browser for `stash web`
private = true                 # default privacy mode for URL items with no per-item preference

# Private-mode flags for browsers not built in to stash.
# Key = browser binary name, value = the flag that opens a private window.
[browser_flags]
opera          = "--private"
my-browser     = "--incognito"

Alternate database location

The database path is resolved in this order (first match wins):

SourceExample
--db flagstash --db /tmp/test.db list
STASH_DB env varexport STASH_DB=/mnt/usb/stash.db
db in stash.tomldb = "/mnt/usb/stash.db"
Default~/.local/share/stash/stash.db

Data storage

FilePurpose
~/.local/share/stash/stash.dbSQLCipher-encrypted database
~/.local/share/stash/stash.saltArgon2id salt for key derivation (mode 0600)
~/.local/share/stash/.sessionCached session key (mode 0600)

On macOS the base directory is ~/Library/Application Support/stash/.

Quick Start

# First run: creates a new vault and prompts for a master password
stash auth login

# Login with a password from stdin (useful in scripts)
echo "mypassword" | stash auth login

# Login with a custom session timeout (overrides config for this session)
stash auth login --timeout 60   # expire after 60 minutes of inactivity
stash auth login --timeout 0    # never expire

# Add items: stash add <type> <name> [content]
stash add note todo  "Buy milk"
stash add url  gh    "https://github.com"
stash add url  work  "https://example.com" --browser firefox
stash add url  priv  "https://example.com" --private       # always open in private mode
stash add url  combo "https://example.com" -b firefox -p   # browser + private at creation

# Add an item with a title
stash add url gh "https://github.com" --title "GitHub"

# Add items with tags
stash add note todo "Buy milk" --tag work --tag personal

# Compose a longer note in your editor
stash add note journal --edit

# Read from stdin
echo "some text" | stash add note pipe --stdin

# Show an item
stash show gh                   # prints title (if set) then content
stash show gh --verbose         # content + metadata (type, title, timestamps, tags)
stash show gh --copy            # copy content to clipboard (no terminal output)

# Edit an item (opens $EDITOR; old version is archived automatically)
stash edit todo

# Update just the title, without opening an editor
stash edit gh --title "GitHub Home"

# View version history (includes title per version)
stash history gh

# Open a URL in the browser
stash web gh
stash web gh --private          # incognito / private mode
stash web gh --browser firefox  # use a specific browser (overrides config)

# Set stored browser preference for one or more URL items
stash browser firefox gh                    # set browser for one item
stash browser firefox gh work archive       # set browser for multiple items at once
stash browser firefox --all                 # set browser for every URL in the vault
stash browser firefox gh --clear            # clear stored browser preference
stash browser firefox gh -p                 # always open in private mode (-a / --private)
stash browser firefox gh --no-private       # clear private-mode preference

# List all items (TITLE column shown when any item has a title)
stash list

# List items filtered by tag (OR logic — shows items with any matching tag)
stash list --tag work
stash list --tag work --tag personal

# List items filtered by type
stash list --type url

# Manage tags on existing items
stash tag todo urgent
stash untag todo personal

# List all tags in the vault with item counts
stash tags

# Search item content and titles (case-insensitive substring)
stash search "github"

# Search using a regular expression (case-sensitive)
stash search --regex '\d{4}-\d{2}-\d{2}'

# Case-insensitive regex (use (?i) inline flag)
stash search --regex '(?i)github'

# Also search archived versions (shown as name:vN)
stash search --include-history "old value"
stash search --regex --include-history '(?i)todo'

# Filter by tag (repeatable; OR logic — shows items with any matching tag)
stash search --tag work
stash search --tag work --tag personal

# Filter by type
stash search --type url

# Combine: pattern + tag + type
stash search --tag work --type url "meeting"

# Delete an item and all its history
stash purge todo
stash purge todo --force        # skip confirmation (useful in scripts)

# Rename an item
stash rename todo todo-done

# Copy an item to a new shortname (copies content, title, type, and tags)
stash copy aws-key aws-key-backup

# Restore an item to its previous version (undo last edit; restores title too)
stash restore todo

# Restore to a specific version
stash restore todo --version 2

# End the session
stash auth logout

# Change the master password (re-keys the database in place)
stash auth reset

# Migrate an existing vault from the old field-level-encrypted format
stash migrate

# Export the entire vault to a JSON file
stash export -o vault.json

# Export including full version history for each item
stash export --include-history -o vault-with-history.json

# Import from a JSON export (skips items that already exist)
stash import vault.json

# Import and overwrite items that already exist
stash import --overwrite vault.json

# Pipe between vaults (e.g. copy to a different database)
stash export --include-history | stash import --db /path/to/other.db

Every command used above is documented in detail in the Command Reference.

Command Reference

Every stash invocation accepts a global --db <FILE> flag (before or after the subcommand) that overrides the database path for that single invocation — see Configuration for the full resolution order.

The subcommands are grouped below by what they operate on:

GroupCommands
Authenticationauth login, auth status, auth logout, auth reset
Adding itemsadd
Reading itemsshow
Editing itemsedit
Historyhistory, restore
Browserweb
Browser preferencesbrowser
Listing & searchinglist, search
Tagstag, untag, tags
Item managementrename, copy, purge
Export & importexport, import
Migrationmigrate
Shell completionscompletions

auth

stash auth login

stash auth login [--timeout <MINUTES>]

Authenticates against the vault. On first run, creates a new vault and prompts you to set a master password (minimum 12 characters). On subsequent runs, prompts for the existing password.

If stdin is not a terminal (i.e., data is piped in), the first line is read as the password and the confirmation prompt is skipped. This enables non-interactive use in scripts:

echo "mypassword" | stash auth login

The derived key is cached for up to session_timeout_minutes minutes (default 15; 0 = no timeout). The timeout is a sliding window — every stash command resets the clock, so the session only expires after that many minutes of inactivity. Pass --timeout to override the config value for this login only:

stash auth login --timeout 60   # expire after 60 minutes of inactivity
stash auth login --timeout 0    # never expire

Timeout resolution order: --timeout flag > session_timeout_minutes in stash.toml > default (15 minutes).

On macOS the key is stored in Keychain; on Linux it is stored in the Secret Service (GNOME Keyring / KWallet) if secret-tool is available. In both cases the session file (~/.local/share/stash/.session, mode 0600) is kept as a fallback. The login message notes (keychain) when the system keychain was used.

stash auth status

Shows whether a session is currently active.

stash auth logout

Removes the cached session key. The vault database is not affected.

stash auth reset

Changes the master password. Prompts for the current password (to verify it), then prompts for the new password (minimum 12 characters, with confirmation). The database is re-keyed in place via SQLCipher’s PRAGMA rekey — a single atomic operation that re-encrypts every page of the database file.

The current session is cleared on success; run stash auth login afterward.

add

stash add <url|note> <name> [TEXT] [options]
Argument / OptionDescription
url|noteItem type (required, positional)
<name>Short identifier used in all other commands (required, positional)
TEXTInline content (optional positional)
-t, --title <TITLE>Human-readable title
-e, --editOpen $EDITOR to compose content
--stdinRead content from standard input
-g, --tag <TAG>Attach a tag (repeatable: --tag work --tag personal)
-b, --browser <BROWSER>Store a preferred browser (url items only)
-p, --privateAlways open this URL in private/incognito mode (url items only)
-n, --no-privateClear the stored private-mode preference (url items only)

Exactly one of TEXT, --edit, or --stdin must be supplied. --private and --no-private are mutually exclusive.

stash add note todo  "Buy milk"
stash add url  gh    "https://github.com"
stash add url  gh    "https://github.com" --title "GitHub"
stash add note todo  "Buy milk" --tag work --tag personal
stash add url  work  "https://example.com" --browser firefox
stash add url  priv  "https://example.com" --private
stash add url  combo "https://example.com" -b firefox -p
stash add note journal --edit
echo "some text" | stash add note pipe --stdin

show

stash show <shortname> [-v/--verbose] [-c/--copy] [--clear-after <SECONDS>]

Prints the item’s content. If the item has a title it is printed as a header line above the content. If the item has tags they are shown on a tags: line after the content. Add --verbose / -v to show all metadata (type, title, browser preference, private flag, timestamps, and tags) in a header block.

Add --copy / -c to copy the content to the clipboard instead of printing it (requires pbcopy on macOS, wl-copy on Wayland, xclip or xsel on X11, or clip.exe on Windows/WSL).

Pass --clear-after <SECONDS> to automatically clear the clipboard after the given number of seconds. This overrides clipboard_clear_seconds in stash.toml for a single invocation:

stash show mypassword --copy                  # copy; no automatic clear
stash show mypassword --copy --clear-after 30  # copy; clear after 30 s

Clear timeout resolution order: --clear-after flag > clipboard_clear_seconds in stash.toml > 0 (disabled).

stash show gh                   # prints title (if set) then content
stash show gh --verbose         # content + metadata (type, title, timestamps, tags)
stash show gh --copy            # copy content to clipboard (no terminal output)

edit

stash edit <shortname> [-t/--title <TITLE>]

Without --title, opens the item in $EDITOR. If the content changes, the previous version is automatically saved to history before the update is written.

With --title <TITLE>, updates the title only without opening an editor. The current content and old title are archived to history as part of the update.

stash edit todo                       # opens $EDITOR
stash edit gh --title "GitHub Home"   # title-only update

See also history / restore for reviewing and undoing edits.

history / restore

stash history <shortname>

Shows all archived versions followed by the current content, each labelled with its version number and timestamp. If a version had a title set, it is shown alongside the content for that version.

stash history gh

stash restore <shortname>

stash restore <shortname> [--version <N>]

Restores an item to a previous version. Without --version, restores to the most recently archived version (undo last edit). Both content and title are restored from the history entry. The current content and title are archived before the restore, so the full history is preserved.

stash restore todo               # undo last edit
stash restore todo --version 2   # restore a specific version

web

stash web [-p/--private] [-b/--browser <browser>] <shortname>

Opens a url-type item in the browser. Pass -p / --private to open in private/incognito mode. Pass -b / --browser to specify a browser binary (e.g., firefox, google-chrome); this overrides the browser field in stash.toml. Without a specified browser, the system default is used (or, for --private, tries Firefox, Chrome, Chromium, Brave, and Vivaldi in order).

If the item has a stored private-mode preference (set via stash browser --private), private mode is activated automatically even without -p.

Browser resolution order: --browser flag > per-item stored browser (stash browser) > browser in stash.toml > system default.

Private mode resolution order: -p flag > per-item stored private preference > private in stash.toml > off.

stash web gh
stash web gh --private          # incognito / private mode
stash web gh --browser firefox  # use a specific browser (overrides config)

Browser name shorthand

Anywhere a browser name is accepted (--browser, stash browser, stash add -b), you can use the shortest unambiguous prefix of a known browser name instead of the full name:

stash web gh -b fi        # resolves to firefox
stash web gh -b go        # resolves to google-chrome
stash web gh -b brave     # resolves to brave-browser
stash web gh -b vi        # error: ambiguous (vivaldi, vivaldi-stable)
stash web gh -b vivaldi-  # resolves to vivaldi-stable

Unknown names (not in the built-in list) are passed through as-is, so custom browsers always use their exact binary name.

Built-in private-mode support: firefox (--private-window), google-chrome / chrome, chromium, chromium-browser, brave-browser, vivaldi, vivaldi-stable (--incognito). chrome is accepted as an alias for google-chrome.

Custom browsers: add a [browser_flags] section to stash.toml to enable private mode for any other browser:

[browser_flags]
opera      = "--private"
my-browser = "--incognito"

browser

Sets or clears browser preferences stored on one or more url-type items.

stash browser <BROWSER> <URL>... [options]
stash browser <BROWSER> --all    [options]
Argument / OptionDescription
<BROWSER>Browser binary to store (e.g. firefox, chromium)
<URL>...One or more URL item shortnames to update
--allApply to every URL item in the vault (conflicts with named shortnames)
--clearRemove the stored browser preference (<BROWSER> is ignored)
-a, --privateAlways open this URL in private/incognito mode
-n, --no-privateClear the stored private-mode preference

Flags can be combined: e.g. stash browser firefox gh -a sets both browser and private mode in one command. --private and --no-private are mutually exclusive.

stash browser firefox gh                  # set stored browser for one item
stash browser firefox gh work archive     # set browser for several items at once
stash browser firefox --all               # set browser for every URL in the vault
stash browser firefox gh --clear          # remove stored browser preference
stash browser firefox gh -a               # always open in private mode
stash browser firefox gh --no-private     # clear private-mode preference
stash browser firefox gh -a --clear       # clear browser pref, set private mode

See web for how these stored preferences interact with -p/--browser flags and stash.toml defaults.

list / search

stash list

stash list [-g/--tag <TAG>]... [-t/--type <TYPE>]

Lists all items in a table showing name, type, and tags. When at least one item has a title, a TITLE column is added to the table. Pass one or more -g/--tag options to show only items that have any of the specified tags (OR logic). Pass -t/--type to restrict to a single type (url or note). Both filters can be combined.

stash list
stash list --tag work
stash list --tag work --tag personal
stash list --type url
stash search [PATTERN] [--regex] [--include-history] [--tag <TAG>]... [--type <TYPE>]

Searches items by content, title, tag, and/or type. At least one of PATTERN, --tag, or --type is required.

OptionDescription
PATTERNText or regex to match against content and titles
-r, --regexTreat PATTERN as a regular expression
-H, --include-historyAlso search archived versions (shown as name:vN)
-g, --tag <TAG>Restrict to items with this tag (repeatable; OR logic)
-t, --type <TYPE>Restrict results to url or note

Without --regex, matching is a case-insensitive substring search. With --regex, the pattern is case-sensitive; prefix with (?i) for case-insensitive regex.

stash search "api key"
stash search --regex '\d{4}-\d{2}-\d{2}'
stash search --regex '(?i)secret'
stash search --tag work
stash search --tag work --tag personal
stash search --type url "github"
stash search --include-history "old password"
stash search --regex --include-history '(?i)todo'

tag / untag / tags

stash tag <shortname> <TAG>...

Adds one or more tags to an existing item. Duplicate tags are silently ignored.

stash tag todo urgent

stash untag <shortname> <TAG>...

Removes one or more tags from an existing item. Tags not present on the item are silently ignored.

stash untag todo personal

stash tags

Lists every tag in the vault, sorted alphabetically, with a count of how many items carry each tag:

TAG        ITEMS
──────────────────
personal   3
work       5

2 tag(s).

rename / copy / purge

stash rename <shortname> <new-name>

Renames an item. Fails if <new-name> is already in use.

stash rename todo todo-done

stash copy <shortname> <dest>

Copies an item (content, title, type, and tags) to a new shortname. History is not copied.

stash copy aws-key aws-key-backup

stash purge <shortname>

Deletes an item and its entire history after a confirmation prompt. Pass --force / -f to skip the prompt (useful in scripts).

stash purge todo
stash purge todo --force        # skip confirmation

export / import

stash export

stash export [-o/--output <FILE>] [--include-history]

Exports all vault items to JSON. Output goes to stdout by default so it can be piped or redirected; use -o/--output to write directly to a file.

OptionDescription
-o, --output <FILE>Write to this file instead of stdout
--include-historyInclude full version history for each item

The JSON format is versioned ("version": 1) and includes the shortname, type, content, title, tags, browser preference, private flag, and timestamps for every item. History entries include their title at the time of archiving.

stash export -o vault.json
stash export --include-history -o vault-with-history.json

stash import

stash import [FILE] [--overwrite]

Imports items from a JSON export file. Reads from stdin if FILE is omitted, making it composable with stash export via a pipe.

OptionDescription
FILEPath to the export file (omit to read from stdin)
--overwriteReplace existing items instead of skipping them

By default, items whose shortname already exists in the vault are skipped and counted in the summary. Pass --overwrite to delete the existing item (and its history) and replace it with the imported version. History entries present in the export file are restored in either case.

Prints a summary on completion: items imported, items skipped, items that failed (e.g. unknown type).

stash import vault.json
stash import --overwrite vault.json

# Pipe between vaults (e.g. copy to a different database)
stash export --include-history | stash import --db /path/to/other.db

migrate

stash migrate

Converts a vault from the old field-level-encrypted plain SQLite format to the current whole-database SQLCipher format. Run this once after upgrading from v1.0.x.

The same master password is used — no password change is required. The migration writes a new encrypted database to a sibling file first, then renames it into place atomically. The original file is not modified until the rename succeeds, so a crash mid-migration leaves the old vault intact.

After migration, run stash auth login to start a new session.

completions

stash completions <SHELL>

Prints a shell completion script to stdout. Supported shells: bash, zsh, fish, powershell, elvish.

Source the output in your shell’s startup file to get tab-completion for all subcommands, flags, and arguments:

# bash — add to ~/.bashrc
source <(stash completions bash)

# zsh — add to ~/.zshrc
source <(stash completions zsh)

# fish — save to the completions directory
stash completions fish > ~/.config/fish/completions/stash.fish

Keeping Secrets Out of Shell History

When you pass content inline (stash add note pw "s3cr3t"), that content lands in your shell history. The cleanest remedies are to use --edit or --stdin instead; if you still want inline content, you can suppress history recording at the shell level.

bash

Add to ~/.bashrc:

# Ignore any command that starts with "stash"
HISTIGNORE="stash *:stash:$HISTIGNORE"

Alternatively, enable space-prefix suppression and lead every sensitive stash command with a space:

HISTCONTROL=ignorespace   # or ignoreboth (combines with ignoredups)
#  ↓ leading space — not recorded
 stash add note pw "s3cr3t"

zsh

Add to ~/.zshrc:

# Option 1: ignore stash via a hook (works in all zsh versions)
zshaddhistory() { [[ $1 != stash\ * && $1 != stash$'\n' ]] }

# Option 2: space-prefix suppression (simpler, same caveat as bash)
setopt HIST_IGNORE_SPACE
#  ↓ leading space — not recorded
 stash add note pw "s3cr3t"

fish

Fish has no pattern-based ignore list. Use --stdin or --edit to keep secrets out of the command line entirely. If you do pass inline content and want to remove the entry afterward:

history delete -- "stash add note pw s3cr3t"

Security Notes

  • The entire database file is encrypted with SQLCipher (AES-256-CBC with HMAC-SHA512 page authentication). Shortnames, types, titles, tags, content, and timestamps are all opaque to anyone without the key.
  • The salt file (stash.salt) is stored in plaintext alongside the database. It contains the Argon2id salt used to derive the encryption key from your master password. Losing it means losing access to the vault.
  • The session file stores the raw 32-byte derived key in base64. It is written with mode 0600 and removed on stash auth logout.
  • $EDITOR opens items in a temporary file. Some editors create swap or backup files in the same directory.

See the disclaimer in the Introduction for the overall threat model stash is — and isn’t — designed for.