Skip to content

RT: retire the plaintext secrets.json fallback — encrypted store is the only source - #10

Merged
m4ttheweric merged 2 commits into
mainfrom
plaintext-secrets-retire
Aug 21, 2026
Merged

RT: retire the plaintext secrets.json fallback — encrypted store is the only source#10
m4ttheweric merged 2 commits into
mainfrom
plaintext-secrets-retire

Conversation

@m4ttheweric

@m4tthewericm4ttheweric commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Deletes readPlaintextSecretsFallback; loadSecrets reads the sops/age store only and propagates failures (every caller terminates at a logged seam — verified per call site in review). Remediation strings point at rt secrets set rt <key>. e2e sdm browser-login gets an SDM_EMAIL env seam instead of seeding the file; stale VS Code extension prose fixed.

Reviewed (opus) + scoped re-review approved. Closes the RT-32 transition window: ~/.mattstack/rt/secrets.json is deleted at cutover after this merges.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • StrongDM browser login now supports SDM_EMAIL as an environment override before using the stored email.
    • Secret loading now uses the encrypted store as the single source of truth.
  • Bug Fixes

    • Secret read failures are now reported instead of silently falling back or returning partial results.
    • Updated guidance and error messages to use rt secrets set for configuring secrets.
  • Documentation

    • Clarified how encrypted secrets and VS Code’s secret storage are used.

