SecondBrain/credvault-gem-plan.md

4.3 KiB

type title summary tags scope date last_updated related source
plan credvault Ruby gem — implementation plan Implementation plan for the credvault Ruby gem wrapping the bw CLI with ensure/get/rotate/list/status operations, Sandi Metz ports-and-adapters design, sync-before-read, global locking, and a Minitest strategy.
type/plan
project/credvault
tool/vaultwarden
domain/credential-management
domain/ruby
project 2026-07-09 2026-07-09
credvault-integration-master-plan
credvault-vaultwarden-setup-plan
ruby-gems

credvault Gem — Implementation Plan

Home: ~/dev/ruby-gems/credvault/ (private GitHub repo, use git-context:repo-init). Follows the planka-api sibling pattern: client layer + CLI, Minitest, small objects.

Surface (frozen)

Commands: ensure, get, rotate, list, status. No delete, no export, no raw bw passthrough. JSON output, stable error codes (authentication_failed, vault_locked, collection_unavailable, credential_not_found, duplicate_managed_key, invalid_key, invalid_password_policy, bitwarden_command_failed, configuration_error). Org/collection IDs come from ~/.config/credvault/config.yml only — never CLI args.

Object model (ports and adapters)

CredentialKey, Credential, PasswordPolicy            # domain values
Operations::{Ensure,Get,Rotate,List}Credential(s)    # use cases, depend on Vault::Repository
Vault::Repository (interface) ← Vault::BitwardenRepository
Bitwarden::{Client, Session, ProcessRunner}          # adapter; Open3.capture3 argv arrays only
Presenters::JsonPresenter
exe/credvault

Non-negotiable behaviors (from critique)

  1. bw sync before every read path (ensure/get/rotate/list) — stale local cache otherwise breaks idempotency and creates duplicates.
  2. One global file lock serializes ALL bw invocations (shared appdata dir is unsafe for concurrent bw processes), not just find-then-create.
  3. Custom-field lookup is client-side: list collection items, parse JSON, filter on managed_key custom field (bw list --search doesn't cover custom fields).
  4. Duplicate managed_key → explicit error, never arbitrary pick. Cross-host races are detected-and-reported, not prevented.
  5. Auth lifecycle fully owned: login-state persisted in BITWARDENCLI_APPDATA_DIR; unlock --passwordfile per invocation; lock on exit; BW_SESSION only in process memory, never in logs/errors/output. Master-password file is mode-600 (owned by the invoking user; OS isolation is a future hardening option, not v1).
  6. Rotation is vault-only and says so: output must remind the caller that the live app still holds the old secret (two-phase workflow is the caller's job). v1 may refuse rotation without --acknowledge-live-impact style flag.
  7. Audit log (JSONL, ~/.local/state/credvault/audit.jsonl): timestamp, operation, key, result, item_id. Every get is logged. Never any secret material.
  8. Password generation via bw generate or Ruby SecureRandom under named policies (strong 32 full-charset default, compatibility no-special, passphrase 5 words).

Testing (Minitest)

  • Unit: fake Vault::Repository — ensure returns existing / creates missing / rejects duplicates; rotate fails on missing key and preserves metadata; list excludes secrets; no auth material in any output/error.
  • Adapter: fake ProcessRunner with canned bw JSON (login, unlock, sync, list, create, edit, lock) — primary seam, matches planka-api pattern.
  • High-fidelity: a fake bw shell script on PATH to exercise real Open3/argv/exit codes.
  • Integration (optional, tagged): disposable Vaultwarden container. Never production.

Build order & delegation

  1. Skeleton + config + error model + ProcessRunner/Client/Session (status end-to-end) — one sonnet agent. Blocker for the rest: defines the interfaces.
  2. In parallel (disjoint operations against slice-1 interfaces, merged before review):
    • get + list (read paths, sync + lock + client-side filter) — sonnet.
    • ensure + rotate + audit log + policies — sonnet.
  3. Security/quality pass — opus review + /code-review before first production use. Each slice ships with its tests green; design decisions are all above, so agents don't improvise.