1Password

The bundled onepassword plugin has two independent, opt-in surfaces:

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

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:

Enable the bundled plugin:

apus plugins enable onepassword

Create the token directory and file under the Apus state directory:

mkdir -p ~/.apus/credentials/onepassword
chmod 700 ~/.apus/credentials/onepassword
printf '%s' "$OP_SERVICE_ACCOUNT_TOKEN" > \
  ~/.apus/credentials/onepassword/service-account-token
chmod 600 ~/.apus/credentials/onepassword/service-account-token
unset OP_SERVICE_ACCOUNT_TOKEN

When APUS_STATE_DIR is set, replace ~/.apus 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:

apus onepassword secretref setup \
  --anthropic-id op://Automation/Anthropic/credential \
  --openrouter-id op://Automation/OpenRouter/credential \
  --plan-out ./apus-1password-secrets-plan.json

Use --provider-key <provider=id> for another model provider, or --target <path=id> for any registered SecretRef credential target. 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:

apus onepassword secretref status
apus secrets apply --from ./apus-1password-secrets-plan.json --dry-run --allow-exec
apus secrets apply --from ./apus-1password-secrets-plan.json --allow-exec
apus secrets audit --check --allow-exec
apus 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:

{
  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 Apus'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:

apus onepassword secretref status --json

Configure registered secrets

Add plugin config to apus.json:

{
  "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:

{ "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:

{
  "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

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. Apus 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.

Pending authorization reads and writes run on the SQLite worker. Tool execution waits for an approval's pending write to finish before consuming it; a failed write fails the request without retrieving a secret. Existing pending rows and their expiry remain compatible across updates.

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:

apus 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:

apus onepassword audit
apus 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

The SecretRef resolver runs op read. On a nonzero exit, its protocol error includes up to 512 characters from the end of stderr after secret redaction. It removes the service-account token and any captured secret stdout from that detail. Both output streams are capped at 64 KiB; exceeding either cap stops the read and returns an output-limit error without child output. Gateway degradation warnings still use generic provider failure reasons. After fixing the underlying problem, run apus secrets reload to retry cold owners.

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

Related