> ## Documentation Index
> Fetch the complete documentation index at: https://kernel.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Collect end-user credentials and inject them into browser forms while controlling the login workflow

Fill from Vault is an integration path where your application or agent controls the login workflow. vaults store encrypted credential items. our `fill` api call writes selected values from an item into browser fields, without exposing secrets to your application or agent. your application or agent owns navigation, field selection, submission, and recovery.

start with the [end-user auth workflow cookbook](/docs/browsers/use-vault-credentials-in-browser-agent) for an end-to-end example of secure collection, browser attachment, the `fill` operation, form submission, and cleanup.

KERNEL collects credential values from the user or accepts them from a trusted backend. when invoking `fill`, your controller sends field names and selectors rather than credential values. KERNEL reads the encrypted item and returns value-free outcomes, keeping stored secrets out of agent prompts and browser-automation payloads.

<Note>
  `fill` only writes stored values into fields you select. it doesn't discover fields, navigate, submit forms, verify authentication, monitor the session, or reauthenticate. your application or agent owns each of those steps.
</Note>

## When to use it

Fill from Vault works best when:

* a login or authentication prompt can appear in the middle of a workflow, in the same browser session.
* your product needs to control the credential collection experience.
* your application or agent already handles browser navigation and site-specific recovery.
* you don't need KERNEL to monitor the session or reauthenticate it automatically.

choose [managed auth](/docs/auth/managed-auth) instead when you want KERNEL to run the login flow, save the authenticated state, monitor the connection, and attempt reauthentication for eligible flows.

## How it works

these examples continue in order, using hacker news as the login destination. set `KERNEL_API_KEY` in your trusted backend environment. all examples use the default project; keep the vault and browser in the same project if you select a different one.

