feat(features,gateway,host,desktop): a webhook's secret is shown once, in the window that minted it, as the webhook is made or a new secret is asked for
Merged
DA
4d ago+358+358+012345678901234567890123456789-199-199-012345678901234567890123456789
Summary
Dean: "Isn't it standard practice to show the webhook secret once when it's created? If so, let's do that even if it means changing AGENTS.md." It is the standard (GitHub, Stripe, Vercel, and Linear webhooks all show the secret once), and Dean approved the AGENTS.md change. This PR replaces #2776's "copied, never shown" path with "shown once".
- Minting. When a webhook trigger is made, or its secret is replaced, the write that mints the secret answers it to the window that asked, once:
- the service's secret, as a webhook is made or a trigger changes to one, which the client no longer drops;
- the host's secret, for New secret: minted in the host, with only its SHA-256 sent to the service, so the service never holds it.
- The act and method are renamed
automations.newWebhookSecret.
- The page shows it in a read-only field with Copy and "You won't be able to see this again. Copy it into your sender now.", until Done or until the page is left. After that only New secret exists, and it asks first, since the new secret stops the old one. A secret is never retrievable again: the server keeps only the hash, as before.
- Kept narrow:
- The secret travels on the act's answer to the asking panel alone, as a Terminal's port does.
- It's held in the editor page's React state. It is never written to app state, the automations list (the hook keeps the automation alone), navigation history, another window, or the voice window. It's never in a trace, counted event, fixture, or log.
- The field's container is
ph-no-capture, so session replay leaves it out whole, beside the masking every input already has. PRIVACY.md's list of left-out surfaces now names it.
- AGENTS.md, the one line Dean approved, under the Never list's exceptions: "And, approved by Dean on 2026-10-15, an automation webhook secret may be shown once, in the window that minted it, at creation or rotation; never anywhere else."
- Removed: #2776's clipboard path, meaning the native node's
os.copyTextcapability and the host's copy seam. - The Automations pages load as a chunk of their own the first time they open (
React.lazy), as Settings does (#2738).- Main had grown to the renderer's bundle ceiling: CI measured 687,907 gzipped bytes against 687,873.
- With the editor out of the up-front bundle, this PR's renderer measures 682,600, so the pages' growth stops counting against every window open.
- The baseline is unchanged.
Evidence
- Platform-independent checks:
pnpm check:affectedpasses. - New and changed tests, each failing without the change:
automations-page.test.ts: a webhook made in the window shows its secret once, read-only with Copy. The field sits inside.ph-no-capturefor the replay masking. The window's app state doesn't hold it. Copy hands main the secret. After Done it's gone from the document, and only New secret remains.automations-page.test.ts: New secret asks first, then the page that asked shows the new secret once, and after leaving and reopening the page it's never shown again.compose-automations.test.ts: the host mints the secret, sends the service only its hash, and answers the secret to the window. A refused replacement answers no secret, and a closed gate asks nothing.automation-client.test.ts: create and change answers carry the service-minted secret through for the window to show once.automation-acts.test.ts: the voice and call windows are refused the renamed act.automations-page.test.ts(Devin's review): a shown secret is let go of once the automation is saved as anything but a webhook.
- Test count: +1 (two clipboard tests replaced by the shown-once ones).
Invariants
-
./scripts/check.shgreen. (CI) - JSON Schema goldens unchanged.
- Every behavior change has, in this PR, the test that fails without it.
- Gateway envelope goldens unchanged. The secret rides only this one act's answer, under the new AGENTS.md line.
- No new
asassertion, run outside the edges, raw async primitive, or dropped Effect. - Test count delta listed.
- No hand-rolled file replaced. The clipboard capability is deleted.
- AGENTS.md is edited with Dean's approved line; CLAUDE.md is its symlink.
-
PRIVACY.mdchanged (a product decision): the shown-once secret joins the surfaces left out of the screen recording. - Package deps unchanged.
- Barrel/door rule unaffected.
Notes
- Mac: this is a UI PR, and CI could not verify the Mac UI.
./scripts/verify.shwas not run.
š¤ Generated with Claude Code