Skip to content

Mask secrets in the lane's title, step and error before they leave the daemon - #21794

Merged
teamleaderleo merged 10 commits into
feat-cmux-nextfrom
feat/lane-step-sanitize
Oct 19, 2026
Merged

teamleaderleo merged 10 commits into
feat-cmux-nextfrom
feat/lane-step-sanitize

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

The Lanes view showed raw prompt text in each lane's title and step line, so a pasted token appeared on screen and went to every fleet peer. acpmux now masks secrets in the lane before it leaves the daemon, and the clients read only the masked lane. Follow-up to #21727, part of cmuxterm-hq#2509.

What changed

  • New acpmux::secret_scan module. find and mask replace each secret with its kind: [github token], [anthropic key], [jwt], [private key], [auth token], [credentials], [secret]. It catches provider keys (Anthropic, OpenAI, Stripe, GitHub, Slack, AWS, Google), JWTs, PEM private keys (truncated ones too), auth headers, credentials in URLs, NAME=value secrets, and long random runs. The masked text no longer holds the secret, so nothing downstream can reveal it. The module only reads text, so transcripts, notifications and the phone relay can use it later.
  • The lane has two new fields, step and title, and lane.error is now masked as well.
    • step is the running prompt or the agent's last reply.
    • title is the session's title, falling back to its first prompt.
    • Both are scanned only within their first 4,096 characters, then masked, then cut to 240 characters. Masking happens before the cut, so a token that crosses the cut can't leave half of itself behind.
  • When an older peer sends a lane without title, this daemon decides that lane again, so the peer's lanes come out masked too.
  • The webview and Swift lanes read only lane.title, lane.step and lane.error. The raw prompt, title and error fallbacks are gone. Against an older daemon, a lane shows the session name and no step.
  • AgentActivityLane.of no longer takes a title:, since the one its callers passed could be the raw first prompt.
  • The protocol IR and generated web types now include step and title. Re-blessing the IR also picked up two stale LifecycleReceipt descriptions from the base branch.

Left alone on purpose: git SHAs and other hex, UUIDs, paths, branch names, camelCase identifiers, sk-learn-… package names, data:…;base64, images, and code or config that only names tokens (max_tokens: 100000, crate::secret_scan::mask, token: expired). For an assignment to be masked, its value must contain a letter and a digit. The module doc lists the known misses: short passwords with no recognizable shape, and values containing ;, , or spaces.

Next: a stacked PR replaces these hand-written patterns with gitleaks' rule set (MIT, pinned to v8.30.1) and adds Nosey Parker's examples as a fixture corpus, keeping the same find/mask API.

Question for Leo

The summary still sends these fields raw to clients and peers: turn.prompt, lastPrompt, lastMessage, preview and title. Should the daemon mask them for peers too (remote chats would then show masked text), or stay with only the lane fields the Lanes view uses?

Verification

  • Red first:
    • 50d0d57 and ac4a0b3 add the tests.
    • On ac4a0b3, the webview test a lane's title and error are the daemon's masked ones failed (1 of 8). On Huge Blue, the Rust tests the_lanes_title_is_masked… and ordinary_text_is_left_alone failed, and so did a_lane_from_an_older_peer… once its fixture gained indicator. That fixture change landed in b3b7f14.
    • On 50d0d57, the step test failed (1 of 7), and the Rust tests did not compile because the module did not exist yet.
  • Green on the head, run on Huge Blue: cargo test -p acpmux --lib, protocol_ir_is_current, cargo fmt --check, the webview priority and lanes tests, and protocol codegen with no diff.
  • The fake secrets in the tests are assembled at run time, so no committed file contains a token a scanner would flag. Each masked case also checks that the secret is gone and that a second scan finds nothing.
  • Not run locally: the Swift AgentLanesTests change. That is left to CI.

The protocol spec changed (Lane.step, Lane.title), so this may need a coordinator WINDOW.

Changelog

Fixed: The Lanes view masks API keys, tokens and other secrets in each lane's title, step and error, including for fleet peers.

