16 Commits

Author SHA1 Message Date
leetcrypt 639f3db3af docs: correct hero claim — "no plaintext at rest", not "no trace on disk"
ci / build-test (push) Has been cancelled
gopass writes GPG-encrypted blobs to disk and the backup gate writes sealed
.age bundles; only *decrypted* secrets are RAM-only. Fix the tagline, ASCII
box, banner alt-text, and banner.svg prose to state the defensible claim.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-17 23:52:58 -07:00
leetcrypt 2459cd4fd8 rotate: LIVE-REAL proof level + execute-time proof gate (M2)
Add ProofLiveReal (highest level, no driver yet) and enforce it at
execute time: rotate --execute only runs drivers with proof >= LIVE-VM
(MinExecuteProof). MOCK-ONLY/UNPROVEN drivers are skipped before any
provider call unless --allow-mock-proven is passed. ExecuteResult gains
Skipped/SkipReason; the CLI reports and audit-logs skips separately from
failures. Closes the last structural gap where an emulator-only driver
could touch a real credential unattended. Covered by execute_gate_test.go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-15 11:29:07 -07:00
leetcrypt 0be82839b9 docs: add ROADMAP + STATE snapshot, refresh CLAUDE.md dev state (M0)
Record the 2026-07-14 development snapshot and the roadmap to a deployable
rotation system. Update CLAUDE.md "Where development is" — the stale entry
claimed zero rotation drivers; reality is 22 drivers + a wired Mode A execute
spine (8 LIVE-VM / 14 MOCK-ONLY). Gitignore the incredigo-map.html build
artifact and the local rotation runbook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-15 11:00:29 -07:00
leetcrypt 88e603f3c0 rotate: Vercel token + SendGrid API-key drivers (create→verify→revoke)
Two more provider-API rotation drivers for the vibe-coder credential surface,
both following the create-new → verify → revoke-old pattern with the
self-contained blob form <provider>://<host>/?id=&secret=.

- vercel: POST /v3/user/tokens mints a new bearer; Verify lists tokens and
  asserts the new id is present; RevokeOld DELETEs the old token by id.
- sendgrid: clones the old key's scopes (GET /v3/api_keys/<id>) onto the new
  key so rotation is a faithful replacement, not a privilege downgrade; Verify
  is a GET /v3/scopes auth proof; RevokeOld DELETEs the old key by id.

Both ship Bearer-enforcing httptest emulators that prove a real cutover (old
credential dead, new alive after RevokeOld) plus a leak check. Recorded as
MOCK-ONLY in proofs.go + docs/ROTATION-PROOFS.md (no self-host). go build/vet
clean; 77 rotate tests pass, -race clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-20 12:12:20 -07:00
leetcrypt 80916c817a rotate: Resend API-key driver (create→verify→revoke)
First of the vibe-coder API-rotation batch. Resend mints a new API key
authenticated by the old one (POST /api-keys), verifies it by listing keys
with the new Bearer token and asserting its id is present, then deletes the
old key by id (DELETE /api-keys/<id>). Self-contained blob
resend://host/?id=&secret= keeps the token off Identity/Meta/logs/argv.

httptest emulator enforces Bearer validity (key authenticates only while it
exists) so the test proves a real cutover: old dead, new alive, plus a leak
check. Recorded MOCK-ONLY in proofs.go + ROTATION-PROOFS.md (no self-host).

Note: GitHub PAT is intentionally NOT an API Rotator — GitHub has no
create-token API (classic Authorizations API removed 2020; fine-grained PATs
are UI-only), so a PAT belongs in the guided/worklist path, not here.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-20 12:01:08 -07:00
leetcrypt 851890b119 pwstore: pass/gopass + macOS Keychain in-place adapters
Group 2 of the manager wire-up — two CLI ItemUpdaters:

  pass/gopass: the user's own password-store as a rotation source. Reads
  line-1=password + key:value metadata via `<bin> show`; updates in place by
  piping the full body to `<bin> insert -m -f` (off-argv), preserving metadata
  across the password swap. gopass enumerates via `ls --flat`, pass by walking
  the store dir; --pass-prefix scopes a subtree.

  keychain: macOS internet passwords via the `security` CLI. Pure-exec (no
  build tag) so it unit-tests cross-platform via a fake bin; Available() is
  false off darwin. Read off-argv (find-internet-password -w); write is
  delete+add with -w on argv (CLI limitation, documented like 1password).