m4tthewericand others added 2 commits August 21, 2026 12:41
The encrypted sops/age store has been authoritative since the foundation
lane; every consumer is ported. loadSecrets now reads only the encrypted
store and propagates a decrypt failure instead of degrading to
~/.mattstack/rt/secrets.json, so a broken store can never be mistaken for
"nothing configured."
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ment
- e2e/tests/sdm-browser-login.test.ts seeded sdmEmail via a secrets.json
write, which loadSecrets no longer reads (the orchestrator's email
preflight would silently fail). Added SDM_EMAIL as a narrow env override
ahead of the encrypted store in lib/sdm/browser-login.ts's email seam, and
point the test at it instead of writing to disk. Verified by reading the
code path (browser-login.ts, core.ts's SDM_EMAIL passthrough to the sdm
subprocess, e2e/interactive.ts's env merging) and the test — not run,
since it drives a real browser and requires RT_SDM_BROWSER_E2E=1.
- extensions/vscode/rt-context/src/secrets.ts header and
showCantSaveSecretMessage doc comment still described the daemon's
since-removed plaintext fallback; rewritten to state the encrypted store
as the only source.
- lib/daemon/__tests__/secrets-handler.test.ts header trimmed to drop the
pure history sentence (git keeps that).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@m4ttheweric
m4ttheweric merged commit eeafc9b into mainAug 21, 2026
1 of 2 checks passed
@coderabbitai

coderabbitaiBot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bbcfb6f-6220-40e3-af19-656ff73ff06b

📥 Commits

Reviewing files that changed from the base of the PR and between 837a14c and 602d161.

📒 Files selected for processing (8)
  • e2e/tests/sdm-browser-login.test.ts
  • extensions/vscode/rt-context/src/secrets.ts
  • lib/__tests__/linear.test.ts
  • lib/daemon/__tests__/secrets-handler.test.ts
  • lib/daemon/freshness.ts
  • lib/daemon/handlers/secrets.ts
  • lib/linear.ts
  • lib/sdm/browser-login.ts

📝 Walkthrough

Walkthrough

The daemon now reads secrets only from the encrypted store and propagates read failures. Secret configuration guidance uses rt secrets set. SDM browser login accepts an SDM_EMAIL override, with updated unit and end-to-end tests.

Changes

Encrypted secrets behavior

Layer / File(s)Summary
Encrypted store loading and failure propagation
lib/linear.ts, lib/__tests__/linear.test.ts
loadSecrets removes plaintext fallback behavior, returns encrypted-store values, and propagates read failures. Tests cover empty stores, persistence, and rejected reads.
Daemon secret errors and documentation
lib/daemon/__tests__/secrets-handler.test.ts, lib/daemon/freshness.ts, lib/daemon/handlers/secrets.ts, extensions/vscode/rt-context/src/secrets.ts
Secret-reader exceptions now reject handler promises. Error messages and documentation direct secret writes through rt secrets set.
SDM email override and end-to-end setup
lib/sdm/browser-login.ts, e2e/tests/sdm-browser-login.test.ts
Browser login checks SDM_EMAIL before the encrypted sdmEmail value. The end-to-end test supplies the environment override.

Estimated code review effort: 3 (Moderate) | ~20 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch plaintext-secrets-retire

Comment @coderabbitai help to get the list of available commands.

m4ttheweric added a commit that referenced this pull request Aug 22, 2026
…DME drift, escaping, log collision)
#1: walkthrough.sh's cleanup() defaulted a missing phases.jsonl to success
via ${f:-0}, so dying before the first vm_phase_end (e.g. no tart on a
fresh machine) exited 0 with an empty report. Dropped the default so a
missing ledger fails the `[ -eq 0 ]` test and falls through to exit 1,
matching xcuitest.sh's existing fail-closed form. Pre-existing on main;
reproduced the before/after with the review's no-tart repro.
#2/#9: README described ax.sh/drive-setup.sh/trigger-update.sh as not yet
in the tree and misattributed the screens-phase failure to an unstaged
guest script; all three are staged into $GUEST_BIN by walkthrough.sh today.
Corrected the Status/Layout prose to state what's actually gating
`--scenario create/join` (L3's setup screens) and the update phase (L3's
MATTSTACK_APPCAST_URL hook). Also corrected the disk-footprint line: the
~60 GB figure is cleanroom-only, and an --xcode golden needs substantially
more (full Xcode install on top of the base OS).
#3: check-vm-scripts.sh's ax.sh syntax-error net only matched "script
error"/"Expected " literally, missing other osascript compile-failure
shapes (e.g. "syntax error: A property can't go after..."). Widened to a
bare "syntax error" alternative, which osascript writes for every compile
failure and never for a runtime error.
#4: ax_click_button_named defaulted its process arg to the already-escaped
$AX_APP, then ran ax_esc on it again, double-escaping any AX_APP containing
a quote or backslash. Now only escapes when an explicit (raw) $2 is given.
#6: build-golden.sh's tart boot log was named golden-$VER-tart.log for both
flavours, so an --xcode build silently overwrote the cleanroom golden's
boot log. Named it after $GOLDEN instead, which already carries the -xcode
suffix.
Findings #5 (VM_APPCAST_PORT default duplication), #7 (--ver not
version-validated), #8 (xcuitest.sh's guest-staging convention), and #10
(PAT/password on guest ssh argv) are parked per the reviewer's ruling —
not touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
m4ttheweric added a commit that referenced this pull request Aug 22, 2026
R-T7-a (#1): tool.daemon's launchd/worktrees sub-facts are real negative
signals now, not folded into a "ready" detail — either failing flips the
row to "invalid" with the specific fact named.
R-T7-b (#4): the legacy split-state branch (required, invalid) carries a
{type:"steps"} merge-by-hand remedy instead of action:null; the detail also
gets verify's plural handling back.
R-T7-c (#6): fixes the bundle-memo hazard at its source. appBundleRoot()
(lib/bundle-layout.ts) now memoizes only the true default
(exists === existsSync); an injected exists (every Probes-driven caller)
never reads or writes it. Validator tests drop the reset ceremony this made
unnecessary.
R-T7-d (#12): tool.rt-link's needs-you branch carries a {type:"run"} action
to fix the link in one step.
#2/#3: tool.fzf and tool.rt now distinguish "genuinely absent" (127) from
"resolved but won't run" (any other exit) — the latter is "error", never
"ready"/"missing".
#5: tool.daemon and tool.app get recheck:"on-activate" (Task 6's convention
for out-of-band, leave-the-app-and-come-back rows).
#7: the five optional rows carry real optionalNotes.
#8: tool.app's legacy note names the exact hit path(s), matching verify's
phrasing.
#9: interceptsRow wraps shimReport()/staleIntercepts() so a throw degrades
to an "error" row instead of rejecting the whole plan.
#10: tool.daemon's Login Items action is imported from permissions.ts
(now exported as LOGIN_ITEMS_SETTINGS_ACTION) instead of a duplicate
literal.
#11: lib/shell-integration.ts gains detectShellFrom()/shellRcPathFor(),
pure functions the real detectShell()/shellRcPath() now delegate to and
tool.shell reuses over Probes; an unrecognized shell gets an honest
"can't write automatically" detail instead of "Install writes it".
#13: the tool.daemon describe saves/restores DAEMON_CONFIG_PATH's
pre-existing content around the whole block instead of only deleting it,
so status-fallback.test.ts's absence assumption can't be poisoned.
#14/#15: header comment no longer cites the brief's table, the rt-link
"no app" test asserts its reason string, and commands/verify.ts's docblock
is trimmed to the one load-bearing line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@m4ttheweric
m4ttheweric deleted the plaintext-secrets-retire branch August 24, 2026 17:50
m4ttheweric added a commit that referenced this pull request Aug 24, 2026
RT: retire the plaintext secrets.json fallback — encrypted store is the only source
m4ttheweric added a commit that referenced this pull request Aug 24, 2026
…DME drift, escaping, log collision)
#1: walkthrough.sh's cleanup() defaulted a missing phases.jsonl to success
via ${f:-0}, so dying before the first vm_phase_end (e.g. no tart on a
fresh machine) exited 0 with an empty report. Dropped the default so a
missing ledger fails the `[ -eq 0 ]` test and falls through to exit 1,
matching xcuitest.sh's existing fail-closed form. Pre-existing on main;
reproduced the before/after with the review's no-tart repro.
#2/#9: README described ax.sh/drive-setup.sh/trigger-update.sh as not yet
in the tree and misattributed the screens-phase failure to an unstaged
guest script; all three are staged into $GUEST_BIN by walkthrough.sh today.
Corrected the Status/Layout prose to state what's actually gating
`--scenario create/join` (L3's setup screens) and the update phase (L3's
MATTSTACK_APPCAST_URL hook). Also corrected the disk-footprint line: the
~60 GB figure is cleanroom-only, and an --xcode golden needs substantially
more (full Xcode install on top of the base OS).
#3: check-vm-scripts.sh's ax.sh syntax-error net only matched "script
error"/"Expected " literally, missing other osascript compile-failure
shapes (e.g. "syntax error: A property can't go after..."). Widened to a
bare "syntax error" alternative, which osascript writes for every compile
failure and never for a runtime error.
#4: ax_click_button_named defaulted its process arg to the already-escaped
$AX_APP, then ran ax_esc on it again, double-escaping any AX_APP containing
a quote or backslash. Now only escapes when an explicit (raw) $2 is given.
#6: build-golden.sh's tart boot log was named golden-$VER-tart.log for both
flavours, so an --xcode build silently overwrote the cleanroom golden's
boot log. Named it after $GOLDEN instead, which already carries the -xcode
suffix.
Findings #5 (VM_APPCAST_PORT default duplication), #7 (--ver not
version-validated), #8 (xcuitest.sh's guest-staging convention), and #10
(PAT/password on guest ssh argv) are parked per the reviewer's ruling —
not touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
m4ttheweric added a commit that referenced this pull request Aug 24, 2026
R-T7-a (#1): tool.daemon's launchd/worktrees sub-facts are real negative
signals now, not folded into a "ready" detail — either failing flips the
row to "invalid" with the specific fact named.
R-T7-b (#4): the legacy split-state branch (required, invalid) carries a
{type:"steps"} merge-by-hand remedy instead of action:null; the detail also
gets verify's plural handling back.
R-T7-c (#6): fixes the bundle-memo hazard at its source. appBundleRoot()
(lib/bundle-layout.ts) now memoizes only the true default
(exists === existsSync); an injected exists (every Probes-driven caller)
never reads or writes it. Validator tests drop the reset ceremony this made
unnecessary.
R-T7-d (#12): tool.rt-link's needs-you branch carries a {type:"run"} action
to fix the link in one step.
#2/#3: tool.fzf and tool.rt now distinguish "genuinely absent" (127) from
"resolved but won't run" (any other exit) — the latter is "error", never
"ready"/"missing".
#5: tool.daemon and tool.app get recheck:"on-activate" (Task 6's convention
for out-of-band, leave-the-app-and-come-back rows).
#7: the five optional rows carry real optionalNotes.
#8: tool.app's legacy note names the exact hit path(s), matching verify's
phrasing.
#9: interceptsRow wraps shimReport()/staleIntercepts() so a throw degrades
to an "error" row instead of rejecting the whole plan.
#10: tool.daemon's Login Items action is imported from permissions.ts
(now exported as LOGIN_ITEMS_SETTINGS_ACTION) instead of a duplicate
literal.
#11: lib/shell-integration.ts gains detectShellFrom()/shellRcPathFor(),
pure functions the real detectShell()/shellRcPath() now delegate to and
tool.shell reuses over Probes; an unrecognized shell gets an honest
"can't write automatically" detail instead of "Install writes it".
#13: the tool.daemon describe saves/restores DAEMON_CONFIG_PATH's
pre-existing content around the whole block instead of only deleting it,
so status-fallback.test.ts's absence assumption can't be poisoned.
#14/#15: header comment no longer cites the brief's table, the rt-link
"no app" test asserts its reason string, and commands/verify.ts's docblock
is trimmed to the one load-bearing line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
m4ttheweric added a commit that referenced this pull request Aug 26, 2026
Task 0c is done: mantine-kit 47014c8 (CI green, first green run on that
main since July) and console PR #10. Steps 1-4 and 6 checked off; step 5
is Task 1's to verify.
Three plan corrections that came out of running it:
- The 0b/0c order is reversed. Both edit console's package.json so one
has to be the base, and it is 0c: 0b sits behind a question only Matt
can answer, while 0c touches nothing under src/ui/design-system.
- 0b gains a blocking input. Console PR #9 adds spacing xxl/xxxl and an
h1..h6 sizes ladder to the app-theme.ts that 0b extracts, and the run
views consume them directly. Both orderings are written out, with the
exact values, so the fallback cannot quietly drop a row.
- 0b's parity capture is marked as the known-vacuous gate it is. It
renders static mocks, never the app, so zero drift is necessary and
not sufficient. An implementer reading it now knows not to report it
as visual proof.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@m4ttheweric