{"id":47998160,"url":"https://github.com/bnema/sekeve","last_synced_at":"2026-04-04T12:07:22.858Z","repository":{"id":344748456,"uuid":"1182602856","full_name":"bnema/sekeve","owner":"bnema","description":"CLI secret manager with GPG encryption and gRPC sync. Built with Go 1.26 runtime/secret memory protection.","archived":false,"fork":false,"pushed_at":"2026-03-29T16:18:08.000Z","size":369,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-29T16:27:32.893Z","etag":null,"topics":["cli","encryption","golang","gpg","grpc","password-manager","secret-manager","security"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bnema.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-15T18:30:03.000Z","updated_at":"2026-03-29T16:18:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bnema/sekeve","commit_stats":null,"previous_names":["bnema/sekeve"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/bnema/sekeve","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnema%2Fsekeve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnema%2Fsekeve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnema%2Fsekeve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnema%2Fsekeve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bnema","download_url":"https://codeload.github.com/bnema/sekeve/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnema%2Fsekeve/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31398799,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","encryption","golang","gpg","grpc","password-manager","secret-manager","security"],"created_at":"2026-04-04T12:07:22.256Z","updated_at":"2026-04-04T12:07:22.850Z","avatar_url":"https://github.com/bnema.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sekeve\n\nA secret manager with a GTK4 overlay UI and a full CLI. Secrets are encrypted locally with your GPG key and stored on a remote gRPC server. The server sees only encrypted blobs. Your private key never leaves your machine.\n\nDecrypted values are protected in memory using Go 1.26's `runtime/secret.Do`, which zeroes registers, stack, and heap after use.\n\n## Install\n\n```bash\ngo install -tags runtimesecret github.com/bnema/sekeve/cmd/sekeve@latest\n```\n\nOr build from source:\n\n```bash\nmake build\nmake install\n```\n\n### Runtime dependencies (Linux)\n\nThe GTK4 overlay (omnibox and PIN prompt) requires `libgtk-4` and `gtk4-layer-shell`. Install via your package manager:\n\n```bash\n# Arch\nsudo pacman -S gtk4 gtk4-layer-shell\n\n# Debian / Ubuntu\nsudo apt install libgtk-4-1 libgtk4-layer-shell0\n```\n\nWhen `gtk4-layer-shell` is installed, sekeve automatically sets `LD_PRELOAD` and re-execs itself — no wrapper scripts needed. Without it, the overlay opens as a regular window. The CLI works either way.\n\n## Server setup\n\n\u003e [!WARNING]\n\u003e The gRPC server uses **plaintext HTTP/2** by default and does not terminate TLS. Do not expose it directly to the internet. Place it behind a reverse proxy that handles TLS, such as Caddy, nginx with a certificate, or Cloudflare proxy. All data on the wire is GPG-encrypted, but connection metadata and the auth handshake are not protected without TLS.\n\n### Docker Compose (recommended)\n\n```bash\ndocker compose up -d\n```\n\nOn first run, initialize the server with your GPG public key:\n\n```bash\ndocker compose run -it sekeve-server server init --data /data/sekeve.db\n```\n\nThis opens an interactive prompt where you paste your armored GPG public key. The key is stored in the database; no file remains on disk.\n\nThe server stores the registered public key. If PIN unlock is enabled, it also stores the PIN hash and salt. It never stores the plaintext PIN.\n\nYou can also provide the key non-interactively:\n\n```bash\n# Via file\ndocker compose run sekeve-server server init --data /data/sekeve.db --pubkey-file /path/to/key.asc\n\n# Via environment variable\nSEKEVE_PUBKEY=\"$(gpg --export --armor you@example.com)\" docker compose run sekeve-server server init --data /data/sekeve.db\n\n# Via stdin\ngpg --export --armor you@example.com | docker compose run -T sekeve-server server init --data /data/sekeve.db\n```\n\n### Local\n\n```bash\nsekeve server init --pubkey-file ./key.asc --data ./sekeve.db\nsekeve server start --data ./sekeve.db --addr :50051\n```\n\n### Port configuration\n\nCreate a `.env` file (gitignored) to map the host port:\n\n```env\nSEKEVE_SERVER_PORT=50053\n```\n\nThe container always listens on port 50051 internally.\n\n## Client setup\n\nRun the interactive setup:\n\n```bash\nsekeve init\n```\n\nThis prompts for the server address and GPG key ID, writes `$XDG_CONFIG_HOME/sekeve/config.toml` (defaults to `~/.config/sekeve/config.toml`), and verifies the server connection.\n\nBoth fields can be overridden with `--server`, `--gpg-key` flags or `SEKEVE_SERVER_ADDR`, `SEKEVE_GPG_KEY_ID` environment variables.\n\n## Environment variables\n\n| Variable             | Description                                          | Default   |\n|----------------------|------------------------------------------------------|-----------|\n| `SEKEVE_LOG_LEVEL`   | Log level: `trace`, `debug`, `info`, `warn`, `error` | `info`    |\n| `SEKEVE_LOG_FORMAT`  | Log output format: `console`, `json`                 | `console` |\n| `SEKEVE_SERVER_ADDR` | gRPC server address                                  | `localhost:50051` |\n| `SEKEVE_GPG_KEY_ID`  | GPG key ID for encryption/auth                       | (none)    |\n| `SEKEVE_PUBKEY`      | Armored GPG public key for non-interactive server init | (none)  |\n\n## Usage\n\n```bash\n# Add entries\nsekeve add login github --site github.com --username user\nsekeve add secret stripe-key sk_live_abc123\necho \"some notes\" | sekeve add note meeting-notes\n\n# Retrieve and decrypt\nsekeve get github\nsekeve get stripe-key --json\n\n# List and search\nsekeve list\nsekeve list --type login --json\nsekeve search git\n\n# Edit and delete\nsekeve edit github\nsekeve rm stripe-key\n```\n\n## Omnibox overlay\n\n`sekeve omnibox` opens a GTK4 overlay for searching, viewing, adding, and editing entries — all from one keyboard-driven window. It renders as a Wayland layer-shell surface, so it floats above your desktop without a title bar or window decorations.\n\nBind it to a hotkey in your compositor:\n\n```kdl\n// niri\nMod+Ctrl+P hotkey-overlay-title=\"Sekeve\" { spawn \"sekeve\" \"omnibox\"; }\n```\n\n```ini\n# sway / hyprland\nbindsym $mod+Ctrl+p exec sekeve omnibox\n```\n\nIf no session exists, a PIN prompt appears first. Press Escape on empty search to close the overlay.\n\n## Fuzzel / dmenu integration\n\nList all entries in a picker-friendly format, then copy the selected value to clipboard:\n\n```bash\nsel=$(sekeve dmenu --list | fuzzel --dmenu --with-nth=1 --accept-nth=2) \u0026\u0026 sekeve dmenu --copy \"$sel\"\n```\n\nWhen PIN is configured, use `--ensure-session` to authenticate before the picker opens (see [PIN unlock](#pin-unlock) below).\n\nLogins copy the password, secrets copy the value, notes copy the full content.\n\n## Import from Bitwarden\n\nExport your vault from Bitwarden as unencrypted JSON (`bw export --format json`), then import:\n\n```bash\nsekeve import bitwarden ~/bw-export.json\n```\n\nLogins are imported with the username appended to the name (e.g., `GitHub (alice@work.com)`). URIs are normalized to strip paths while preserving subdomains. Secure notes are imported as-is. Cards, identities, and SSH keys are skipped.\n\nDelete the export file after import - it contains plaintext credentials.\n\n## PIN unlock\n\nPIN unlock adds a second step to the GPG challenge-response flow. The client asks for a PIN only when the server requires one.\n\n`sekeve` reuses a cached session token first. If none exists, it authenticates with GPG. If the server still requires a PIN, it prompts in the TTY or in the GTK overlay when no TTY is available.\n\nThe server stores the PIN as an argon2id hash and salt, requires the current PIN to change it, issues one-time unlock tickets, backs off after failed attempts, and invalidates all sessions when the PIN changes.\n\n```bash\nsekeve pin set       # first-time setup; 4-6 digits\nsekeve pin change    # change an existing PIN\n```\n\nIn a terminal, the PIN is prompted interactively. When launched from a hotkey with no TTY (for example, a niri or sway keybinding), a GTK4 overlay prompt appears automatically via layer-shell.\n\nFor the dmenu workflow, use `--ensure-session` so the PIN prompt appears before fuzzel:\n\n```bash\nsekeve dmenu --ensure-session \u0026\u0026 sel=$(sekeve dmenu --list | fuzzel --dmenu) \u0026\u0026 sekeve dmenu --copy \"$sel\"\n```\n\nWithout `--ensure-session`, fuzzel opens simultaneously with the PIN prompt and steals focus. The omnibox handles this automatically.\n\n## Auth\n\nGPG challenge-response establishes the session: the server encrypts a nonce with your public key, and the client decrypts it to prove identity. The client caches the session token locally for one hour.\n\n## Development\n\n```bash\nmake proto    # regenerate protobuf\nmake test     # run all tests\nmake lint     # golangci-lint\nmake mock     # regenerate mocks\nmake build    # build binary\nmake wipe     # delete all vault entries (requires jq)\nmake install  # install to $GOBIN\n```\n\nRequires Go 1.26+, `buf`, `mockery`, `golangci-lint`, and `gpg`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnema%2Fsekeve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbnema%2Fsekeve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnema%2Fsekeve/lists"}