Skip to main content

🔐 sekrt

PyPI CI Python License: MIT

sekrtsecret, with the vowels taken out. Everything else is AES-256, and only you can read it.

A fast TUI + CLI secret manager for developers and DevOps engineers. Like pass, but with a modern Textual interface, first-class API key, SSH keypair and .env file support, and painless sync through any private git remote (GitHub, GitLab, self-hosted — anything).

sekrt TUI

  • 🔑 Passwords & API keys — organised in folders, generated, copied with auto-clearing clipboard
  • 📄 .env files — encrypt the .env of any repo into your vault, restore it in any fresh clone with one command
  • 🗝️ SSH keypairs — import, generate (ed25519), and restore with correct permissions
  • ☁️ Git sync — every change is a commit; sekrt sync pushes/pulls a private repo
  • 🖥️ TUI + CLI — a full keyboard-driven interface and script-friendly commands
  • 🪶 Lightweight — three dependencies (textual, click, cryptography), no daemon, no sudo, no gpg setup

Contents

Install

uv tool install sekrt      # recommended
# or: pipx install sekrt
# or: pip install --user sekrt

Requires Python 3.11+. Nothing else to set up — no daemon, no GPG keyring, no sudo.

Quickstart

sekrt init                             # create a vault at ~/.local/share/sekrt
sekrt add work/github -u alberto -g    # generate & store a password
sekrt get work/github -c               # copy it (clipboard clears in 45s)
sekrt                                  # open the TUI

On a second machine, init asks whether you already have a vault in a git repo and clones it for you if so — or skip the question with sekrt clone <url>.

That's a working local vault. init prompts you to choose (and confirm) a passphrase — that passphrase is the vault; there's no recovery if you lose it, so pick something you'll remember, and see the security model below before you rely on it for real.

Anything that touches a secret's contents (get, show, edit, add, mv, env, ssh, …) decrypts the vault key on demand, so by default you'll be prompted for the passphrase each time — ls, find, rm and status don't need to decrypt anything, so they never prompt. Run sekrt unlock once and the rest stop prompting for an hour (-t MIN to change that), courtesy of a RAM-backed, user-private session cache — like gpg-agent, without the agent. sekrt lock forgets it immediately.

sekrt unlock              # cache the key for 60 min
sekrt get work/github -c  # no prompt this time
sekrt lock                # forget it now

Syncing with a git remote

The vault is a plain git repository. sekrt sync is git pull --rebase then git push against origin — so you need an empty private repo to point it at (GitHub, GitLab, Gitea, a bare repo over SSH — anything git can push to).

# 1. create an empty private repo, e.g. `gh repo create secrets --private --clone=false`
sekrt remote git@github.com:you/secrets.git   # or: sekrt init --remote <url> on a fresh vault
sekrt sync                                    # first push

On every other machine, sekrt init asks the one question that matters and does the right thing with the answer:

$ sekrt init
Do you already have a sekrt vault pushed to a git repo? [y/N]: y
Vault repo URL: git@github.com:you/secrets.git
✔ vault cloned to ~/.local/share/sekrt
  3 entries available
  unlock with the passphrase that created this vault: `sekrt unlock`

sekrt clone <url> does the same thing in one shot if you'd rather not be asked.

[!IMPORTANT] A second machine must clone the vault, not create one. init generates a new encryption salt and a new git root, so two independently-created vaults share no history and cannot decrypt each other's entries. Cloning reuses the existing salt, which is why your original passphrase keeps working. You don't have to remember this: the prompt above steers you, init --remote <url> and remote <url> refuse when the remote already holds a vault, and sync refuses the impossible merge instead of corrupting anything.

Every add/edit/mv/rm auto-commits locally; sekrt sync is what actually talks to the remote. Want every change pushed immediately instead?

sekrt autosync on

Remember: the remote only ever sees ciphertext and entry names — see what the remote sees below.

The .env workflow

.env files never land in your project repos — so every fresh clone starts with a scavenger hunt. sekrt ends it:

cd ~/code/my-saas       # any git repo
sekrt env push         # encrypts .env into the vault, keyed by the repo's origin URL
sekrt sync

Months later, on another machine:

git clone git@github.com:you/my-saas.git && cd my-saas
sekrt env pull         # .env is back, byte for byte (0600 perms)

The .env round trip

The key is the repo's origin URL, not the path on disk — so a clone anywhere finds its own file, and HTTPS vs SSH remotes resolve to the same key. It handles several env files per repo (sekrt env push .env apps/*/.env.*), reports what actually changed rather than rewriting blindly, and never overwrites a local file you've edited without --force.

📄 Full guide: the .env workflow — how repos are identified, monorepos with one env file per service, the overwrite rules, using --repo for forks and renames, what the remote can see, and troubleshooting.

SSH keys

sekrt ssh add laptop --key ~/.ssh/id_ed25519    # import an existing keypair
sekrt ssh add deploy --generate                 # or generate a fresh ed25519 key
sekrt ssh pub deploy                            # print the public key for GitHub
sekrt ssh restore deploy --dir ~/.ssh           # on a new machine: 0600/0644, done

Whole files

For anything bigger than a note — a list of MFA recovery codes, a keystore, a PDF — sekrt file encrypts the file itself, byte-for-byte, no $EDITOR round-trip:

sekrt file add mfa/github-recovery ~/Downloads/recovery-codes.txt
sekrt file get mfa/github-recovery                    # restores original filename, cwd
sekrt file get mfa/github-recovery -o ./codes.txt      # or pick the destination
sekrt file ls

Binary-safe (content is base64-encoded at rest), and sekrt show only prints its size — use file get to get the bytes back out.

The TUI

sekrt with no arguments (or sekrt tui) opens the interface: a folder tree of your vault, fuzzy filtering, a masked detail view, add/edit forms with a built-in password generator, and one-key sync.

sekrt TUI walkthrough

Key Action
/ filter entries
c copy the entry's secret (auto-clears in 45s)
u copy the entry's username
r reveal / mask fields
a / e / d add / edit / delete
s sync with the git remote
l lock the vault (prompts for the passphrase again)
q quit

.env, SSH and file entries show up in the tree read-only — add, restore and inspect those from the CLI (sekrt env, sekrt ssh, sekrt file) instead.

CLI reference

sekrt init [--remote URL]      create a vault, or clone one if you have it already
sekrt clone URL                set up from an existing vault repo, no questions asked
sekrt add NAME [-u USER] [-g]  add password/api_key/note   (alias: insert)
sekrt get NAME [-c] [-f FIELD] print or copy a secret
sekrt show NAME [--reveal]     show all fields
sekrt ls [PREFIX]              list entries                (alias: list)
sekrt find QUERY               search names                (alias: search)
sekrt edit NAME                edit fields in $EDITOR (notes: raw multiline text)
sekrt mv OLD NEW               rename                      (alias: rename)
sekrt rm NAME [-f]             delete                      (alias: remove)
sekrt generate [LEN] [--token] generate without storing
sekrt env push|pull|ls|show|rm .env files per repository    (guide: docs/env.md)
sekrt ssh add|restore|ls|pub   SSH keypairs
sekrt file add|get|ls          whole files, binary-safe
sekrt remote URL               set the sync remote
sekrt sync                     pull --rebase + push
sekrt autosync on|off          push automatically on every change
sekrt git <args...>            raw git inside the vault
sekrt unlock [-t MIN] / lock   cache / forget the vault key
sekrt passwd                   change passphrase (re-encrypts everything)
sekrt status                   vault, remote, session info
sekrt tui                      open the interactive TUI

Every command has --help (e.g. sekrt add --help) with the full option list and examples.

sekrt CLI walkthrough

Security model

  • Encryption: every entry is an independent file encrypted with AES-256-GCM. The key is derived from your passphrase with scrypt (N=2¹⁵, r=8, p=1, random per-vault salt).
  • Tamper binding: an entry's logical name is the GCM associated data — a ciphertext moved or renamed by an attacker fails to decrypt.
  • What the remote sees: entry names and folder structure (like pass), timestamps, and commit history. Entry contents are always ciphertext. Use names accordingly (work/github, not password-is-hunter2).
  • Session cache: sekrt unlock stores the derived key (never the passphrase) in $XDG_RUNTIME_DIR — tmpfs on Linux: RAM-backed, user-only (0600), wiped on logout — with a TTL. sekrt lock clears it immediately.
  • Clipboard: auto-clears after 45 s, and only if it still holds the copied value. Secrets are never passed through argv.
  • Files: vault dir 0700, entries 0600, atomic writes, restored SSH keys 0600/0644.
  • Threat model: protects secrets at rest and in your git remote. It does not protect against an attacker with root/physical access to your unlocked machine — nothing userspace does.
  • Your passphrase is the whole game: anyone who obtains the vault files (including whoever hosts your sync remote) can attempt an offline brute-force. scrypt makes each guess expensive, but a weak passphrase falls anyway — use a long one. sekrt enforces a minimum of 8 characters; treat that as a floor, not a target.
  • A compromised remote cannot read entry contents or swap ciphertexts between names (AEAD name binding), but it can delete entries, serve you an old version of the vault (rollback), or corrupt the vault config. If sekrt sync suddenly reports missing entries or a passphrase failure, investigate before typing your passphrase anywhere else.

