Core Concepts

Secrets

Extract sensitive values from browser pages and use them securely in commands — without ever exposing the actual value to the LLM. Secret names are visible. Secret values never are.

How it works

Pagerunner's secret system has two parts:

  • extract_secret — runs JavaScript in a browser tab, captures the result, and stores it in the sealed store under a name you choose. The value is never printed to stdout or logged.
  • use_secret — runs a shell command with the secret value injected via stdin. The value flows directly to the command and is never visible in process arguments or logs.

The agent knows the secret exists (it can see the name) but never sees the value. This is the fundamental security guarantee.

Extracting a secret

Pull a value from a page and store it. The JavaScript expression runs in the tab context and the result goes straight to the sealed store.

extract secret
// Extract an API token from a settings page
extract_secret(
  session_id, target_id,
  js="document.querySelector('.token').textContent.trim()",
  name="npm_token"
)
→ Secret 'npm_token' stored successfully

Using a secret

Inject the secret value into a command via stdin. The value is never visible in the command arguments.

use secret
// Set a GitHub secret using the extracted token
pagerunner use-secret npm_token -- \
  gh secret set NPM_TOKEN --repos owner/repo
// The secret value flows via stdin, never in args

Managing secrets

ToolDescription
extract_secretEvaluate JS in a tab and store the result as a named secret.
use_secretRun a command with a secret injected via stdin.
list_secretsList secret names (names only — values are never shown).
delete_secretDelete a named secret from the sealed store.

Example: rotating an API key

api key rotation workflow
// 1. Navigate to the API key page
navigate(session_id, target_id, "https://app.com/settings/api")
wait_for(session_id, target_id, selector=".api-key")
// 2. Click "Regenerate" and confirm
click(session_id, target_id, "#regenerate-btn")
click(session_id, target_id, ".confirm-dialog .confirm")
wait_for(session_id, target_id, selector=".new-api-key")
// 3. Extract the new key (never seen by the agent)
extract_secret(session_id, target_id,
  js="document.querySelector('.new-api-key').textContent",
  name="api_key")
// 4. Deploy it to production
$ pagerunner use-secret api_key -- gh secret set API_KEY

Security model

  • Secret values are encrypted at rest in the sealed store (AES-256-GCM, key in macOS Keychain).
  • Values are never printed to stdout, stderr, or logs.
  • use_secret passes values via stdin, not command arguments (prevents ps leakage).
  • The LLM only ever sees the secret name and confirmation of operations — never the value itself.

Next: PII Anonymization →