Skip to main content

Multi-backend credential abstraction for solo Python developers. SOPS+age, macOS Keychain, Bitwarden CLI, direnv, env vars.

Project description

himitsubako

ci docs PyPI

秘密箱 — "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, and systemd
  • 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 cd into 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:

  1. A Python API for runtime credential lookup, rotation, and backend switching
  2. A CLI for interactive secret management (hmb get, hmb set, hmb list, hmb delete, hmb status, hmb rotate)
  3. A pydantic-settings source for declarative credential loading in Python applications
  4. 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
  5. Additional backends for cases where SOPS doesn't fit: macOS Keychain (via keyring), Bitwarden CLI (via bw subprocess), 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

himitsubako-0.4.0.tar.gz (183.2 kB view details)

Uploaded Source

Built Distribution

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

himitsubako-0.4.0-py3-none-any.whl (39.0 kB view details)

Uploaded Python 3

File details

Details for the file himitsubako-0.4.0.tar.gz.

File metadata

  • Download URL: himitsubako-0.4.0.tar.gz
  • Upload date:
  • Size: 183.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for himitsubako-0.4.0.tar.gz
Algorithm Hash digest
SHA256 43866feb55d54799b033a98a7f6eb2bf31584abbe00852daed94c122b6310990
MD5 47b79604f3a94eff9b28cbab29519184
BLAKE2b-256 1c46f23a512694d0c22ef089d90ed47b41a62f3c0f17e64700886ead008b85ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for himitsubako-0.4.0.tar.gz:

Publisher: release.yml on originalrgsec/himitsubako

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file himitsubako-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: himitsubako-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 39.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for himitsubako-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c63f2835b0d730120663d642116f65560c3c3acf2d8de18803575d96a5dddb37
MD5 1f4d4f1c6ad7e9e26bac0ab9f2916795
BLAKE2b-256 c213c4b979ad616532bb5c8e15f5347c4e2ae53dacea2b109a3808513f42c7c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for himitsubako-0.4.0-py3-none-any.whl:

Publisher: release.yml on originalrgsec/himitsubako

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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