Found a vulnerability? Please report it privately via GitHub security advisories rather than a public issue.

Vault location & configuration

What Default Override
Vault directory ~/.local/share/sekrt $SEKRT_VAULT
Passphrase (CI/scripts) interactive prompt $SEKRT_PASSPHRASE
Editor for sekrt edit $EDITOR $VISUAL

The vault is a plain git repository — inspect it any time with sekrt git log.

Upgrading from tupacs? This project was published under that name through 0.1.0. The TUPACS_* variables above still work as fallbacks, and vaults written by 0.1.0 (.tup entry files) are read as-is. Only the vault directory needs a hand — see the migration note.

Why not just pass?

pass is excellent, and sekrt borrows its best idea (one encrypted file per secret, git-friendly). Differences: no GPG key management — a single passphrase with scrypt+AES-GCM; a real TUI; structured entries (username, URL, notes — not just a text blob); and purpose-built .env and SSH-key workflows.

Development

git clone https://github.com/alberto-rota/sekrt && cd sekrt
uv sync                 # installs everything incl. dev deps
uv run pytest           # tests
uv run ruff check .     # lint
uv run sekrt --help

Try changes against a throwaway vault so you never touch your real one:

export SEKRT_VAULT=/tmp/sekrt-dev SEKRT_PASSPHRASE=dev
uv run sekrt init && uv run sekrt

Contributions welcome — see CONTRIBUTING.md.

The docs/*.gif demos are recorded with VHS from the tapes in docs/vhs/. Run them from the repo root with sekrt on $PATH (brew install vhs, then uv tool install --editable .):

vhs docs/vhs/quickstart.tape   # -> docs/quickstart.gif
vhs docs/vhs/tui.tape          # -> docs/tui.gif (run quickstart.tape first to seed the demo vault)
vhs docs/vhs/env.tape          # -> docs/env.gif
vhs docs/vhs/env-multi.tape    # -> docs/env-multi.gif
vhs docs/vhs/env-safety.tape   # -> docs/env-safety.gif

The three env tapes are self-contained: each one rebuilds its fixture — real git repos with remotes, a fresh clone, and a scratch vault under /tmp/sekrt-vhs-env — by running docs/vhs/setup-env-demo.sh, and points $HOME at it, so your real vault and ~/.gitconfig are never touched.

docs/screenshot.svg (the image at the top of this file) is a Textual export rather than a recording, so it has its own generator:

uv run python docs/vhs/make-screenshot.py   # -> docs/screenshot.svg

Roadmap

  • sekrt grep — search inside decrypted entries
  • TOTP / 2FA codes (sekrt otp NAME)
  • Import from pass, Bitwarden, 1Password CSV
  • Diceware passphrase generation
  • Windows clipboard & session-cache support

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sekrt-0.3.2.tar.gz (1.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sekrt-0.3.2-py3-none-any.whl (43.8 kB view details)

Uploaded Python 3

File details

Details for the file sekrt-0.3.2.tar.gz.

File metadata

  • Download URL: sekrt-0.3.2.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sekrt-0.3.2.tar.gz
Algorithm Hash digest
SHA256 128c1a9739857fd0479fca97c9ef37ca38e7f4819534822371481585a452ed84
MD5 de7c10de8a4bafb7b534f7fe19a2c2fa
BLAKE2b-256 4e105df3da514e413bd7d514273d7b5bdcbb28966f2dde33a71ac951c3f8da92

See more details on using hashes here.

File details

Details for the file sekrt-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: sekrt-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 43.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sekrt-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b2b43f697cf24f8ed0041ef92d86feb2bbaadd793debab63365856abbb7faaa9
MD5 f9b51ead25fa9fed3f92f28d6f44f47b
BLAKE2b-256 6453e65dbccf2f83cefcc38d6f18e68db5d3faaf8d6c501204cca23ae6e842e3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page