From 8329f3e89379b0d251598e217243cc5fd7311cbb Mon Sep 17 00:00:00 2001 From: masnwilliams <43387599+masnwilliams@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:00:34 +0000 Subject: [PATCH] Document credential field labels --- snippets/collect-browser-credentials.mdx | 60 +++++++++++++++---- vaults/credentials.mdx | 76 +++++++++++++++++------- 2 files changed, 104 insertions(+), 32 deletions(-) diff --git a/snippets/collect-browser-credentials.mdx b/snippets/collect-browser-credentials.mdx index dc6c4ab..de36e1e 100644 --- a/snippets/collect-browser-credentials.mdx +++ b/snippets/collect-browser-credentials.mdx @@ -9,10 +9,22 @@ const item = await kernel.vaults.items.upsert("hn-login", { type: "credential", spec: { description: "Hacker News", - fields: { - username: { type: "text", required: true, sensitive: false }, - password: { type: "password", required: true, sensitive: true }, - }, + fields: [ + { + name: "username", + label: "Username", + type: "text", + required: true, + sensitive: false, + }, + { + name: "password", + label: "Password", + type: "password", + required: true, + sensitive: true, + }, + ], }, }); if (item.type !== "credential") throw new Error("expected a credential item"); @@ -31,10 +43,22 @@ item = kernel.vaults.items.upsert( type="credential", spec={ "description": "Hacker News", - "fields": { - "username": {"type": "text", "required": True, "sensitive": False}, - "password": {"type": "password", "required": True, "sensitive": True}, - }, + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": True, + "sensitive": False, + }, + { + "name": "password", + "label": "Password", + "type": "password", + "required": True, + "sensitive": True, + }, + ], }, ) if item.type != "credential": @@ -49,10 +73,22 @@ kernel browsers playwright execute "$BROWSER_ID" \ kernel vaults credentials create "$VAULT_NAME" hn-login --spec-file - <<'JSON' { "description": "Hacker News", - "fields": { - "username": {"type": "text", "required": true, "sensitive": false}, - "password": {"type": "password", "required": true, "sensitive": true} - } + "fields": [ + { + "name": "username", + "label": "Username", + "type": "text", + "required": true, + "sensitive": false + }, + { + "name": "password", + "label": "Password", + "type": "password", + "required": true, + "sensitive": true + } + ] } JSON ``` diff --git a/vaults/credentials.mdx b/vaults/credentials.mdx index 6bc80c2..95eaef5 100644 --- a/vaults/credentials.mdx +++ b/vaults/credentials.mdx @@ -31,10 +31,22 @@ const item = await kernel.vaults.items.upsert("portal-login", { type: "credential", spec: { description: "Account Portal", - fields: { - username: { type: "email", required: true, sensitive: false }, - password: { type: "password", required: true, sensitive: true }, - }, + fields: [ + { + name: "username", + label: "Email address", + type: "email", + required: true, + sensitive: false, + }, + { + name: "password", + label: "Password", + type: "password", + required: true, + sensitive: true, + }, + ], }, }); ``` @@ -54,10 +66,22 @@ item = kernel.vaults.items.upsert( type="credential", spec={ "description": "Account Portal", - "fields": { - "username": {"type": "email", "required": True, "sensitive": False}, - "password": {"type": "password", "required": True, "sensitive": True}, - }, + "fields": [ + { + "name": "username", + "label": "Email address", + "type": "email", + "required": True, + "sensitive": False, + }, + { + "name": "password", + "label": "Password", + "type": "password", + "required": True, + "sensitive": True, + }, + ], }, ) ``` @@ -68,6 +92,8 @@ use only the recognizable site or service name for `description`. it's the colle | field setting | behavior | | --- | --- | +| `name` | required stable machine key for item state, value updates, and browser fill | +| `label` | optional human-readable form text; falls back to `name` when omitted | | `type` | `text`, `email`, `password`, or `totp` | | `required` | defaults to `true`; all required fields must have values for the item to become ready | | `sensitive` | defaults to `true`; leave it `true` for write-only values, or set it `false` only when your backend or collection form needs to read and prefill the value | @@ -75,9 +101,11 @@ use only the recognizable site or service name for `description`. it's the colle password and totp fields must be sensitive. usernames and email addresses can also remain sensitive; `fill` works either way. fields marked `sensitive: false` return their stored values in item responses. +`fields` is an ordered array. define fields in the same top-to-bottom order as the website; collection forms preserve it exactly. `name` remains authoritative for `state.fields`, updates, and `fill`. `label` only changes display text and is returned as non-secret metadata. labels must be nonempty, trimmed, single-line display text of at most 128 utf-8 bytes. + an item accepts 1–32 named fields. field names must start with an ascii letter, contain only ascii letters, numbers, and underscores, and be at most 64 characters. `email` values must be bare valid addresses such as `user@example.com`; use `text` for usernames that aren't valid email addresses. initial values must be nonempty strings and fit within 16 kib of utf-8 data each; `null` and empty strings aren't accepted on creation. -field names, types, required flags, and sensitivity are immutable. repeating the same creation request retrieves the current item without overwriting later edits. use an update for value changes; use a new item key for a different field definition. +field names, labels, types, required flags, and sensitivity are immutable. repeating the same creation request retrieves the current item without overwriting later edits. use an update for value changes; use a new item key for a different field definition. ### Copy values from an existing vault @@ -123,20 +151,24 @@ const item = await kernel.vaults.items.upsert("portal-login", { type: "credential", spec: { description: "Account Portal", - fields: { - username: { + fields: [ + { + name: "username", + label: "Email address", type: "email", required: true, sensitive: true, value: credential.username, }, - password: { + { + name: "password", + label: "Password", type: "password", required: true, sensitive: true, value: credential.password, }, - }, + ], }, }); if (item.type !== "credential" || item.state.status !== "ready") { @@ -163,20 +195,24 @@ item = kernel.vaults.items.upsert( type="credential", spec={ "description": "Account Portal", - "fields": { - "username": { + "fields": [ + { + "name": "username", + "label": "Email address", "type": "email", "required": True, "sensitive": True, "value": credential["username"], }, - "password": { + { + "name": "password", + "label": "Password", "type": "password", "required": True, "sensitive": True, "value": credential["password"], }, - }, + ], }, ) if item.type != "credential" or item.state.status != "ready": @@ -212,11 +248,11 @@ poll the item with a bounded wait until its status is `ready`. readiness means r invoke the advertised `collect` operation to reopen collection for a ready item. this doesn't clear its values. record its `version` before opening the form, then retrieve without `wait` to observe edits: `wait` waits for readiness, not changes to an already-ready item. a version change can also come from an api update, so it doesn't identify a particular form submission. -the form includes text, email, and password fields, not totp. non-sensitive values can be prefilled; stored secrets aren't revealed. required visible inputs must be populated on submission. +the form includes text, email, and password fields in declaration order, not totp. it renders `label` when present and falls back to `name`. non-sensitive values can be prefilled; stored secrets aren't revealed. required visible inputs must be populated on submission. ## Read and update values -`spec.fields` contains definitions, never initial values. `state.fields` reports `has_value` for each field. it also returns `value` when the field is populated and `sensitive: false`. sensitive values aren't returned. +`spec.fields` contains ordered definitions, including optional labels, but never initial values. `state.fields` remains keyed by stable field `name` and reports `has_value` for each field. it also returns `value` when the field is populated and `sensitive: false`. sensitive values aren't returned. updates require `type: "credential"` and the latest `version`. send only fields you want to change. bind forms to the immutable item id with `expected_item_id` so a deleted and recreated key can't receive an old form's submission. @@ -273,6 +309,6 @@ KERNEL generates a fresh six-digit code immediately before filling, using hmac-s your authenticated backend can retrieve safe item state and submit value updates using the ordinary item api. authenticate your own users, authorize their access to the vault and immutable item id, and apply the version precondition. keep broad KERNEL api credentials out of the frontend. -render every form-supported field, omit totp, prefill only non-sensitive values, and don't log submitted values. for an unchanged form, use your own completion callback rather than an empty update. KERNEL doesn't store or authenticate your custom collection url. +render every form-supported field in declaration order, display `label` with a `name` fallback, keep controls and updates keyed by `name`, omit totp, prefill only non-sensitive values, and don't log submitted values. for an unchanged form, use your own completion callback rather than an empty update. KERNEL doesn't store or authenticate your custom collection url. continue with [fill browser fields](/vaults/fill) or the [browser agent cookbook](/browsers/use-vault-credentials-in-browser-agent).