Changelog¶
Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.7.0] - 2026-04-14¶
Sprint 7 exits maintenance mode with Google OAuth support and a P1 SOPS
bug fix. Three stories, eight points. New google-oauth composite
backend type, hmb rotate plugin for OAuth refresh tokens with device
flow and browser flow, and SOPS subprocess env propagation for non-default
key paths.
Added¶
- Google OAuth credential backend (HMB-S030). New
google-oauthcredential type in.himitsubako.yamlgroups the three Google OAuth secrets (client_id, client_secret, refresh_token) as one logical credential. Delegates storage to any existing backend (sops,env,keychain,bitwarden-cli). Python API exposeshimitsubako.get_google_credentials(key)which returns a livegoogle.oauth2.credentials.Credentialsobject ready forgoogle-api-python-clientconsumers. CLI:hmb get <cred>emits JSON with all three fields,hmb set <cred>prompts for each field separately. Individual constituent keys remain accessible via the normalhmb get <key>/ Pythonget(key)paths. Optional dependenciesgoogle-auth>=2.49.0,<3.0,!=2.49.2andgoogle-auth-oauthlib>=1.3.1,<2.0under the new[google]extras. hmb rotateGoogle OAuth plugin (HMB-S032). When the resolved target is a google-oauth credential,hmb rotateruns an OAuth authorization flow to obtain a fresh refresh token and writes it back to the configured storage backend automatically. Two modes: device flow (default) prints a verification URL and user code that works over SSH, in containers, and any environment without a browser;--browserflag usesInstalledAppFlowwith a localhost callback for desktop use. New audit logmethodfield records which flow was used. Device flow errors include a remediation hint pointing at--browserwhen the GCP OAuth client is not configured for device flow.
Fixed¶
- SopsBackend now propagates
age_identityandconfig_file(HMB-S031)..himitsubako.yaml'ssops.age_identityis now passed to the sops subprocess asSOPS_AGE_KEY_FILEenv var, and a newsops.config_fileoption is passed as--config. Without this, consumers with non-default key paths or.sops.yamllocations hit "identity did not match any of the recipients" errors even with a correctly populated config. Theage_identityfield is nowstr | Nonewith a default ofNone(SOPS uses its own default resolution). Tilde expansion applied uniformly tosecrets_file,age_identity, andconfig_file. Unblocks home-ops HOP-S106 secrets migration.
Changed¶
write_audit_entrygains an optionalmethodfield. JSONL schema evolution is backward-compatible; legacy entries omit the field. Used today byhmb rotateto recordmethod: deviceormethod: browseron google-oauth rotations.
[0.6.0] - 2026-04-11¶
Sprint 6 clears the remaining backlog with docs polish and a CI safety net. Three stories, five points, no library code changes. The backlog is empty after this sprint; the project enters maintenance mode.
Added¶
- TestPyPI dry-run workflow (HMB-S027). New
.github/workflows/testpypi.ymltriggered on pre-release tags (v*-rc*). Mirrors the production release workflow but publishes to TestPyPI, giving every release a low-risk rehearsal before the real PyPI publish. Same SHA-pinned actions, Trusted Publishers OIDC, PEP 740 attestations, and required-reviewer gate. Tag patterns are mutually exclusive withrelease.yml. Motivated by the v0.3.0 → v0.3.1 incident where a Docker image pull failure was only caught on the production tag.
Changed¶
- Attestation docs use version placeholders (HMB-S028). All
hardcoded
v0.4.0references indocs/security/attestations.mdverification commands replaced with<VERSION>placeholders. An admonition callout explains the substitution convention and thevprefix difference between git refs and pip arguments. - Docs site aligned with OWB brand identity (HMB-S029). Palette
switched from stock indigo to the originalrgsec gold (#D4A017) /
royal blue (#3A5BA0) scheme with dark-first slate mode. System
sans-serif font stack (
font: false), profile-avatar logo and favicon, gold glow effects on navigation, and custom admonition styling. Brand CSS ported from open-workspace-builder; layout classes (hero, feature cards, buttons) excluded.
[0.5.0] - 2026-04-11¶
Sprint 5 expands distribution reach beyond PyPI and closes the macOS CI gap. Three stories, seven points, no library code changes.
Added¶
- macOS runner for keychain integration tests (HMB-S024). The CI
workflow now includes a
test-macosjob onmacos-latest(arm64) that exercises the keychain backend against a real macOS login Keychain. Uses SHA256-verified sops + age darwin-arm64 binaries, single Python 3.13 to control 10x macOS runner cost, and a runtime arch assertion to fail clearly if the runner image shifts to x86_64. Alluses:directives are SHA-pinned per supply-chain policy. - conda-forge recipe (HMB-S025). A
recipe.yaml(v1 format, CEP 13) for conda-forge was written and submitted as conda-forge/staged-recipes#32938. Uses PyPI sdist as source,noarch: python, and lists all five runtime dependencies. Merge is controlled by conda-forge maintainers. Local copy atconda/recipe.yaml. - Homebrew tap (HMB-S026). A new repo
originalrgsec/homebrew-tap
provides
brew install originalrgsec/tap/himitsubako. The formula passesbrew audit --newandbrew testclean. Includes all transitive Python dependencies as resource stanzas, depends on Rust for building pydantic-core from sdist, and recommendssops+age.
[0.4.0] - 2026-04-11¶
Sprint 4 closes the loops still open after the v0.3.1 PyPI publish: a credential rotation command with an append-only audit trail, a live published docs site at originalrgsec.github.io/himitsubako, and PEP 740 / Sigstore provenance attestations on every release artifact. Three stories, seven points, no new runtime dependencies.
Added¶
hmb rotate <credential>— credential value rotation with audit log (HMB-S021). A new CLI command that rotates a single credential's value throughBackendRouterand appends a JSON Lines entry to~/.himitsubako/audit.log. Distinct fromhmb rotate-key, which rotates the age master key; the help text of both commands calls out the distinction in the first line. Reads the new value from stdin (pipe) or--value-from-file, and refuses a--valueargv flag entirely — secrets on the command line are not supported by design. The audit log is created with mode 0600 inside a 0700 parent directory, usesO_APPENDatomic single-writes so concurrent rotations from separate processes interleave cleanly, and passes everyerrorfield through the shared token redaction helper (_redaction.redact_tokens, lifted out of the Bitwarden backend in the same commit) before writing to disk. On success the command printsrotated <credential>; on audit-write failure after a successful rotation it emits a stderr warning and still exits 0, because rolling back a successful rotation to preserve a log line is the wrong trade-off.- Live documentation site (HMB-S022). The mkdocs-material site
built in v0.3.0 is now deployed to
originalrgsec.github.io/himitsubako
on every push to
mainvia a new SHA-pinned workflow at.github/workflows/docs.yml. Docs track the tip ofmain, not the last release tag, so doc-only fixes reach users without waiting for the next version bump. TheDocumentationURL inpyproject.toml[project.urls]has been flipped from the GitHub README anchor to the live Pages URL, so PyPI metadata for v0.4.0 links users to the real site. README carries a newdocsbadge alongside the existingciandPyPIones. - PEP 740 / Sigstore provenance attestations on every PyPI release
(HMB-S023).
attestations: trueis now set on thepypa/gh-action-pypi-publishstep inrelease.yml, and the publish job carries an additionalattestations: writepermission scoped to itself. Every wheel and sdist on pypi.org/project/himitsubako/ from v0.4.0 onward ships with an attached Sigstore attestation bundle bound to the exact GitHub Actions release run that produced it. Downstream users can verify a release with eithergh attestation verifyorpython -m sigstore verify identity; the full guide lives in the docs atsecurity/attestations.md. The threat model adds T-034 (attestation binding misconfiguration), T-038 (downstream non-verification), and mitigations M-027 and M-031.
Changed¶
src/himitsubako/backends/bitwarden.pyimportsredact_tokensfromsrc/himitsubako/_redaction.py. The 40+-char base64 regex that redacts BW_SESSION tokens from Bitwardenbwstderr (HMB-S009 review) has been lifted into a shared helper module so the audit log can reuse it without creating a dependency cycle. Behavior is unchanged; regression-guarded by the existing Bitwarden redaction tests.
Security¶
- T-034, T-035, T-036, T-037, T-038 added to the threat model.
v0.4.0 introduces three new attack surfaces (
hmb rotateaudit log, GitHub Pages deployment, Sigstore attestations) and each is reviewed in a new "v0.4.0 Release Polish Surface Review" section ofthreat-model.md. Highest residual risks are T-038 (downstream non-verification, accepted with documentation mitigation) and T-036 (audit log tampering, accepted as a local log whose threat model is "evidence for me, not against me"). T-035 is mitigated to Low by job-scoped deploy permissions.
Operator actions required before tagging v0.4.0¶
- GitHub Pages enablement. Flip Settings → Pages → Source to
"GitHub Actions" on
github.com/originalrgsec/himitsubako. The workflow cannot enable this itself; without the flip, the firstdocs.ymlrun will fail atactions/deploy-pageswith a missing Pages site error. Completed 2026-04-11 before the sprint-close merge. - TestPyPI dry run — skipped by operator decision. The Sprint 4
plan called for a pre-release tag (
v0.4.0-rc.1) against TestPyPI before the realv0.4.0tag. This step was skipped during sprint close for three reasons: (a) the currentrelease.ymltrigger regex excludes pre-release tags, so exercising it would require a separate TestPyPI workflow and a second Trusted Publisher binding; (b) the attestation change is a single line on an already-tested workflow, not a new release surface; (c) thepypi-releaseenvironment's required-reviewer gate is the real safety net — a failed attestation publish would pause for human approval rather than silently ship bad artifacts. A dedicated TestPyPI dry-run workflow may be added as HMB-S027 in a future sprint if the need arises; for v0.4.0 the risk is accepted.
Test count and coverage¶
- Tests: 190 → 210 (+20 covering
audit.pyandrotate_credential) - Coverage: 85.80% → 86.13%
- All four quality gates green on every commit:
ruff check,ruff format --check,mypy,pytest.
[0.3.1] - 2026-04-11¶
First public PyPI release. The v0.3.0 tag exists in git history but
was never published to PyPI: the release workflow's publish step
failed at docker pull because pypa/gh-action-pypi-publish is a
Docker container action whose registry image is tagged by release
version, not by commit SHA. The action wrapper attempted to pull
ghcr.io/pypa/gh-action-pypi-publish:<commit-sha> and the registry
returned manifest unknown because no such tag exists. Nothing
reached pypi.org during the failed run.
Fixed¶
- Release workflow —
pypa/gh-action-pypi-publishswitched from SHA-pin to version tag (@v1.13.0). Documented as the only Docker-container-action exception to the project's SHA-pinning policy. PyPA does not move version tags, the action is published by the official Python Packaging Authority (also the operator of pypi.org), and v1.13.0 is well past the 7-day quarantine window — the trust delta vs SHA pinning is small in practice. The header comment in.github/workflows/release.ymldocuments the exception in full so future maintainers do not "fix" it back.
No library code changes between v0.3.0 and v0.3.1; the entire diff is the one-line action pin in the release workflow plus this CHANGELOG entry. Everything in the [0.3.0] section below also applies to v0.3.1.
[0.3.0] - 2026-04-11¶
Sprint 3 closes himitsubako's path to PyPI. Seven stories land together:
the CRUD closeout (hmb delete, hmb status), a real integration test
suite that surfaced and fixed a latent SOPS encryption bug that had
broken every hmb set since v0.1.0, a CI pipeline with SHA-pinned
actions and verified sops+age binaries, a full mkdocs-material
documentation site, local-only integration tests for keychain / bw /
direnv, and the release workflow + Trusted Publishers OIDC binding
that publishes to PyPI on every v*.*.* tag.
Added — CLI commands¶
-
HMB-S018 —
hmb deleteCLI command. Removes a secret from the configured backend with a confirmation prompt (--force/--yesto skip,--missing-okfor idempotent cleanup). Routed dispatch names the resolved target backend in the prompt rather than the router wrapper. Exit codes:0success,1not found,2backend error (env backend read-only, keychain denied, etc.). -
HMB-S019 —
hmb statusdiagnostic command. Read-only introspection of the active configuration: config path, default backend, SOPS binary + age recipients from.sops.yaml, theBackendRoutertable in declaration order, and a per-backend ping-style availability check.--jsonemits a single JSON object for scripting. Never reads, writes, or enumerates any credential. Also adds a publicKeychainBackend.check_availability()method.
Added — testing and infrastructure¶
-
HMB-S013 — CI-runnable integration test suite. New
tests/integration/tree with 26 real-binary tests for SOPS and env backends,BackendRouterdispatch, and the fullhmb init → set → get → list → delete → statusCLI flow. Excluded from the defaultuv run pytestvia--ignore=tests/integration; run explicitly withuv run pytest tests/integration/. -
HMB-S014 — GitHub Actions CI pipeline.
.github/workflows/ci.ymlruns ruff check, ruff format check, mypy, unit tests with a--cov-fail-under=80gate, and the S013 integration subset on every push tomainand every PR. Matrix: Python 3.12 and 3.13 onubuntu-latest. Everyuses:reference is SHA-pinned;sopsv3.12.2 andagev1.3.1 are installed from upstream releases with SHA256 verification. Top-levelpermissions: contents: read, concurrency group cancels stale runs, no repo secrets consumed. -
HMB-S020 — local-only integration tests. New test modules for the backends that cannot run in default CI:
test_keychain_real.py(macOS login keychain with UUID-prefixed service and finalizer teardown),test_bitwarden_real.py(gated on an explicitHMB_TEST_BW_SESSIONenv var with per-test folder isolation), andtest_direnv_real.py(realdirenv allow/exec/denyisolation, covering duplicate-marker refusal and shlex-quoted tricky filenames end-to-end). 14 new tests total.
Added — release infrastructure¶
- HMB-S016 — PyPI publish preparation.
pyproject.tomlversion bumped to0.3.0;src/himitsubako/__init__.py__version__matches.project.urlsnow declares Documentation and Changelog URLs. NewCONTRIBUTING.md(development setup, running unit vs integration tests, dependency license discipline, release checklist) andSECURITY.md(supported versions, private vulnerability reporting via GitHub Security Advisories, in-scope and out-of-scope boundaries, regression-guarded defense list). New.github/workflows/release.ymltriggered on finalv*.*.*tags: verify → build → publish jobs. Publish uses Trusted Publishers OIDC (pypa/gh-action-pypi-publish@v1.13.0, SHA-pinned) bound to thepypi-releaseGitHub Actions environment with a required- reviewer approval gate. No long-lived PyPI API tokens. A build-job guard asserts that the git tag,pyproject.tomlversion, andhimitsubako.__version__all agree before any artifact is produced. Local smoke test before this commit: wheel and sdist built viapython -m build,twine check dist/*PASSED, scratch venv install of the wheel reportshmb, version 0.3.0.
Docs¶
- HMB-S015 — mkdocs-material documentation site. New
docs/tree plus top-levelmkdocs.ymlconfiguring thematerialtheme with a light/dark palette toggle and tabbed navigation. Pages: landing, getting-started walkthrough, full CLI reference, configuration andBackendRouterguide, one backend page each for SOPS / env / keychain / bitwarden-cli, integration pages for pydantic-settings and direnv, a user-facing security summary, a "why not ..." section, and a changelog page that rendersCHANGELOG.mdvia the snippets extension.uv run mkdocs build --strictis green in 0.3 s. Deploy target intentionally deferred — the build is the success criterion; GitHub Pages / Read the Docs / Cloudflare Pages selection is a follow-up decision.
Fixed¶
- HMB-S013 (discovered by new integration tests) —
SopsBackend._encryptcould not encrypt against a default-init'd vault. The backend writes to atempfile.mkstemp(suffix=".yaml")tempfile and then callssops --encrypt --in-place <tmpfile>. sops applies.sops.yaml'screation_rulespath_regexagainst the file it's operating on — which is the tempfile name, not.secrets.enc.yaml— so sops aborts witherror loading config: no matching creation rules found. This broke everyhmb set/hmb delete/ rotate path in v0.1.0 through v0.2.0; unit tests did not catch it because subprocess was mocked. The fix passes--filename-override <real_secrets_file>so sops applies the creation_rules against the real target path. Requires sops >= 3.8.0 (the version that introduced--filename-override); the README and backend table now document the minimum. A unit regression test inTestSopsBackendFilenameOverridepins argv ordering so the flag cannot silently drop.
Chore¶
- Codebase-wide
ruff formatpass so the CI format-check stays green. - mypy strict pass over
src/; one pre-existing type narrowing incli/secrets.py::list_secretsannotated asSecretBackend | None. - Register
bitwardenanddirenvpytest markers to support the S020 opt-in local-only suites and the S014 CI filter.
0.2.0 - 2026-04-11¶
Sprint 2 ships the alternate-backend track and the per-credential routing dispatcher that ties them together. v0.2.0 turns himitsubako from "a SOPS wrapper" into "a multi-backend credential abstraction" without breaking any v0.1.x configuration.
Added — backends¶
-
HMB-S007 — first-class environment variable backend.
EnvBackend(prefix: str = "")inhimitsubako.backends.env. Read-only by design (set/deleteraiseBackendError). With a configured prefix,get("DB_PASSWORD")resolvesMYAPP_DB_PASSWORDandlist_keys()returns matching variables with the prefix stripped. The internal_EnvFallbackBackendshim is removed; no-config fallback now returns the realEnvBackend().hmb listagainst an unprefixed env backend emits a stderr warning so users do not mistake inherited shell credentials for app secrets. -
HMB-S008 — macOS Keychain backend.
KeychainBackend(service: str)inhimitsubako.backends.keychain. Wraps thekeyringlibrary (optional[keychain]extra).list_keys()raisesBackendErrorunconditionally because the keyring API does not expose enumeration — the CLI catches this and prints a friendly "this backend does not support listing" message. Insecure-backend deny-list at first call: the resolvedkeyring.get_keyring()is rejected if its MRO matchesNull,PlaintextKeyring,EncryptedKeyring, orfail.Keyring, preventing both direct and subclass-based bypass on misconfigured Linux hosts. -
HMB-S009 — Bitwarden CLI subprocess backend.
BitwardenBackend(folder, bin, unlock_command)inhimitsubako.backends.bitwarden. Invokes thebwsystem binary; nobitwarden-sdkPython dependency (the SDK is non-OSI; see the COR-S037 retrospective). Three modes: - Strict (default):
BW_SESSIONmust be set; the library never prompts. Missing/empty session raises a clearBackendError. - Pinned bin:
bin=constructor arg orHIMITSUBAKO_BW_BINenv var pins an absolute path, mitigating T-005 (PATH hijack ofbw). - Shell-out unlock:
unlock_commandruns a configured command, captures stdout as the master password, pipes it tobw unlock --rawviaBW_PASSWORDenv var (NOT argv) to obtain a session token used in-memory only. Token is never written to disk or logged. Hardened secrecy:BW_SESSIONis never logged or interpolated into errors; the_raise_friendlyhelper redacts any base64 token-like string frombwstderr before re-raising. All subprocess calls use a 30s timeout matching SOPS.
Added — dispatcher¶
- HMB-S012 —
BackendRouterper-credential routing. Newhimitsubako.router.BackendRouterimplementsSecretBackendand dispatches each key to the configured backend. Resolution order: exact match inconfig.credentials→ first matching glob (declaration order,fnmatch.fnmatchcase) →default_backend.list_keys()aggregates across all backends in use; backends that raise onlist_keys(keychain) are caught, logged to stderr as a partial- failure warning, and skipped. Backend instances are cached on first construction. Bothcli/secrets.pyandapi.pywere refactored to return a router rather than a single backend, so all CLI commands and Python API calls transparently support per-credential routing.
Backward compatibility: configs with no credentials: section
behave identically to v0.1.x. All v0.1.x tests pass unchanged.
Added — integrations¶
-
HMB-S010 — direnv helper. New
himitsubako.direnvmodule withgenerate_envrc()andupdate_envrc(). The managed block is delimited by# --- himitsubako start ---and# --- himitsubako end ---markers;update_envrcpreserves any user lines outside the markers and replaces the managed block in place. Idempotent. Refuses to operate on a.envrcwith duplicate markers (would silently corrupt user lines between blocks). Thesecrets_filepath isshlex.quoted before interpolation into the eval line so paths with spaces or shell metacharacters cannot break the eval. Newhmb direnv-exportCLI command regenerates the managed block on demand.hmb inituses the new helper for the initial.envrc;hmb setcallsupdate_envrc()best-effort after a successful sops write. -
HMB-S011 — pydantic-settings source.
HimitsubakoSettingsSourceinhimitsubako.pydanticextendsPydanticBaseSettingsSourceto pull each settings field from a himitsubako backend or router. Use insettings_customise_sourcesto mix backends in a single settings model —db_passwordfrom SOPS,oauth_client_secretfrom Keychain, routed by.himitsubako.yaml. Recommended source order documented in the module:init kwargs > env > himitsubako > dotenv > file_secret > defaults. Optional[pydantic-settings]extra; ImportError converts to a clear BackendError naming the install command.
Config schema additions¶
HimitsubakoConfig.credentials: dict[str, CredentialRoute]— new optional section for per-credential routing.BitwardenConfig.bin: str | NoneandBitwardenConfig.unlock_command: str | None— for HMB-S009.extra=forbidonCredentialRouterejects unknown fields.
Security¶
- T-005 mitigated (HMB-S009):
bwbinary path can be pinned viaHIMITSUBAKO_BW_BINor config to prevent PATH hijack. - T-007 partially mitigated (HMB-S009):
BW_SESSIONis never logged or interpolated into error strings;bwstderr is sanitized to redact base64 token-like substrings before re-raising. The OS-level visibility of env vars to same-user processes remains an accepted limitation of the env-var session model. - T-008 mitigated (HMB-S009): 30-second subprocess timeout on all
bwcalls, matching the SOPS pattern from v0.1.1. - T-020 mitigated (HMB-S008): Keychain access delegates to the OS via the keyring library; first access from a new binary triggers a Touch ID / password prompt on macOS.
- T-022 mitigated via M-014 (HMB-S009): Documentation guidance on
safe
unlock_commandchoices; the library does not log unlock_command output. - T-023 mitigated via M-015 (HMB-S008): Insecure-backend deny-list at first call, with MRO-based subclass detection.
Test state¶
- 80 → 156 passing tests (+76, +95%)
- Coverage 86.27% → 84% (broader surface, same density)
- ruff clean
- Code review (python-reviewer): 2 CRITICAL + 4 HIGH findings, all fixed before tag. Findings included BW_SESSION leak via stderr passthrough (now redacted), BW_PASSWORD env var defense-in-depth cleanup, keychain MRO bypass (now MRO-checked), direnv duplicate markers (now refused), direnv shlex injection (now quoted).
0.1.1 - 2026-04-11¶
Hardening release. Closes the four known limitations flagged at v0.1.0 ship time (threat-model items T-001, T-004, T-010, T-018; ADR open question OQ-4). No new public surface beyond the additions listed below; v0.2.0 alternate backends still land in the next sprint.
Added¶
SopsBackend(secrets_file, sops_bin=None)— new optionalsops_binargument pins thesopsbinary path instead of relying on PATH lookup.HIMITSUBAKO_SOPS_BINenvironment variable — when set and non-empty, takes precedence over both the constructor argument and the config field.sops.binfield in.himitsubako.yaml— optional path to a non-PATHsopsbinary, plumbed through both the CLI (hmb get/set/list/rotate-key) and the Python API. Defaults toNone, preserving v0.1.0 behavior.hmb get KEY --reveal(-r) — boolean flag that authorizes printing the decrypted value to a TTY. When stdout is a pipe or redirect, the flag is optional and the value is printed as before, so$(hmb get KEY)andhmb get KEY | pbcopycontinue to work unchanged.
Changed¶
- All
subprocess.runcalls inSopsBackendnow passtimeout=30s(hardcoded module constant_SOPS_TIMEOUT_SECONDS). Timeouts are caught and re-raised asBackendError("sops", "sops <decrypt|encrypt> timed out after 30s"). SopsBackend._encryptwrites the temp plaintext file with mode0o600from creation (viaos.fchmod), and re-asserts0o600on the destination file after the atomic rename. The destination mode is now independent of the caller's umask.hmb get KEYrunning against a TTY without--revealexits 1 with a stderr message pointing at the flag. This is a deliberate behavior change from v0.1.0; scripts that piped output are unaffected.
Security¶
- T-001 mitigated. A malicious
sopsbinary earlier in PATH can no longer silently shadow the intended one; operators can pin an absolute path via env var or config. - T-004 mitigated. A hung or hostile
sopssubprocess can no longer block himitsubako indefinitely; the 30-second timeout caps the worst case. - T-010 mitigated.
.secrets.enc.yamlis now mode0600regardless of umask, narrowing the local-disclosure window on multi-user systems. - T-018 mitigated.
hmb getno longer prints plaintext to a terminal by default. Shoulder-surfing and terminal scrollback exposure now require an explicit--revealopt-in per invocation. - ADR open question OQ-4 closed: TTY-aware reveal gate selected over config-driven defaults to keep script ergonomics intact while protecting interactive sessions.
0.1.0 - 2026-04-11¶
First rescoped release: the SOPS track ships standalone.
v0.1.0 was originally planned as the full multi-backend release (SOPS, macOS Keychain, Bitwarden CLI, env, direnv, pydantic-settings source, docs site, PyPI publish). After Sprint 1 shipped the SOPS track end-to-end, the version was rescoped down to "SOPS works standalone" so that later multi-backend work lands in v0.2.0 and the public PyPI release lands in v0.3.0. See the project PRD for the new phasing.
This is not yet on PyPI. The public release target is v0.3.0 (HMB-S016).
Added¶
SecretBackendprotocol —@runtime_checkablestructural typing contract withget,set,delete,list_keys, and abackend_nameproperty. Norotatemethod in the protocol; credential-level rotation isset(key, new_value)and age-key rotation is thehmb rotate-keyCLI command.HimitsubakoConfigpydantic model with frozen sub-configs (SopsConfig,KeychainConfig,BitwardenConfig,EnvConfig). v0.1.0 only routes thesopsbackend; otherdefault_backendvalues parse but fail fast at CLI dispatch.find_config()walks up the directory tree to locate.himitsubako.yaml.load_config()parses YAML viayaml.safe_loadand wraps all errors inConfigError.- Error hierarchy:
HimitsubakoError > BackendError > SecretNotFoundError, plusConfigError. - SOPS + age backend:
get,set,delete,list_keysviasopssubprocess; atomic writes via tempfile-plus-rename inlined in the backend. hmb init— creates an age keypair (viaage-keygen) if absent, writes.sops.yaml,.envrc,.secrets.enc.yaml(SOPS-encrypted empty file), and.himitsubako.yaml. Idempotent;--forceto overwrite.hmb get <key>— prints the decrypted value to stdout. Exits 1 with a message to stderr if the key is not found.hmb set <key>— masked prompt by default;--value <v>for scripting.hmb list— prints all key names managed by the configured backend.hmb rotate-key --new-key <path>— updates.sops.yamlrecipients and re-encrypts the project's SOPS file viasops updatekeys.--dry-runprints the plan without executing.- Python API:
himitsubako.get(),set_secret(),list_secrets()with config-driven backend resolution. Fallback chain:.himitsubako.yaml>.sops.yaml> read-only env var fallback. - 16 story files in
docs/stories/covering the v0.1.0 → v0.3.0 roadmap (Sprint 1 through Sprint 3).
Security¶
SecretNotFoundErrorexcludes credential key names from its string representation; the missing key is still accessible programmatically viaerr.key.- Broad
exceptin config loading narrowed toValueError | TypeError. types-PyYAMLadded for static analysis confidence on YAML parsing paths.- SOPS backend tests assert that credential values do not appear in captured output.
hmb setmasks prompted input viaclick.prompt(hide_input=True).yaml.safe_load()used exclusively (noyaml.loadoryaml.unsafe_load).
Known Limitations (v0.1.1 hardening targets)¶
hmb getprints the full plaintext value to stdout with no--revealgate. Redacted-by-default output is the top v0.1.1 priority..secrets.enc.yamlis written with the default umask (usually 0644) rather than an explicit 0600 chmod.- The SOPS backend resolves
sopsvia PATH only and does not set a subprocess timeout.
Deferred¶
| Feature | Deferred to | Story |
|---|---|---|
| First-class env backend (CLI-routable) | v0.2.0 | HMB-S007 |
| macOS Keychain backend | v0.2.0 | HMB-S008 |
| Bitwarden CLI backend | v0.2.0 | HMB-S009 |
| direnv integration helper | v0.2.0 | HMB-S010 |
| pydantic-settings source | v0.2.0 | HMB-S011 |
| Per-credential backend routing | v0.3.0 | HMB-S012 |
| Real-binary integration tests | v0.3.0 | HMB-S013 |
| CI pipeline (GitHub Actions) | v0.3.0 | HMB-S014 |
| mkdocs documentation site | v0.3.0 | HMB-S015 |
| PyPI publication | v0.3.0 | HMB-S016 |