🔐 sekrt
sekrt — secret, 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).
- 🔑 Passwords & API keys — organised in folders, generated, copied with auto-clearing clipboard
- 📄
.envfiles — encrypt the.envof 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 syncpushes/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
- Quickstart
- Syncing with a git remote
- The
.envworkflow - SSH keys
- The TUI
- CLI reference
- Security model
- Vault location & configuration
- Why not just
pass? - Development
- Roadmap
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
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 another machine, point $SEKRT_VAULT at a fresh directory (or just run
sekrt init, then sekrt remote <url> — sync will pull the rest):
sekrt remote git@github.com:you/secrets.git
sekrt sync
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 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
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.
| 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 and SSH entries show up in the tree read-only — add, restore and
inspect those from the CLI (sekrt env, sekrt ssh) instead.
CLI reference
sekrt init [--remote URL] create a vault
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
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 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.
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, notpassword-is-hunter2). - Session cache:
sekrt unlockstores 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 lockclears 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, entries0600, atomic writes, restored SSH keys0600/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 syncsuddenly 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. TheTUPACS_*variables above still work as fallbacks, and vaults written by 0.1.0 (.tupentry 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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sekrt-0.2.0.tar.gz.
File metadata
- Download URL: sekrt-0.2.0.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
308453139c3751074c87c900cd2ffc160be0fd42c1fad55cee86b11da126f407
|
|
| MD5 |
5847b52a5c8ed073d7af5c691d50ae80
|
|
| BLAKE2b-256 |
8feaabdc981618bd11dc66fc6b8feb91cc9239c257ed0b0d3774e888e35a17ce
|
File details
Details for the file sekrt-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sekrt-0.2.0-py3-none-any.whl
- Upload date:
- Size: 37.9 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f816ec0a77fea8752bde09d78277f1c1bc2e9a72331522fe1ca0ac0c33b76f39
|
|
| MD5 |
1d9b1e72e70c1cf4d4cf07de022f12d7
|
|
| BLAKE2b-256 |
8ec67b8368727fc4078ddafe61e2b4c30f8104aa0b390fc43c45ebccbf0fa6e6
|