Repository navigation
Vault: an agent's secret request and the agents' guidance (root decision 0198 point 9, DEV-6631 tasks 651 and 652) - #401
Conversation
18aefea to
2a57a9c
Compare
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty Exact-head review of 2a57a9c against stacked base 3425e71: APPROVED. I traced the request store/lock, CLI, admitted Launchpad routes, stdin-only bw create path, Secure-note reader, UI and guidance. Locally in an isolated clone, git diff --check and check:static passed; 74 focused tests passed (0 failed, 28 snapshots) with Bun 1.4.3 under the host's heavy-command lock. GitHub's current-head checks are green; I did not independently rerun the full 2639-test suite or the real Vaultwarden run. The documented interrupted-create duplicate and absent answerer attribution remain known limits, not additional claims of verification. Public-diff scan found no named client Organization or person; the sole named Organization in added lines is the internal HumanAndMachine-ai reference to a wireframe. No merge performed.
3425e71 to
7eab155
Compare
The base branch was changed.
…s a runtime secret The ToolRunner takes an optional `stdin`, which the process reads before its standard input closes. The bw driver gains `createNote`: one Secure note in the Environment's own vault (organization and collection by ID, one custom field per value, hidden for a secret one), encoded as `bw encode` would and written to `bw create item` on stdin, never on argv; of bw's answer, which prints the whole item with its values, only the new item's id is read. Runtime secrets (decision F46) now also come from a Secure note with exactly one hidden custom field, besides a Login's password: what an agent's secret request becomes (DEV-6631 task 651). A note with more than one hidden field is `ambiguous` and never guessed; one without is `empty`. The item reader keeps only the count of a note's hidden fields and the value of the only one; its text fields and notes are dropped as before. The frozen 0197 pilot subset reads login passwords only, unchanged. Tests: the item reader on notes with one and two hidden fields and with malformed fields; `vaultSecrets` against the fake vault reading a note's one hidden value, two hidden fields ambiguous, none or an empty one empty, no other note content in the answer, no value on argv or in the journal; the pilot ignoring a Secure note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… it into its vault
Root decision 0198 point 9 (c), DEV-6631 task 651: what an agent cannot get
any other way, it asks a person for with `lazurio vault request --item
<name> --need <what> --why <why> --where <where in the service> --field
<Name>[:secret] ...`, which prints the request's id and its page in the
Launchpad (`<external origin>/settings/vault/requests/<id>`); `lazurio vault
request status <id>` answers pending, saved (with the item's name) or
declined, and `list` every request. The words are checked (one line each,
bounded), 1 to 10 fields with unique names, at most 50 waiting, and only
where the Environment's own vault is connected (never a 0197 pilot).
One core for the CLI and the Launchpad, coordinated by files and the
account's kernel lock: one JSON per request under the account's custody
(`requests/<id>.json`, 0600 in 0700), a random 24-character base32 id, and
never a value. The Launchpad's admitted, same-origin routes `GET
/api/vault/requests` (waiting ones), `GET /api/vault/requests/<id>`, `POST
.../<id>/save {values}` and `POST .../<id>/decline` let any admitted person
answer, without an account in the vault. A save, under the lock so a
request is answered once, writes exactly one Secure note named like the
request into the collection Agenti (a hidden field for each secret field,
the need, why and where as its notes) through `bw create item` on stdin,
syncs and marks the request saved; a refusal of the vault leaves it waiting.
Tests over the real consumers (`runVaultCommand`, `startLaunchpad` over HTTP,
locally and behind the gateway) against the fake Vaultwarden and a fake bw
that now creates items from stdin: create, list and status; invalid words
and fields; no vault, a pilot, too many; the save creating exactly one note
with the right fields in Agenti, read right away as a runtime secret; the
values in the vault only (not on argv, in a request file or any file of this
Platform, the journal or an answer); decline; values that are not the
fields; an unadmitted or cross-origin request refused; a refused or
unreachable vault keeping the request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at or a file DEV-6631 task 652, root decision 0198 point 9. The `bitwarden` tool's usage and the manual's Secrets section tell agents: never ask for a secret in chat and never take one from a file or a message; check the vault's state again with `lazurio vault status` before saying anything about it; read secrets only from the vault through the shared session and never print a value; and get what the vault lacks in this order: (a) where the shared browser window has the Bitwarden extension, the person signs in to the service there and confirms saving it into the Environment's vault (worded as conditional: the extension arrives with a later task); (b) a secret the agent can create itself in a signed-in session (a new API key or client secret) is created and stored in the vault with bw, piped through `bw encode` into `bw create item`, never as an argument; (c) otherwise `lazurio vault request`, phrased so the person understands what is wanted (what, why, where in the service), formal Czech, with the link handed over and other work continued meanwhile. This computer (the local preset) has no vault yet: its manual keeps the store the Operator names and gains only the rule against chat and files. Agent instructions stay in the Czech singular; the person-facing example addresses people formally. Template revision base-instructions-50 with its pinned digest and the snapshots; a test renders every preset in both languages and checks the order a, b, c in the manual and the commands of each path in the tool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s own page Root decision 0198 point 9 (c), DEV-6631 task 651, the wireframe's screens/SecretRequest.tsx and the section of EnvVault.tsx. Nastavení → Trezor (F53's addendum) gets the sub-route `/settings/vault/requests/<id>`, like Integrace's forms; the first-paint script and `pageRoute` agree on it. vault-requests-panel.ts draws "Žádosti agentů" (the count and one row per waiting request with "Zadat") into the Trezor page's slot `#vault-requests`, and the request's page into `#vault-request`, which this change adds as the last of the Trezor section. The page: the agent's need as its title, then why and where, one input per field (a password input with "Zobrazit" for a secret one), the item it becomes, "Uložit do trezoru" (enabled once every field has a value) and "Odmítnout". A value goes from its input into the one save request and nowhere else: not to browser storage, the address or a log; the inputs are emptied once the request is answered, and kept after a refusal the person can retry (the vault busy or unreachable). Formal Czech, minimal texts. The request id pattern is shared by the core and the routes (src/vault/request-id.ts), which import nothing. Tests: the routes of the section and the page, the first paint, the view's texts in both languages and its reading of the routes' answers, and the page serving both routes. Checked in a browser with the fake vault (section, filling in, show and hide, save, decline, an unreachable vault and the retry, a narrow screen). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t follow it The new decision F54 records root decision 0198 point 9 as built: the request and its CLI, the store beside the account's custody, the admitted routes, the one Secure note a save writes into Agenti through bw's standard input, the request page and section, the runtime secret read from a Secure note's one hidden field, the agents' guidance (paths a, b and c, template revision base-instructions-50) and the journal; with its honest limits (who answered is not recorded; a save interrupted after bw created the item can lead to a second item of the name; answered requests are kept), the alternatives and how it is verified. F46 and F53 point to it; environment-tools.md (custody, bw, CLI, Launchpad, runtime secrets, the agents' texts), launchpad-development.md (the page), module-adoption.md (a module secret from a Secure note, asked for with a request) and public-development.md (a value entered for a request passes through memory to bw's stdin and is never written) follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a command, never to print them `lazurio vault request status` said to read a saved item with `bw get item`, which prints the item with its values into the agent's output. The line now says to pass its fields from `bw get item` straight into the command that needs them, as the tool's usage text does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e requests fill its slot The Trezor page (F53's addendum) leaves the slot `<section class="group" id="vault-requests" hidden>` above its vault. The requests' panel fills that slot with its head and rows instead of a group of its own. On a request's page the panel marks the Trezor section (`data-request`) and only `#vault-request` shows of it, whatever else the Trezor page holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is unresolved Since #391's `creating` state (an organization whose creation is not resolved yet: a receipt with its time and no ids), the request core already refuses it as `not-connected`, as every vault that is not ready: it needs the recorded vault and Agenti by ID. A test now writes that record, in its four-field form with `pendingSince`, and shows a new request and a save refused while the status reads `creating`, and the waiting request saved once the vault is ready again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… F53, as main numbers them Main took F52 for the module generator and renumbered the vault decision to F53, so the secret-request decision is F54. The comments of the request core, its CLI, routes, page and tests, the bw driver, the runner, the catalog and the manual say so; the docs were renumbered in their own commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2a57a9c to
8d7f316
Compare
agentrozjedemeai
left a comment
There was a problem hiding this comment.
@immakermatty Exact-head re-review of 8d7f316 against main 1cc2ef6: APPROVED. I compared the prior approved stack via range-diff, inspected the #402 Trezor slot/request-only route, F54/F53 references and base-instructions-50 pin, and traced the retained request core, stdin-only bw create and Secure-note reader. In a fresh isolated clone, git diff --check and check:static passed; focused tests passed in two serial shards (23 + 74, 0 failed, 28 snapshots) under the host heavy-command lock. Current-head CI is green; no unresolved threads or competing review requests. I did not independently rerun the full suite, real-image e2e or browser smoke; the author reports those separately, with real-image e2e on the pre-rebase head. Known interrupted-create duplication and absent answerer attribution remain documented limits. No merge performed.
Motivation
Root decision 0198 point 9: a secret never reaches agents through chat or a file, and a person needs no account in the vault to hand one over. An agent gets it in this order:
Until now an agent had no way to do (c) and no instructions for the order. This PR builds (c) end to end and teaches agents a → b → c (DEV-6631 tasks 651 and 652). It builds on the vault (#391) and on Nastavení → Trezor (#402), both merged. Decision F54 records it.
Target state
lazurio vault requestcreates a request:--item <name> --need <what> --why <why> --where <where in the service> --field <Name>[:secret] ...(1–10 fields).<the Folder's recorded external origin>/settings/vault/requests/<id>. Where no entry is recorded, it prints the page's path instead.--jsongives the request withlink.request status <id>answerspending,saved(with the item's name) ordeclined;request listlists every request.creating), and never for a 0197 pilot.requests/<id>.json, 0600 in 0700) with a random 24-character base32 id, and never a value. The CLI never calls the Launchpad.GET /api/vault/requests(waiting),GET /api/vault/requests/<id>,POST …/<id>/save {values}andPOST …/<id>/decline {}. Any admitted person answers; no account in the vault is needed.bw create itemwith the encoded item on stdin (a newstdinoption of the ToolRunner), never on argv. Thenbw sync, and the request issaved./settings/vault/requests/<id>is a sub-route of Nastavení → Trezor, like Integrace's forms:#vault-requestsabove the vault: the count and one row per waiting request, each with "Zadat".ambiguous. The frozen 0197 pilot reads passwords only, as before.bitwardenusage text and the manual's Secrets section, template revisionbase-instructions-50. It tells agents:lazurio vault statusbefore saying anything about the vault;bw encodepiped intobw create item, never as an argument; then (c),lazurio vault requestin plain words and formal Czech, handing over the link and going on with other work.What changes
src/tools/status.ts: the ToolRunner takesstdin.src/vault/bw.ts:createNotesends the encoded note on stdin and reads only the new id from bw's answer. The item reader keeps a Secure note's hidden-field count and the value of the only one.src/vault/session.tsandflow.ts:readSecretsreads Secure notes for the own vault (notes: true), and the journal gains arequestoperation.src/vault/requests.ts(the core) andsrc/vault/request-id.ts(the import-free id pattern and page path).src/vault/cli.tsgainsrequest,request statusandrequest list.src/launchpad/vault-request-routes.ts, composed inserver.tson the samevaultHostOfas the Tools row and the Trezor page.routes.tsgets the request sub-route ofvault.index.htmlgets the first paint, the request page#vault-requestas the last child of Vault: Nastavení → Trezor and the Správci of the Environment's own vault (DEV-6631 tasks 648, 638b) #402's section, the comment of its slot, and CSS. Newvault-requests-panel.tsandvault-requests-view.ts, plus changes toui.tsandmessages.ts(en, and formal neutral cs).src/tools/catalog.tsandsrc/folder/manual.tscarry paths a, b and c.render.tsmoves tobase-instructions-50(main's Copy: address people formally (vykání) in Launchpad, shell, CLI and agents' speech #328 took 48 and Vault: each Environment owns its vault (root decision 0198, DEV-6631 task 638a) #391 took 49), with its pinned digest, the folder-tools test and the snapshots.environment-tools.md,launchpad-development.md,module-adoption.mdandpublic-development.md.tests/vault-requests.test.tsandtests/vault-requests-view.test.ts. Extended: thevault-secrets,vault-pilot,launchpad-routes,launchpad-first-paintandfolder-manualtests. The fake bw now creates items from stdin, as the real one does.What deliberately does not change
requests/is added beside it), and the frozen 0197 pilot's reading of runtime secrets.bitwardenpurpose shown to people is main's; the usage text is agents' instructions and stays in ty.Verification
bun run checkon head8d7f316fa0dd882708f3ccf27e937d958cea333a, rebased on main1cc2ef6: Biome,tsc, the public guard, the proof build and smoke, then 3528 pass, 8 skip, 0 fail across 251 files, exit 0 (650 s).GitHub's checks on this head passed: static checks, tests 1/2 and 2/2, check ubuntu-24.04 and scope; macOS and Macroscope skipped.
Contract tests on the real consumers, against the fake Vaultwarden and the fake bw (
tests/vault-requests.test.ts, 10 tests):creating);create item --nointeractiononly, followed by async;$HOME/bw.calls), request file, file of this Platform, journal or answer;F46:
tests/vault-secrets.test.tsandtests/vault-pilot.test.ts.The page:
tests/launchpad-routes.test.ts,tests/launchpad-first-paint.test.tsandtests/vault-requests-view.test.ts.The guidance:
tests/folder-manual.test.tsrenders every preset in both languages and checks the order a, b, c and each path's commands. The revision is pinned intests/folder-template-revision.test.ts.In a browser (root decision 0178), on this head: a throwaway Launchpad with the fake vault and two requests. The harness is outside the repository, at
~/Lazurio/drafts/DEV-6631-phase1/secret-requests/ui-check/serve.ts. Checked:bw.callsor the request files;Earlier on the pre-rebase branch: show/hide, decline, an unreachable vault and the retry, and a narrow screen. Screenshots (local, synthetic values):
~/Lazurio/drafts/DEV-6631-phase1/secret-requests/01…13-*.png.End to end against the real image
ghcr.io/lazurio/vaultwarden:1.37.4-lazurio.1@sha256:68da93bb…f492(amd64 under Rosetta in OrbStack) with the real pinned bw 2026.7.0 (macOS arm64), run on the pre-rebase head18aefea(the request path has not changed since). 22 of 22 checks passed:Client IDas text andClient secrethidden with the given values, and notes without a value;answered;vaultSecretsreads the note's hidden field;create item --nointeraction, and the items went on stdin;Everything was removed afterwards. The Linux bw binary was not exercised.
Risks, open questions, follow-ups
ambiguousuntil a person removes one. F54's limits record this.lazurio vault requestrefuse when the vault already holds an item of that name? The guidance tells agents to look first.Plan and work context
HumanAndMachine-ai_GEN3/.worktrees/productionspace/LazurioPlatform/DEV-6631-secret-requests, branchagent/DEV-6631-secret-requests, onmain.b643d1c5-bc04-44a8-a2c5-d80707571628(claude --resume b643d1c5-bc04-44a8-a2c5-d80707571628).Status: ready for review
🤖 Generated with Claude Code