<Steps>
  <Step title="Create a Vault">
    create a [vault](/docs/vaults/overview) for each end user or credential-sharing boundary. A vault groups the items that an attached browser session can use.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import Kernel from "@onkernel/sdk";

      const kernel = new Kernel();
      const vault = await kernel.vaults.upsert({ name: "user-12345" });
      ```

      ```python Python theme={null}
      from kernel import Kernel

      kernel = Kernel()
      vault = kernel.vaults.upsert(name="user-12345")
      ```

      ```bash CLI theme={null}
      VAULT_NAME="user-12345"
      kernel vaults create --name "$VAULT_NAME"
      ```
    </CodeGroup>
  </Step>

  <Step title="Attach the Vault to a Browser">
    attach the vault when you create the browser. The attachment can't change during the session and grants access to every item in that vault.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const browser = await kernel.browsers.create({ vaults: [{ id: vault.id }] });
      ```

      ```python Python theme={null}
      browser = kernel.browsers.create(vaults=[{"id": vault.id}])
      ```

      ```bash CLI theme={null}
      kernel browsers create --vault "$VAULT_NAME" -o json
      read -r -p "paste the returned session_id: " BROWSER_ID
      ```
    </CodeGroup>
  </Step>

  <Step title="Navigate and Collect Credentials">
    your application or agent navigates to the login page and identifies its fields before defining a [credential item](/docs/vaults/credentials). hacker news has both login and create-account forms; the selectors in the next step target the login form. inspect the page and recheck them if it changes.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      await kernel.browsers.playwright.execute(browser.session_id, {
        code: "await page.goto('https://news.ycombinator.com/login'); return await page.title();",
      });
      const item = await kernel.vaults.items.upsert("hn-login", {
        id_or_name: vault.id,
        type: "credential",
        spec: {
          description: "Hacker News",
          fields: {
            username: { type: "text", required: true, sensitive: false },
            password: { type: "password", required: true, sensitive: true },
          },
        },
      });
      if (item.type !== "credential") throw new Error("expected a credential item");
      const collectionURL = item.action?.url;
      // Show collectionURL only to the intended user, not in general application logs.
      ```

      ```python Python theme={null}
      kernel.browsers.playwright.execute(
          browser.session_id,
          code="await page.goto('https://news.ycombinator.com/login'); return await page.title();",
      )
      item = kernel.vaults.items.upsert(
          "hn-login",
          id_or_name=vault.id,
          type="credential",
          spec={
              "description": "Hacker News",
              "fields": {
                  "username": {"type": "text", "required": True, "sensitive": False},
                  "password": {"type": "password", "required": True, "sensitive": True},
              },
          },
      )
      if item.type != "credential":
          raise RuntimeError("expected a credential item")
      collection_url = item.action.url if item.action else None
      # Show collection_url only to the intended user, not in general application logs.
      ```

      ```bash CLI theme={null}
      kernel browsers playwright execute "$BROWSER_ID" \
        "await page.goto('https://news.ycombinator.com/login'); return await page.title();"
      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}
        }
      }
      JSON
      ```
    </CodeGroup>

    present the collection url only in the intended user's authenticated interface or private conversation. don't log it or open it in the agent-controlled browser. wait for the user to finish before continuing. an existing ready item may omit the collection action; reuse it or follow [credential collection](/docs/vaults/credentials#collect-values-from-the-user) to reopen the form.
  </Step>

  <Step title="Invoke the Fill Operation">
    retrieve the same item, require readiness and an advertised `fill` operation, then invoke [`fill`](/docs/vaults/fill) with field names and selectors. readiness means values exist, not that the website has accepted them. your application must authorize the destination before filling.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const current = await kernel.vaults.items.retrieve(item.key, {
        id_or_name: vault.id,
        wait: 60,
      });
      if (current.id !== item.id || current.type !== "credential" ||
          current.state.status !== "ready" ||
          !current.available_operations.some((operation) => operation.type === "fill")) {
        throw new Error("credential is not ready to fill");
      }
      const result = await kernel.vaults.items.performOperation(item.key, {
        id_or_name: vault.id,
        type: "fill",
        browser_id: browser.session_id,
        page_url: "https://news.ycombinator.com/login",
        fields: [
          { field: "username", selector: "form:has(input[autocomplete='current-password']) input[name='acct']" },
          { field: "password", selector: "input[autocomplete='current-password']" },
        ],
      });
      if (result.type !== "fill" || result.status !== "completed") {
        throw new Error("stop and reconcile the fill outcome");
      }
      ```

      ```python Python theme={null}
      current = kernel.vaults.items.retrieve(item.key, id_or_name=vault.id, wait=60)
      if (current.id != item.id or current.type != "credential" or
              current.state.status != "ready" or
              not any(operation.type == "fill" for operation in current.available_operations)):
          raise RuntimeError("credential is not ready to fill")
      result = kernel.vaults.items.perform_operation(
          item.key,
          id_or_name=vault.id,
          type="fill",
          browser_id=browser.session_id,
          page_url="https://news.ycombinator.com/login",
          fields=[
              {"field": "username", "selector": "form:has(input[autocomplete='current-password']) input[name='acct']"},
              {"field": "password", "selector": "input[autocomplete='current-password']"},
          ],
      )
      if result.type != "fill" or result.status != "completed":
          raise RuntimeError("stop and reconcile the fill outcome")
      ```

      ```bash CLI theme={null}
      kernel vaults items get "$VAULT_NAME" hn-login --wait 60 -o json
      # Continue only if the same item is ready and advertises fill.
      kernel vaults items invoke "$VAULT_NAME" hn-login fill --spec-file - <<JSON
      {
        "browser_id": "$BROWSER_ID",
        "page_url": "https://news.ycombinator.com/login",
        "fields": [
          {"field": "username", "selector": "form:has(input[autocomplete='current-password']) input[name='acct']"},
          {"field": "password", "selector": "input[autocomplete='current-password']"}
        ]
      }
      JSON
      ```
    </CodeGroup>

    `completed` means the selected fields were filled, not that login succeeded. if `fill` fails, returns an uncertain outcome, or loses its response, stop and [inspect the outcome](/docs/vaults/fill#handle-the-outcome) rather than automatically retrying.
  </Step>

  <Step title="Submit and Handle the Response">
    after `fill` completes, your application or agent submits the login form once and verifies the site's response. `fill` doesn't perform either step. handle any additional authentication prompts before continuing the task.

    delete the demo browser when finished, and delete the vault only if you created it for this demo and no longer need its credentials. see the [cookbook](/docs/browsers/use-vault-credentials-in-browser-agent) for the complete agent handoff and cleanup guidance.
  </Step>
</Steps>

## Credential sources

you can collect values from an end user with KERNEL's hosted collection form or [copy them from an existing credential vault](/docs/vaults/existing-credential-vault) that your trusted backend can read. both paths produce a ready credential item and use the same `fill` operation.

today, copying values stores an encrypted KERNEL copy. `fill` doesn't accept raw values or a third-party vault reference in its request.

## Session state

`fill` completes one part of the workflow. it doesn't monitor the resulting session or reauthenticate it later. if you want to reuse the authenticated state, create the browser with a [profile](/docs/browsers/profiles) and save its changes after the login succeeds.

## Next steps

<CardGroup cols={3}>
  <Card title="Credential Items" icon="lock" href="/docs/vaults/credentials">
    define fields, collect values, and update credentials without returning sensitive values.
  </Card>

  <Card title="Fill Browser Fields" icon="input-text" href="/docs/vaults/fill">
    map credential fields to browser inputs and handle completed, failed, or unknown outcomes.
  </Card>

  <Card title="Build an End-User Auth Workflow" icon="user-lock" href="/docs/browsers/use-vault-credentials-in-browser-agent">
    follow the complete collection and browser fill workflow with the sdk or cli.
  </Card>
</CardGroup>
