Multi-backend credential abstraction for solo Python developers. SOPS+age, macOS Keychain, Bitwarden CLI, direnv, env vars.
Project description
himitsubako
秘密箱 — "secret box." Named after Japanese puzzle boxes from the Hakone region, which open through a sequence of sliding moves rather than a single key.
A multi-backend credential abstraction for solo Python developers. SOPS + age as the primary backend, with optional macOS Keychain, Bitwarden CLI, direnv, and environment variable support. MIT licensed.
Documentation: originalrgsec.github.io/himitsubako — full reference, CLI guide, backend configuration, and operations notes.
Who this is for
You are a solo developer. You have credentials — API keys, OAuth tokens, session cookies, PATs, database passwords — scattered across .env files, your shell history, a dotfile repo, and probably a sticky note somewhere. You want one consistent way to manage them that:
- Works offline, on your laptop, without a server
- Commits encrypted secrets to git so they're backed up and portable
- Lets you rotate the master key across every project in one command
- Doesn't require you to trust a SaaS vendor
- Doesn't require you to run a daemon
- Plays nicely with
pydantic-settings,direnv,docker-compose,launchd, andsystemd - Gets out of your way when you just want to read an environment variable
himitsubako is not for teams. If you need RBAC, audit logs, dynamic database credentials, or service-to-service authentication between multiple humans and services, you want HashiCorp's OpenBao or a commercial secrets manager. himitsubako deliberately stays small so it can stay correct.
The opinionated answer
Most solo-dev secrets pain comes from trying to homebrew a single pattern that covers everything. himitsubako picks one primary pattern and supports a handful of escape hatches.
Primary pattern: SOPS + age + direnv
- SOPS (CNCF) encrypts YAML/JSON files in place. Only values are encrypted — keys stay plaintext so git diffs stay readable.
- age is the modern, simple encryption tool SOPS uses as a backend. One keypair per developer, stored in
~/.config/sops/age/keys.txt. - direnv is a shell hook that loads environment variables when you
cdinto a project directory and unloads them when you leave.
Combined, you get: encrypted secrets committed to git, decrypted into your shell env automatically when you enter the project, loaded into Python via os.environ[...] like any other env var. No library import required for 90% of use cases.
When you need the library, himitsubako provides:
- A Python API for runtime credential lookup, rotation, and backend switching
- A CLI for interactive secret management (
hmb get,hmb set,hmb list,hmb delete,hmb status,hmb rotate) - A
pydantic-settingssource for declarative credential loading in Python applications - Opinionated scaffolding (
hmb init) that creates your age key, writes the.sops.yaml, generates an example.envrc, and encrypts an empty secrets file — all in one command - Additional backends for cases where SOPS doesn't fit: macOS Keychain (via
keyring), Bitwarden CLI (viabwsubprocess), environment variables
Backends
| Backend | Use when | Requires |
|---|---|---|
| sops (primary) | You want encrypted secrets in git, portable across machines, rotatable with one command | sops >= 3.8 + age binaries on PATH |
| keychain | You want OS-native storage that survives repo deletion, typically for personal long-lived tokens | macOS (Linux/Windows via keyring fallback) |
| bitwarden-cli | You already use Bitwarden and want your dev credentials in the same vault as your passwords | Bitwarden account, bw CLI on PATH |
| env | 12-factor simplicity, CI/CD pipelines, containers with env injection | Nothing |
| direnv helper | Automatic env var loading when entering a project directory | direnv binary on PATH |
Backends are selected per-credential via a project-level config file. Your devto_api_key can live in SOPS while your aws_access_key_id lives in macOS Keychain, all accessed through the same himitsubako.get(...) call.
60-second getting started
Install (will be available on PyPI after v0.1.0 ships):
pip install himitsubako
Initialize a project:
cd my-project/
hmb init
That creates: an age keypair (if you don't have one), .sops.yaml with your public key as the recipient, an empty encrypted .secrets.enc.yaml, a .envrc that auto-loads the secrets via direnv, and a .himitsubako.yaml config file.
Add a secret:
hmb set DEVTO_API_KEY
# prompts for value with masked input, encrypts, writes to .secrets.enc.yaml
Delete a secret:
hmb delete DEVTO_API_KEY
# prompts for confirmation; pass --force (alias --yes) to skip,
# or --missing-ok to exit 0 silently if the key is absent
Read it in Python:
import os
key = os.environ["DEVTO_API_KEY"] # loaded by direnv on cd
Or programmatically, if direnv isn't your style:
from himitsubako import get
key = get("DEVTO_API_KEY")
Diagnose configuration and backend availability:
hmb status
# prints config path, default backend, SOPS binary and age recipients,
# any router patterns, and a one-line availability check per backend
# (e.g. "sops: ok (sops 3.8.1)", "keychain: unavailable (...)").
# Pass --json to emit the same information as a single JSON object.
Rotate the master age key:
hmb rotate-key --new-key ~/.config/sops/age/keys.txt.new
# re-encrypts every .secrets.enc.yaml in configured projects with the new key
Integration with pydantic-settings
from pydantic_settings import BaseSettings, SettingsConfigDict
from himitsubako.pydantic import HimitsubakoSettingsSource
class AppSettings(BaseSettings):
model_config = SettingsConfigDict(extra="ignore")
devto_api_key: str
hashnode_pat: str
database_url: str
@classmethod
def settings_customise_sources(cls, settings_cls, init_settings,
env_settings, dotenv_settings, file_secret_settings):
return (init_settings, HimitsubakoSettingsSource(settings_cls), env_settings)
Now AppSettings() pulls from himitsubako first, then falls back to environment variables.
Why not ...?
Why not just use SOPS directly?
You can, and for many projects that's the right answer. himitsubako adds value when: (a) you want a consistent Python API across SOPS and other backends, (b) you need programmatic credential rotation mid-process, (c) you want pydantic-settings integration, or (d) you want the opinionated hmb init command that wires everything up correctly the first time. If none of those apply, use SOPS directly — that's exactly the primary pattern this library endorses.
Why not teller?
teller is excellent and covers a broader backend matrix than himitsubako. It's written in Go. If you're a Go developer or want a language-agnostic CLI, teller is probably a better fit. himitsubako is Python-native — you can import it, not just shell out to it — which matters if you want programmatic access in Python code or integration with pydantic-settings.
Why not HashiCorp Vault or OpenBao?
Vault is BUSL-licensed (not OSI-approved, restrictive terms for commercial use). OpenBao is the Linux Foundation's MPL-2.0 fork of Vault and is a great fit for team environments with runtime daemon, RBAC, audit log, and dynamic secret requirements. If your project has any of those needs, use OpenBao directly via hvac. himitsubako is deliberately scoped below that threshold — it's for the solo case where the overhead of running a daemon isn't justified.
Why not a commercial SaaS (Doppler, Infisical, 1Password Secrets Automation)? Those are legitimate options if you prefer a GUI for credential management, want vendor-managed rotation, or need network-accessible secrets for serverless workloads. himitsubako optimizes for offline-first, zero-cost, zero-trust-in-third-parties. Pick based on your constraints.
Why not Bitwarden Secrets Manager?
The Bitwarden Secrets Manager SDK is distributed under a proprietary license (Bitwarden SDK License Agreement v1) that is not OSI-approved and contains clauses that are incompatible with MIT-licensed distribution. The Bitwarden Password Manager CLI (bw) is GPL-3.0 and is safe to invoke via subprocess — and that's the integration himitsubako ships. If you want to store your dev credentials in your Bitwarden personal vault, the bitwarden-cli backend has you covered.
Why named "himitsubako"?
秘密箱 (himitsu-bako) literally means "secret box" in Japanese. Himitsu-bako are traditional puzzle boxes from the Hakone region of Japan, dating to the 1830s, that open through a sequence of sliding moves rather than a single external key. Secrets accessed through a sequence of moves (age keys, SOPS paths, CLI subcommands, direnv hooks) fit the metaphor better than "another library with 'key' or 'vault' in the name." Also, keymaster was taken on PyPI.
Prior art
The backend protocol and several of the backend implementations (age, env, keyring, bitwarden-cli, 1password) are inspired by and extracted from the secrets module in open-workspace-builder, a personal workspace tool under the same author. himitsubako exists because OWB's secrets module proved the abstraction was useful enough to extract into a standalone library that other projects (personal and otherwise) could adopt without depending on OWB.
Project status
v0.1.0 is in development. The backends listed above are the v0.1.0 scope. The library will be published to PyPI when v0.1.0 is ready. Until then, it can be installed directly from the git repository for testing.
See stories/ for the v0.1.0 roadmap (coming soon).
License
MIT. See LICENSE.
Contributing
Issues and pull requests welcome. Please read the (forthcoming) CONTRIBUTING.md before opening a PR. The project follows a strict license discipline for any new dependency — see the pre-install gate pattern documented in CONTRIBUTING.
Security
If you believe you've found a security vulnerability in himitsubako, please report it privately via GitHub Security Advisories rather than opening a public issue. Details in SECURITY.md (forthcoming).
Project details
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 himitsubako-0.9.0.tar.gz.
File metadata
- Download URL: himitsubako-0.9.0.tar.gz
- Upload date:
- Size: 284.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13ad5cfbf2d24619347485342d615c6350c34e96507c88b33c95552b070f1f1f
|
|
| MD5 |
d98de7395518f81676e4affb289e42ee
|
|
| BLAKE2b-256 |
981ddaaef9a01874a0e3f09ab2e58fcdd345be32650dd75e0ddb6c6c8ce72b7d
|
Provenance
The following attestation bundles were made for himitsubako-0.9.0.tar.gz:
Publisher:
release.yml on originalrgsec/himitsubako
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
himitsubako-0.9.0.tar.gz -
Subject digest:
13ad5cfbf2d24619347485342d615c6350c34e96507c88b33c95552b070f1f1f - Sigstore transparency entry: 1339985792
- Sigstore integration time:
-
Permalink:
originalrgsec/himitsubako@74899e16488dbda7eefe871f21fa06d8e9e41cb6 -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/originalrgsec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@74899e16488dbda7eefe871f21fa06d8e9e41cb6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file himitsubako-0.9.0-py3-none-any.whl.
File metadata
- Download URL: himitsubako-0.9.0-py3-none-any.whl
- Upload date:
- Size: 55.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32f18a9ee2aa3e0f1303df73105440de0e567c0d24c6385828112fc1c8d5aacc
|
|
| MD5 |
a0646304d7334881907893f0c46e03fb
|
|
| BLAKE2b-256 |
3e828d14306b294eecebc6e5d16c00cf0d80acc9ac089b0c90274cca50ab3f49
|
Provenance
The following attestation bundles were made for himitsubako-0.9.0-py3-none-any.whl:
Publisher:
release.yml on originalrgsec/himitsubako
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
himitsubako-0.9.0-py3-none-any.whl -
Subject digest:
32f18a9ee2aa3e0f1303df73105440de0e567c0d24c6385828112fc1c8d5aacc - Sigstore transparency entry: 1339985793
- Sigstore integration time:
-
Permalink:
originalrgsec/himitsubako@74899e16488dbda7eefe871f21fa06d8e9e41cb6 -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/originalrgsec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@74899e16488dbda7eefe871f21fa06d8e9e41cb6 -
Trigger Event:
push
-
Statement type: