{"id":51778336,"url":"https://github.com/bborbe/lockbox","last_synced_at":"2026-07-20T08:32:15.431Z","repository":{"id":371270675,"uuid":"1299719465","full_name":"bborbe/lockbox","owner":"bborbe","description":"Small self-hosted secret manager in Go — TeamVault-compatible API, kv-backed (no Postgres)","archived":false,"fork":false,"pushed_at":"2026-07-15T21:55:01.000Z","size":229,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-07-20T08:32:13.895Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bborbe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-07-13T20:43:00.000Z","updated_at":"2026-07-15T21:55:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bborbe/lockbox","commit_stats":null,"previous_names":["bborbe/lockbox"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/bborbe/lockbox","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flockbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flockbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flockbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flockbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bborbe","download_url":"https://codeload.github.com/bborbe/lockbox/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flockbox/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35681006,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"ssl_error","status_checked_at":"2026-07-20T02:08:09.736Z","response_time":111,"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":[],"created_at":"2026-07-20T08:32:14.697Z","updated_at":"2026-07-20T08:32:15.421Z","avatar_url":"https://github.com/bborbe.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lockbox\n\nA small, self-hosted secret manager in Go — API-compatible with [TeamVault](https://github.com/seibert-media/teamvault), backed by a key-value store ([bborbe/kv](https://github.com/bborbe/kv): BoltDB / Badger), no Postgres.\n\nDrop-in replacement for a personal TeamVault server: existing [teamvault-cli](https://github.com/Seibert-Data/teamvault-cli) clients work with only a base-URL swap.\n\n## Components\n\n| Path | Description |\n|------|-------------|\n| `main.go` | Lockbox server — serves the TeamVault-compatible HTTP API, kv-backed |\n| `cmd/lockbox-cli/` | Client CLI — read secrets by key |\n| `cmd/migrate-teamvault/` | One-shot Postgres → kv importer (seeds from an existing TeamVault DB) |\n| `cmd/reencrypt/` | One-shot re-encrypt sweep — rewrites every stored secret under the current primary key so retired keys can be removed |\n\n## API\n\nMirrors the TeamVault API (see `cmd/fakevault` in teamvault-cli):\n\n| Endpoint | Response |\n|----------|----------|\n| `POST /api/secrets/` | create a secret (server-generated key); returns `{hashid, api_url, content_type, name, username, url}` |\n| `GET /api/secrets/{key}/` | `{name, username, url, current_revision}` |\n| `GET /api/secret-revisions/{key}/data` | `{password, file}` |\n| `GET /api/secrets/?search=q` | `{count, next, previous, results: [{hashid, api_url, name, username, url}]}` |\n| `PATCH /api/secrets/{hashid}/` | update metadata and/or value; returns the secret representation |\n\nBoth `/api/…` and `/api/v1/…` route to the same handlers. HTTP Basic auth.\n\n## Encryption at rest\n\nSecrets are AES-GCM encrypted (via [bborbe/crypto](https://github.com/bborbe/crypto)) before they are written to the kv store; only ciphertext ever touches disk. Configure the key with `LOCKBOX_ENCRYPTION_KEY`: a base64-encoded 16- or 32-byte AES key. Generate one with `openssl rand -base64 32`. The server refuses to start if the key is missing or not 16/32 raw bytes.\n\n### Keyring and key rotation\n\nFor multi-key operation and key rotation, set `LOCKBOX_ENCRYPTION_KEYS` — a comma-separated list of base64 AES keys (primary first). New secrets are sealed under the primary key and tagged with a content-derived key identifier; any secret is read back under the key that sealed it. Exactly one of `LOCKBOX_ENCRYPTION_KEY` (single-key shorthand) or `LOCKBOX_ENCRYPTION_KEYS` must be set; setting both or neither refuses start, as does any key that is not valid base64 decoding to 16 or 32 bytes, or a duplicate key.\n\nThe single `LOCKBOX_ENCRYPTION_KEY` still works unchanged and boots an equivalent one-entry keyring; existing deployments need no configuration change. Legacy ciphertext written before the keyring feature stays readable in place with no rewrite.\n\n**Rotation by restart** — to rotate the master key, prepend a new key to `LOCKBOX_ENCRYPTION_KEYS` (making it the new primary) and restart the server. New writes adopt the new primary key; every previously stored secret still decrypts under its original key which remains in the keyring. No downtime, no data loss, no rewrite at rotation time.\n\nOnce the server has restarted with the new primary, run the one-shot re-encrypt sweep to migrate all existing secrets onto the new key:\n\n```bash\nDATADIR=/data LOCKBOX_ENCRYPTION_KEYS=\"$(cat /etc/lockbox/new-key.key),$(cat /etc/lockbox/old-key.key)\" go run ./cmd/reencrypt\n```\n\nThe sweep reads every stored secret and rewrites it under the current primary key, after which no secret references a retired key. The command is idempotent and crash-safe — re-running it after an interruption leaves every secret readable and unchanged.\n\n**Operator key-rotation flow:**\n1. Prepend the new key to `LOCKBOX_ENCRYPTION_KEYS` and restart the server.\n2. Run `cmd/reencrypt` to migrate all existing secrets onto the new primary key.\n3. Remove the retired key from `LOCKBOX_ENCRYPTION_KEYS` and restart the server.\n\n## Run locally\n\n```bash\nmake test\nmake run\n```\n\n## End-to-end tests\n\n`make e2e` runs hermetic scenarios (`scenarios/001-core-api-e2e.md`,\n`scenarios/002-keyring-rotation-e2e.md`) that build the real `lockbox` and\n`cmd/reencrypt` binaries and drive them against a temp data dir over HTTP —\nfull TeamVault-compatible API coverage plus a live keyring-rotation +\nreencrypt-sweep flow. No live TeamVault, no network.\n\n## Deploy\n\n```bash\nmake buca\n```\n\n## License\n\nBSD-style license. See [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Flockbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbborbe%2Flockbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Flockbox/lists"}