mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 19:46:18 +00:00
329 lines
13 KiB
Markdown
329 lines
13 KiB
Markdown
---
|
|
summary: "Resolve SecretRefs and give agents curated, audited access to 1Password"
|
|
read_when:
|
|
- You want agents to request curated 1Password secrets
|
|
- You want OpenClaw config credentials to resolve from 1Password
|
|
- You need per-secret approval policy and audit history
|
|
- You are configuring a 1Password service account for OpenClaw
|
|
title: "1Password"
|
|
---
|
|
|
|
# 1Password
|
|
|
|
The bundled `onepassword` plugin has two independent, opt-in surfaces:
|
|
|
|
- a managed exec provider that resolves configured [SecretRefs](/gateway/secrets)
|
|
during Gateway startup, reload, audit, and apply preflight
|
|
- a policy-controlled agent tool that reads a curated set of 1Password fields
|
|
|
|
Both use the official `op` CLI and the same service-account token file. Enabling
|
|
the plugin alone does not expose the agent tool: that surface also requires a
|
|
configured item registry.
|
|
|
|
## Security model
|
|
|
|
- Service-account authentication only. The token stays in a local credentials
|
|
file and is never accepted in `openclaw.json`.
|
|
- Curated agent registry only. Agents can list configured slugs, but the plugin
|
|
never enumerates a 1Password vault. SecretRef reads are limited to references
|
|
explicitly stored on registered OpenClaw credential targets.
|
|
- Per-slug `auto`, `approve`, or `deny` policy.
|
|
- Approval grants expire. A cached value never bypasses current policy.
|
|
- Every access attempt is recorded in OpenClaw's shared SQLite state. Audit
|
|
rows include the supplied reason; keep reasons non-sensitive. The broker
|
|
never copies a fetched value or the service token into an audit row.
|
|
- After the current tool execution, OpenClaw-owned transcript persistence
|
|
replaces a successful `get` value with redacted metadata.
|
|
- The value is model-visible for that execution. If the model copies it into a
|
|
later tool call or reply, that separate record is outside this plugin's
|
|
persistence hook. Keep policies narrow and do not ask the model to echo a
|
|
value.
|
|
- The plugin invokes `op` once per cache miss. It does not retry rate limits or
|
|
other failures.
|
|
- Each `op` call runs with a minimal environment that disables 1Password
|
|
desktop-app integration (`OP_LOAD_DESKTOP_APP_SETTINGS=false`,
|
|
`OP_BIOMETRIC_UNLOCK_ENABLED=false`), so a 1Password app installed on the
|
|
Gateway host never triggers biometric or macOS permission dialogs.
|
|
|
|
Give the service account read access only to the vaults and items used by
|
|
registered SecretRefs and agent-tool slugs.
|
|
|
|
## Before you begin
|
|
|
|
You need:
|
|
|
|
- the 1Password CLI (`op`) installed on the Gateway host
|
|
- a 1Password service account with access to the selected items
|
|
- a dedicated service-account token file
|
|
|
|
Enable the bundled plugin:
|
|
|
|
```bash
|
|
openclaw plugins enable onepassword
|
|
```
|
|
|
|
Create the token directory and file under the OpenClaw state directory:
|
|
|
|
```bash
|
|
mkdir -p ~/.openclaw/credentials/onepassword
|
|
chmod 700 ~/.openclaw/credentials/onepassword
|
|
printf '%s' "$OP_SERVICE_ACCOUNT_TOKEN" > \
|
|
~/.openclaw/credentials/onepassword/service-account-token
|
|
chmod 600 ~/.openclaw/credentials/onepassword/service-account-token
|
|
unset OP_SERVICE_ACCOUNT_TOKEN
|
|
```
|
|
|
|
When `OPENCLAW_STATE_DIR` is set, replace `~/.openclaw` with that directory.
|
|
The plugin warns once when the token file is readable or writable by group or
|
|
other users.
|
|
|
|
## Configure SecretRefs
|
|
|
|
Create a secrets apply plan for common model provider keys:
|
|
|
|
```bash
|
|
openclaw onepassword secretref setup \
|
|
--anthropic-id op://Automation/Anthropic/credential \
|
|
--openrouter-id op://Automation/OpenRouter/credential \
|
|
--plan-out ./openclaw-1password-secrets-plan.json
|
|
```
|
|
|
|
Use `--provider-key <provider=id>` for another model provider, or
|
|
`--target <path=id>` for any registered
|
|
[SecretRef credential target](/reference/secretref-credential-surface).
|
|
The command requires at least one target and writes a plan. Inspect it, check
|
|
the local `op` and token-file prerequisites, then apply and reload:
|
|
|
|
```bash
|
|
openclaw onepassword secretref status
|
|
openclaw secrets apply --from ./openclaw-1password-secrets-plan.json --dry-run --allow-exec
|
|
openclaw secrets apply --from ./openclaw-1password-secrets-plan.json --allow-exec
|
|
openclaw secrets audit --check --allow-exec
|
|
openclaw secrets reload
|
|
```
|
|
|
|
Before apply, status can report that the provider itself is not configured yet;
|
|
`prerequisites ready: yes` confirms that the trusted `op` executable and an
|
|
accepted non-empty token file are ready. After apply, `ready: yes` confirms both the
|
|
provider wiring and prerequisites. Missing or unsafe prerequisites produce
|
|
actionable next steps without printing the token or raw resolver errors.
|
|
|
|
Manual provider configuration uses the existing plugin id:
|
|
|
|
```json5
|
|
{
|
|
plugins: {
|
|
entries: {
|
|
onepassword: { enabled: true },
|
|
},
|
|
},
|
|
secrets: {
|
|
providers: {
|
|
onepassword: {
|
|
source: "exec",
|
|
pluginIntegration: {
|
|
pluginId: "onepassword",
|
|
integrationId: "onepassword",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
models: {
|
|
providers: {
|
|
openai: {
|
|
apiKey: {
|
|
source: "exec",
|
|
provider: "onepassword",
|
|
id: "op://Automation/OpenAI/credential",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
References use `op://<vault>/<item>/<field>` or
|
|
`op://<vault>/<item>/<section>/<field>`. Vault, item, section, and field names
|
|
may contain spaces. The setup command stores references that do not fit
|
|
OpenClaw's shared exec-id grammar in a plugin-local opaque form and decodes them
|
|
only inside the resolver. Very long references should use stable 1Password IDs;
|
|
they are shorter and reduce the number of 1Password API requests.
|
|
|
|
The SecretRef resolver runs at most four `op read` processes concurrently,
|
|
disables the 1Password CLI cache so reloads observe rotated values, never uses
|
|
desktop-app integration, and does not expose an agent tool for arbitrary reads.
|
|
Before passing the service-account token, both plugin surfaces
|
|
resolve the executable and reject paths that another local account can replace;
|
|
Windows ACL verification must also succeed. Check provider wiring and local
|
|
readiness with:
|
|
|
|
```bash
|
|
openclaw onepassword secretref status --json
|
|
```
|
|
|
|
## Configure registered secrets
|
|
|
|
Add plugin config to `openclaw.json`:
|
|
|
|
```jsonc
|
|
{
|
|
"plugins": {
|
|
"entries": {
|
|
"onepassword": {
|
|
"enabled": true,
|
|
"config": {
|
|
"vault": "Automation",
|
|
"defaultPolicy": "approve",
|
|
"cacheTtlSeconds": 300,
|
|
"grantTtlHours": 720,
|
|
"opTimeoutMs": 15000,
|
|
"items": {
|
|
"repository-token": {
|
|
"item": "Repository automation token",
|
|
"field": "credential",
|
|
"policy": "approve",
|
|
"description": "Token for repository automation",
|
|
},
|
|
"model-key": {
|
|
"item": "Model provider key",
|
|
"vault": "Agent credentials",
|
|
"policy": "auto",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
Slugs use lowercase letters, numbers, and hyphens, start with a letter or
|
|
number, and contain at most 64 characters. A registry can contain up to 32
|
|
slugs; descriptions can contain up to 200 characters. `field` accepts one field
|
|
label or ID, must not contain a comma, and defaults to `credential`.
|
|
An item-level `vault` overrides the default vault. `opBin` can set an absolute
|
|
path to the `op` executable; otherwise the plugin resolves `op` from `PATH`.
|
|
Item titles must not start with a hyphen.
|
|
|
|
## Use the agent tool
|
|
|
|
The tool name is `onepassword`.
|
|
|
|
List registered slugs:
|
|
|
|
```json
|
|
{ "action": "list" }
|
|
```
|
|
|
|
The result contains only the slug, description, policy, and whether a standing
|
|
grant is active. It never contains a secret value and does not query 1Password.
|
|
|
|
Request one secret:
|
|
|
|
```json
|
|
{
|
|
"action": "get",
|
|
"slug": "repository-token",
|
|
"reason": "Authenticate the requested repository operation"
|
|
}
|
|
```
|
|
|
|
`reason` is required, must be non-empty, and is limited to 300 characters. A
|
|
successful `get` returns the value plus the configured slug, item title, and
|
|
field label.
|
|
|
|
The tool schema also declares an internal `authorizationNonce` parameter. The
|
|
policy layer injects it after evaluating the request to hand the authorization
|
|
to the executing tool call. Never set it manually: the policy hook overwrites
|
|
any supplied value, and an unknown value fails the request.
|
|
|
|
## Policy tiers and approvals
|
|
|
|
- `auto`: fetch immediately and audit the request.
|
|
- `deny`: block and audit the request.
|
|
- `approve`: use an unexpired standing grant, or ask a human to allow once,
|
|
always, or deny.
|
|
|
|
Allow once authorizes only the current tool call. Allow always writes a standing
|
|
grant for that agent and slug to SQLite; other agents must receive their own
|
|
approval. OpenClaw offers allow always only when the caller has a concrete agent
|
|
identity. The grant expires after `grantTtlHours`, which defaults to 720 hours.
|
|
An unresolved or timed-out approval denies the request; the maximum approval
|
|
wait is 600 seconds. The plugin retains up to 1,024 standing grants; at that
|
|
bound, the oldest grant is evicted and its agent must approve the next access.
|
|
|
|
Each evaluated authorization is single-use and is handed to the executing tool
|
|
call through shared SQLite state, so the handoff also works when more than one
|
|
plugin instance is active in the gateway process. Unused authorizations expire
|
|
after the 600-second approval window.
|
|
|
|
The in-memory cache defaults to 300 seconds and is bounded by the configured
|
|
slug registry. Set `cacheTtlSeconds` to `0` to disable it. Policy is evaluated
|
|
before every cache lookup, and cache hits are audited. Runtime config reloads
|
|
take effect at each policy and execution boundary; disabling the plugin or
|
|
removing, denying, or retargeting a slug invalidates pending authorization and
|
|
cached values.
|
|
|
|
## Inspect status and audit history
|
|
|
|
Show readiness and registry counts:
|
|
|
|
```bash
|
|
openclaw onepassword status
|
|
```
|
|
|
|
This reports whether the token file exists, whether `op` resolved and its path,
|
|
the registered item count, and per-policy counts. It never reads or prints the
|
|
token or secret values.
|
|
|
|
Show the 50 most recent audit rows:
|
|
|
|
```bash
|
|
openclaw onepassword audit
|
|
openclaw onepassword audit --limit 100
|
|
```
|
|
|
|
Rows are newest first and show timestamp, agent, slug, outcome, an `errorCode`
|
|
when the attempt failed, and a truncated reason. The reason is stored as
|
|
supplied; the broker never adds the fetched value to the audit log.
|
|
|
|
## 1Password CLI behavior
|
|
|
|
Each cache miss runs `op item get` with the configured item, vault, and exact
|
|
field selector, JSON output, a bounded timeout, and `--cache=false`. The child
|
|
receives only that field rather than the full item. Only
|
|
`OP_SERVICE_ACCOUNT_TOKEN` and `HOME` are present in the child environment.
|
|
|
|
The plugin makes one attempt. `RATE_LIMITED` errors should be handled by waiting
|
|
before a later agent request; the plugin does not create an automatic retry
|
|
loop.
|
|
|
|
## Error codes
|
|
|
|
Failed attempts carry one closed error code in the tool result and the audit
|
|
row.
|
|
|
|
1Password access errors:
|
|
|
|
| Code | Meaning |
|
|
| ----------------- | ---------------------------------------------------------------- |
|
|
| `TOKEN_MISSING` | Token file is missing or empty |
|
|
| `OP_NOT_FOUND` | `op` binary could not be resolved |
|
|
| `ITEM_NOT_FOUND` | Configured item is not in the vault |
|
|
| `FIELD_NOT_FOUND` | Configured field is not on the item; available labels are listed |
|
|
| `RATE_LIMITED` | 1Password service-account rate limit reached |
|
|
| `AUTH_FAILED` | Service-account authentication failed |
|
|
| `TIMEOUT` | `op` exceeded `opTimeoutMs` |
|
|
| `OP_ERROR` | Any other `op` failure or invalid output |
|
|
|
|
Policy and validation errors:
|
|
|
|
| Code | Meaning |
|
|
| -------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
| `INVALID_ACTION`, `INVALID_REASON`, `INVALID_SLUG` | Request failed input validation |
|
|
| `UNKNOWN_SLUG` | Slug is not in the configured registry |
|
|
| `TOOL_CALL_ID_MISSING` | Call arrived without a tool call id |
|
|
| `POLICY_NOT_EVALUATED` | No matching authorization for this call; the request was not policy-approved |
|
|
| `POLICY_CHANGED` | Config changed between approval and execution |
|
|
| `GRANT_EXPIRED` | Standing grant lapsed before execution |
|
|
| `APPROVAL_CANCELLED` | The run was aborted while the approval was pending |
|