---
type: plan
title: "credvault Ruby gem — implementation plan"
summary: 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.
tags:
  - type/plan
  - project/credvault
  - tool/vaultwarden
  - domain/credential-management
  - domain/ruby
scope: project
date: 2026-07-09
last_updated: 2026-07-09
related:
  - credvault-integration-master-plan
  - credvault-vaultwarden-setup-plan
source: 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.