🤖 Generated with Claude Code

teamleaderleo and others added 3 commits October 18, 2026 15:00
…raw prompt (red)

hq#2509 follow-up. The Lanes view shows each lane's prompt, peers'
included, so a pasted token shows on screen. acpmux should mask secrets
in lane.step before the summary leaves it, and the app should read only
lane.step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Lanes view showed each lane's raw prompt, fleet peers' included, so
a pasted token showed on screen. acpmux now decides the step itself
(lane.step: the running turn's prompt, else the agent's last reply
line) and masks secrets in it and in lane.error before the summary
reaches a client or a peer. The mask is typed: [github token],
[anthropic key], [jwt], [private key], [credentials], [secret]. It is
destructive, not a cover: the masked text has no source left to reveal.

The detector is its own module (acpmux::secret_scan) so transcripts,
notifications and the phone relay can share it. It knows provider key
shapes, JWTs, PEM private keys, auth headers, URL credentials, secret
NAME=value assignments and long random-looking runs, and leaves git
SHAs, UUIDs, paths and data: URI payloads alone.

The app and the web view read only lane.step; an acpmux from before it
shows no step rather than the raw prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:05 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 0463e11f-64eb-4be7-8c45-63cdea060a10

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Receipt for 8644306 (this PR's head; a new push replaces it)

  • Visual review: HQ feed card · gallery before/after runs
  • Checks: this head
  • App of this head (app.zip, kept 1 day): gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr21794 -f ref=feat/lane-step-sanitize -f platform=macos -f backend_mode=local

@teamleaderleo

teamleaderleo commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator Author

Visual preview of 8644306: in the maintainers' private review feed.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-ccgvc created for this PR by the cmux coordinator.

…ssignment false positives (red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:10 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

red: web / Web status; do not merge until green; see lane-rules
red: web / react-apps-check; do not merge until green; see lane-rules
red: web / react-apps-check (1); do not merge until green; see lane-rules

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Unclear: web / react-apps-check (1) failed: no known signature; failed step: Test webviews.

CI failed on 864430675b (run 39172673185 attempt 2): 2 unknown.
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped.

Job Verdict Why
web / react-apps-check (1) unknown no known signature; failed step: Test webviews
web / react-apps-check unknown no known signature; failed step: Check webview test shards

Not re-run automatically: web / react-apps-check (1), web / react-apps-check are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards failed on 864430675b (https://github.com/manaflow-ai/cmux/actions/runs/39172673185). It does not block the merge; a red guard merged into main breaks it for every open PR.
The log named no failed step; see the run.

Agents: python3 scripts/ci/guard_attribution.py fix applies the mechanical fixes locally. This comment is updated in place on each push.

…nes, cuts before scanning (hq#2509)

The lanes view showed the summary's raw title or first prompt and the
raw error. acpmux now sends a masked lane.title, both clients read the
lane's title and error only, and an older peer's lane without them is
decided again here. The step and title scan only the text's head. The
assignment rule needs a letter and a digit, so counts and words named
token or secret are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 18, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:14 — with GitHub Actions Inactive
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:17 — with GitHub Actions Inactive
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo teamleaderleo changed the title Mask secrets in the lane step before it leaves the daemon Mask secrets in the lane's title, step and error before they leave the daemon Oct 18, 2026
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:19 — with GitHub Actions Inactive
@cursor

cursor Bot commented Oct 18, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

A review subagent checked 906e297 (correctness first). It found no leak or panic in the step and error paths. Each finding and what happened to it:

  1. Medium, fixed in b3b7f14. The web lane still showed the raw title (title || lastPrompt) and the raw lastTurn.errorText. acpmux now sends a masked lane.title, and both clients read only lane.title, lane.step and lane.error. The docs no longer say the whole summary is masked. The raw summary fields are the open question in the PR body.
  2. Low, fixed. An older peer's lane arrived without step. A lane without title is now decided again. Test: a_lane_from_an_older_peer_is_decided_again_with_its_masked_step.
  3. Low, fixed. The assignment rule masked crate::secret_scan::mask, max_tokens: 100000 and tokenizer: sentencepiece. A value now needs a letter and a digit and can't start with :. These cases are in ordinary_text_is_left_alone.
  4. Low, left. Values containing ;, , or spaces, and URL passwords containing /, still get through. The module doc says so. The gitleaks-based follow-up covers more shapes.
  5. Low, fixed. The step and title now scan only the first 4,096 characters, so a megabyte paste isn't scanned in full on every summary build.

Head 76290ce. On Huge Blue: cargo test -p acpmux --lib passed (149 tests), cargo fmt --check was clean, the webview lanes and priority tests passed (17), and protocol codegen produced no diff.

…cut into it (red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:21 — with GitHub Actions Inactive
…rompt (hq#2509)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 18, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 18, 2026 22:22 — with GitHub Actions Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

A second review subagent checked 906e297..76290ce. Nothing in it blocks merge. Each finding and what happened to it:

  1. Low-medium, fixed. The 4,096-character scan cut counted whitespace that is folded away later. A token behind a long blank run was cut mid-token, missed by the scan, and its head (ghp_aZ) reached the 240-character step. The cut now happens after folding. Red on ad8d380 (whitespace_before_a_secret_does_not_move_the_scan_cut_into_it produced ghp_aZ), green on 7261ea1.
  2. Low, accepted. A value with no digit (password: hunter) is no longer masked by the assignment rule. That is the cost of leaving token: expired and max_tokens alone. The gitleaks-based follow-up revisits it.
  3. Nit, fixed. The title falls back to lastPrompt, which is the latest prompt, not the first. The docs and IR now say so.

The reviewer also checked these and found them fine: the peer re-decide with closed or current peers, all AgentActivityLane.of callers, and the gallery fixtures.

Head 7261ea1. On Huge Blue, cargo test -p acpmux --lib passes (150 tests, including protocol_ir_is_current), cargo fmt --check is clean, codegen produces no diff, and the webview lanes and priority tests pass (17).

@blacksmith-sh

blacksmith-sh Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Found 4 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp0qwk78vu/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmp8n9gjhws/summary-missing.json (live):/summary-missing.json (live): View Logs
test/narrow-layouts.test.ts/narrow-layouts.test View Logs
test/narrow-layouts.test.ts/narrow-layouts.test View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

teamleaderleo commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator Author

CI on head 8644306: 87 checks pass and 2 fail. 8644306 is cmux-autofix's Prettier reflow of my 7261ea1; the code is unchanged. Both reds are base reds:

  • cmux-next checks, Type scale. This is the CmuxNextDesign/Text base red recorded on cmux NEXT: lanes show each session's harness and current step (hq#2509) #21727. This PR changes no design or text files.
  • web / react-apps-check (1), narrow-layouts.test.ts, "the agent pane holds together at 280 px". It fails on new-tab: New Tab chips: Mode overlaps Dictate. It failed the same way on a rerun (job 117818305231), and feat/chief-pet-setting's run at 3ad7c52, also based on feat-cmux-next, fails with the same line (job 117814793064). This PR doesn't touch the New Tab composer.

Not run: cmux-next swift test. It was skipped by design, because CmuxNext Swift suites run on a PR only with full-ci or a feat-cmux-next/next-batch dispatch. The Swift change is small: AgentActivityLane.of reads lane.title and drops its title: parameter, both callers are updated, and AgentLanesTests has two new tests. Swift wasn't run anywhere else either. Huge Blue has no swiftc, and builds aren't allowed on this Mac. The suites will run on the feat-cmux-next push after merge.

The acpmux Rust tests (cmux-next daemon tests (same-tree)), rustfmt, and the webview shards that cover the lanes all pass.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

PR CI doesn't run the acpmux unit tests, so I ran scripts/verify-cmux-tui-hosted.sh --filter lane_tests on exact head 8644306. Linux test and lint both passed: https://github.com/manaflow-ai/cmux/actions/runs/39177296342

@teamleaderleo
teamleaderleo merged commit b66d869 into feat-cmux-next Oct 19, 2026
230 of 252 checks passed
@teamleaderleo
teamleaderleo deleted the feat/lane-step-sanitize branch October 19, 2026 02:16
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

teamleaderleo added a commit that referenced this pull request Oct 21, 2026
Review: AgentActivityLane.of lost its title: parameter (#21794), so the
test target did not compile. The app reaches the lanes through
SidebarRowFeed.laneRowSource, which replaces SidebarBridge's unused
CmuxNextActions import, so CmuxNextApp's source line count stays put.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c2a-ee5f-7111-b18b-d14161ef8c3c&host=huge-blue
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…ebug toggle) (#20956)

* test(cmux-next): sidebar rows show each agent session's acpmux lane state

Leo 2026-10-15 via cg-ui-coord: a sidebar row shows its agent session's
live status (working, waiting on you, failed, idle) from the acpmux lane
AgentLaneStore already holds, whatever harness runs the chat, behind the
Debug toggle sidebar.laneStatus (off by default). Adds the toggle, the
shared lookup of a workspace's lanes on this Mac (SidebarLocalLanes) and a
pass-through SidebarLaneStatus, so the tests compile and fail until rows
merge the lane state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cmux-next): sidebar rows merge each agent session's acpmux lane state (Debug toggle)

With sidebar.laneStatus on, a workspace row's status mark also follows the
acpmux lane of each agent chat in it on this Mac, read inside the row's
mapping so a lane change maps that row again. Precedence is StatusStack's
state rank: failed, then waiting on you, from either the row or a lane;
then working; then the row's own done; an idle lane adds nothing; the row's
own state wins a tie. Off, no lane is read and rows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cmux-next): name the sidebar's section type in the lane status tests

SidebarSection is ambiguous between CmuxNextDaemon and CmuxNextSidebar in the
app tests; use the bridge's SidebarRowSection. Back to the pass-through
SidebarLaneStatus so this commit is the behavioral red; the next one merges
the lanes again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cmux-next): sidebar rows merge each agent session's acpmux lane state (Debug toggle)

With sidebar.laneStatus on, a workspace row's status mark also follows the
acpmux lane of each agent chat in it on this Mac, read inside the row's
mapping so a lane change maps that row again. Precedence is StatusStack's
state rank: failed, then waiting on you, from either the row or a lane;
then working; then the row's own done; an idle lane adds nothing; the row's
own state wins a tie. Off, no lane is read and rows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 61b50fb

- regenerated schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/38700743401

* chore(next): PR autofix at 8bbe40b

- Packages/macOS/CmuxNext/ci-target-graph.json
- schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39367511362

* refactor(cmux-next): row lane status lives in CmuxNextAgentLanes, not the app target

The CmuxNextApp size cap was this PR's own red: new code goes in its own
target. SidebarLaneStatus and SidebarLocalLanes move to CmuxNextAgentLanes
(the acpmux lanes feed, which already depends on AgentActivity, Bridge and
Sidebar); the app reads them through AgentLaneStore.sidebarRows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c2a-ee5f-7111-b18b-d14161ef8c3c&host=huge-blue

* fix(cmux-next): lane status tests compile; the app grows no source lines

Review: AgentActivityLane.of lost its title: parameter (#21794), so the
test target did not compile. The app reaches the lanes through
SidebarRowFeed.laneRowSource, which replaces SidebarBridge's unused
CmuxNextActions import, so CmuxNextApp's source line count stays put.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c2a-ee5f-7111-b18b-d14161ef8c3c&host=huge-blue

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>

This branch was previously deployed

1 inactive deployment
cmux-tui-merge-tree — 86443067 Deployed Oct 18, 2026 by manaflow-cmux-pr-autofix[bot] via publish the merge tree #2055
Sign up for free to 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