Both MOCK-ONLY (fake-binary unit tests + leak checks); recorded as DATA in
BROWSER-ROTATION.md §8. 153 tests green, vet clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-20 11:39:20 -07:00
leetcrypt 4f5939a3e3 pwstore: generic csvManager for 8 password-manager CSV adapters
One data-driven csvManager (layout = header + record func) covers
edge/brave (chrome layout), safari, lastpass, dashlane, protonpass,
nordpass, roboform, and read/guide-only enpass — replacing 8 near-identical
files. Export is header-name driven (parseLoginCSV + new aliases); import
stages a per-manager-layout CSV on tmpfs and shreds it, gated by --allow-csv.
Notes now round-trip through Meta. Reimporter exposes per-manager re-import
hints. Column layouts are MOCK-ONLY (unit-validated) on the same LIVE-VM
staging machinery; recorded as DATA in BROWSER-ROTATION.md §8.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-20 11:31:28 -07:00
leetcrypt 6bd6aefbe8 docs: bitwarden adapter promoted MOCK-ONLY → LIVE-VM
Proven end to end (scan → plan → commit → restore-from-backup) against real
bw 2026.5.0 driving a self-hosted Vaultwarden 1.36; fake account minted via
real registration crypto. No adapter code changed. Only 1Password remains
MOCK-ONLY (needs a real account, no self-host). Proof recorded as DATA in §8.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-19 19:09:12 -07:00
leetcrypt 694f242a9e docs: chrome/firefox CSV staging proven LIVE-VM (tmpfs + shred)
lab-provision-browsercsv.sh + csv-commit-probe.py drive the real `passwords
commit --allow-csv` path in the sandbox VM and assert the security-critical,
unattended machinery: the new-password CSV is written to a real tmpfs
(/dev/shm), in the correct per-browser column layout, carrying the staged
strong passwords with the OLD ones absent, then securely shredded once the
human confirms — and the user's own export CSV is left untouched. The final
re-import into the browser remains a human step (no programmatic import API),
so the proof is scoped to staging, not end-to-end browser write.

No adapter code changed; only the validation-status DATA in §8.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-19 17:03:09 -07:00
leetcrypt 71f1aa4c6f pwstore: keepassxc LIVE-VM proof + root-group entry-path fix
Proven end-to-end (scan→plan→commit→restore-from-backup) against the real
keepassxc-cli 2.7.6 in the incredigo-sbx sandbox VM. The live run surfaced an
adapter bug the fake CSV masked: KeePassXC's CSV "Group" column is the full path
including the root group name (default root is "Passwords"), but keepassxc-cli
addresses entries relative to root (/GitHub, not /Passwords/GitHub). kpEntryPath
now drops the leading root segment; the unit test that encoded the buggy
/Root/GitHub form is corrected and a nested-group table test added.

Records the proof as data: docs/BROWSER-ROTATION.md promotes keepassxc
MOCK-ONLY -> LIVE-VM (the others stay MOCK-ONLY).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-19 16:57:30 -07:00
leetcrypt c2f181e56d passwords: Phase B browser/manager propagation engine (pwgen + pwstore + CLI)
Add the secondary-persona path from docs/BROWSER-ROTATION.md: ingest a password
manager / browser store, take a mandatory verified sealed backup, generate fresh
strong passwords, hand the human a ready-to-finish task at the MFA wall, and commit
the new value into the manager only after the site change is confirmed.

- internal/pwgen: crypto/rand generator (rejection sampling, per-class guarantee,
  vault-native — never returns plaintext as a Go string).
- internal/pwstore: Manager/ItemUpdater/BulkImporter contracts, secrets-free
  RedactIdentity, and five adapters — bitwarden (bw, stdin), keepassxc
  (keepassxc-cli, stdin), 1password (op, argv assignment with documented caveat),
  chrome/firefox (tmpfs CSV ingest-and-shred). All real code; validation status is
  data (MOCK-ONLY against fake binaries/CSVs, recorded in the design doc).
- internal/pwstore/stage.go: age-sealed staged-list (WriteStaged/ReadStaged) +
  StagedImporter — the only artifacts crossing the plan->commit gap, never plaintext.
- cmd/incredigo: passwords scan|plan|guide|commit. Backup gate seals + round-trip
  verifies before any commit; guide is interactive verify-before-commit; commit is
  headless over a sealed stage. Browser CSV writes are gated behind --allow-csv.

Hard rules honored: backup-before-commit, verify-before-commit, no plaintext on
disk (browser CSV is the flag-gated tmpfs+shred exception), MFA always a human
handoff. go vet + -race clean; end-to-end verified against a fake bw.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-19 13:02:18 -07:00
leetcrypt 9109da7ae4 rotate: promote k8s + mongo to LIVE-VM via real-target POCs
Ran live cutover POCs in the sandbox VM against the real target software and
promoted both drivers from MOCK-ONLY to LIVE-VM in the proof table (data, not
driver behaviour):

- k8s: proven against real k3s v1.35 kube-apiserver + token controller
  (lab-provision-k8s.sh). The real apiserver serves a self-signed cert, so the
  driver gained CA-pinned TLS — blob params ca= (base64 PEM, pinned as the only
  trusted root) and insecure=1 (lab-only escape hatch), routed through a new
  clientFor(cr). Neither is test-awareness; a real deployment configures the same
  CA. Added TestK8sTLSTrust covering CA-pinned / insecure / untrusted-rejection
  and the blob round-trip.
- mongo: proven against real mongod 8.0 (lab-provision-mongo.sh).

npm stays MOCK-ONLY by decision (registry.npmjs.org not self-hostable; real
create-token requires the account password in the body) — documented as a
contract gap rather than faking a LIVE-VM. gcp/twilio/flyio remain MOCK-ONLY.

Live POCs surfaced real-target-only behaviour now recorded in the docs: the
apiserver's ~10s successful-auth cache (old token kept working ~7s after Secret
deletion) and real mongosh's stdout prompt. Full suite 104 green, -race clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 21:50:21 -07:00
leetcrypt 35f19c007a rotate: 6 service-API drivers (mongo, npm, gcp, twilio, flyio, k8s)
Each driver is real-service code only (no test awareness; tests inject just an
HTTPClient/Bin), following the established patterns:

- mongo: in-place changeUserPassword over the old connection; no secret on argv
  (host/db on the URI, both passwords on stdin, output scrubbed).
- npm:   create token -> /-/whoami -> delete-by-key; blob records the key.
- gcp:   service-account KEY rotation; mints an RS256 JWT signed with the SA's
  own private key (crypto/rsa, stdlib) to self-auth the IAM create/verify/delete.
- twilio: API Key create -> get -> delete, Basic auth (KeySid/secret).
- flyio: GraphQL — a managing token mints/deletes the rotated deploy token.
- k8s:   create a service-account-token Secret, await the populated token,
  verify against the SA, delete the old Secret (invalidates the old token).

All 6 register their honest proof level (MOCK-ONLY) in proofs.go +
ROTATION-PROOFS.md; mongo/npm/k8s are LIVE-VM candidates pending real-target VM
POCs. Emulators enforce credential validity (gcp verifies the JWT signature),
tests assert Verify(old) fails after revoke + a secret-leak canary.

Full suite: 103 green, -race clean on rotate/sink/vault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 15:57:59 -07:00
leetcrypt b415c43bff rotate: 4 SaaS-token/app-signing drivers + data-separated proof tracking
Add gitlab, cloudflare, ghactions (MOCK-ONLY) and appsecret (LIVE-VM) one-file
Rotators, each with a table-driven cutover-proof + leak-check test. gitlab/
cloudflare/ghactions are in-place SaaS-token rolls (self/rotate, value-roll,
sealed-secret overwrite) so RevokeOld is a no-op; ghactions seals via
nacl/box.SealAnonymous (no new go.mod dep). appsecret regenerates a local app
signing secret and rewrites it in place across every target file (atomic, mode-
preserving, redacted errors), discoverable via exact-match app-signing key names
in env.go.

Keep real-rotation code distinct from mock code: how each driver's cutover was
validated lives as DATA in internal/rotate/proofs.go (single source of truth) +
docs/ROTATION-PROOFS.md, surfaced as a PROOF column in `incredigo rotate` so a
MOCK-ONLY driver can never be mistaken for a LIVE-VM one. appsecret proven LIVE-VM
against real local files in the sandbox VM.

82 tests green, -race clean on rotate/sink/vault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 15:28:05 -07:00
leetcrypt f6d145669c docs: VISION/MARKET/FINDINGS, brand assets, CLAUDE directive refresh
Adds VISION.md (target personas + 20-credential surface), docs/MARKET.md
(competitive positioning), and docs/FINDINGS-2026-06-16.md (controlled-env
test run: live cutover POCs for postgres/gitea/aws/mysql/redis/wireguard, the
backup-gate defect + fix, and the MariaDB unprivileged-rotation gap). Swaps the
placeholder _igo_options assets for the banner/icon set. Refreshes the project
CLAUDE.md directive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 14:48:56 -07:00
leetcrypt 425c299359 Initial commit: Incredigo — local-first RAM-only credential custody
v1: discover → migrate → expiry tracking → sealed export/import.

- vault: memguard mlock/DONTDUMP arena, handle indirection, zeroize-on-drop
- discover: registry + 8 read-only scanners (aws, env, netrc, ssh, docker,
  kube, git) and a file/dir harvester (--path)
- sink: gopass streaming insert; length-prefixed bundle framing; Sealer
  interface with three impls — age (default, authenticated), hmac
  (authenticated, openssl-only encrypt-then-MAC), openssl (CBC fallback,
  unauthenticated; OpenSSL 3.x enc refuses AEAD)
- policy: local expiry engine, 60d/8w threshold parser
- audit: redacted append-only JSONL, injectable clock
- export/import: passphrase from no-echo prompt or $INCREDIGO_PASSPHRASE into
  locked memory; secrets stream gopass<->Sealer as bytes, never Go strings
- tests: scanner leak-checks, vault zeroize, bundle round-trip via fake gopass;
  go test ./... green, -race clean

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:57:34 -07:00