openclaw-1p-sdk-resolver
v0.2.0
Published
OpenClaw exec secrets provider backed by 1Password SDK
Readme
openclaw-1p-sdk-resolver
[!NOTE] This resolver design aligns with OpenClaw exec-provider realities and is not 1Password's preferred integration approach. See the detailed analysis:
Resolver Security Alignment - 1Password Ideal vs OpenClaw Reality.
Production-focused OpenClaw exec secrets provider (jsonOnly: true) that resolves 1Password secret references via the official @1password/sdk using service account auth.
Features
- No-arg mode preserves OpenClaw exec-provider contract: reads request JSON from
stdinand writes response JSON tostdout. - Adds a safe-by-default CLI with subcommands for config inspection, validation, and guided setup.
- Uses service-account auth only via
OP_SERVICE_ACCOUNT_TOKEN. - Fails closed: malformed input, missing token, timeout, or SDK failures return valid JSON with empty
values. - Supports explicit
op://...passthrough and<item>/<field>mapping via config-driven default vault. - Strict caps for
stdinsize, id count, timeout, and concurrency. - No secret logging in CLI mode;
resolveoutput is redacted by default.
Install
pnpm add -g openclaw-1p-sdk-resolverVerify:
openclaw-1p-sdk-resolver --helpQuick Start (2 Minutes)
Assumes openclaw-1p-sdk-resolver is installed and available on your PATH.
- Set your service account token:
export OP_SERVICE_ACCOUNT_TOKEN="..."- Initialize resolver config (dry-run first, then write):
openclaw-1p-sdk-resolver config init --default-vault MainVault
openclaw-1p-sdk-resolver config init --default-vault MainVault --write- Check both sides of the integration:
openclaw-1p-sdk-resolver openclaw check --strict
openclaw-1p-sdk-resolver 1password check --strict- Generate provider JSON to paste into
openclaw.json:
openclaw-1p-sdk-resolver openclaw snippetUse In OpenClaw Config
Recommended flow:
- Validate current setup:
openclaw-1p-sdk-resolver openclaw check --strict- Generate provider JSON:
openclaw-1p-sdk-resolver openclaw snippet > provider-snippet.jsonSee OpenClaw Snippet below for snippet output behavior and guidance details.
- Paste that snippet manually under
secrets.providersinopenclaw.json.
Example provider entry:
{
"secrets": {
"providers": {
"1p-sdk-resolver": {
"source": "exec",
"command": "/absolute/path/to/openclaw-1p-sdk-resolver",
"jsonOnly": true,
"passEnv": ["HOME", "OP_SERVICE_ACCOUNT_TOKEN", "OP_RESOLVER_CONFIG"]
}
}
}
}OpenClaw Snippet
Generate a provider snippet to paste into openclaw.json:
openclaw-1p-sdk-resolver openclaw snippetThe snippet includes:
source: "exec"jsonOnly: true- command path guidance
passEnv:HOME,OP_SERVICE_ACCOUNT_TOKEN,OP_RESOLVER_CONFIG- This tool never edits OpenClaw files; paste snippet output manually.
- Snippet JSON is always written to
stdout; optional guidance is written tostderr. - Guidance includes likely OpenClaw config path and path-source reasoning.
Protocol
Input:
{ "protocolVersion": 1, "ids": ["MyAPI/token", "op://Vault/item/field"] }Output:
{ "protocolVersion": 1, "values": { "MyAPI/token": "<secret>" } }Unresolved IDs are omitted from values.
ID Mapping
- If id starts with
op://, resolver uses it unchanged. - Otherwise resolver maps with default vault:
MyAPI/token+defaultVault: "MainVault"- becomes
op://MainVault/MyAPI/token
Runtime Configuration
OP_SERVICE_ACCOUNT_TOKEN(required)OP_RESOLVER_CONFIG(optional absolute path override for config JSON)
Resolver config is loaded from:
OP_RESOLVER_CONFIG(explicit path override)$XDG_CONFIG_HOME/openclaw-1p-sdk-resolver/config.json$HOME/.config/openclaw-1p-sdk-resolver/config.json
Example config JSON:
{
"defaultVault": "MainVault",
"vaultPolicy": "default_vault+whitelist",
"vaultWhitelist": ["SharedVault"],
"allowedIdRegex": "^[A-Za-z0-9_\\/-]+$",
"maxIds": 50,
"maxStdinBytes": 131072,
"stdinTimeoutMs": 5000,
"timeoutMs": 25000,
"concurrency": 4,
"onePasswordClientName": "openclaw-1p-sdk-resolver",
"onePasswordClientVersion": "1.0.0"
}Defaults/caps:
maxIds: default50, hard cap200maxStdinBytes: default131072, hard cap1048576timeoutMs: default25000stdinTimeoutMs: default5000concurrency: default4, hard cap10onePasswordClientName: defaultopenclaw-1p-sdk-resolver(used as 1Password SDK client metadata)onePasswordClientVersion: default1.0.0(used as 1Password SDK client metadata)defaultVault: default"default"(also supports legacyvaultkey)vaultPolicy: default"default_vault""default_vault": only configureddefaultVaultallowed for explicitop://...refs"default_vault+whitelist":defaultVaultplusvaultWhitelist"any": any explicit vault allowed
vaultWhitelist: default[]
Vault source precedence:
defaultVaultfrom config file- fallback to legacy
vaultconfig key - fallback to built-in default
"default"
Removed keys:
integrationNameandintegrationVersionare no longer recognized.
CLI Commands
No args means resolver mode. Any recognized subcommand runs CLI mode and does not read protocol stdin.
openclaw-1p-sdk-resolver doctor [--json]
openclaw-1p-sdk-resolver config path [--json]
openclaw-1p-sdk-resolver config show [--json] [--defaults] [--current-file] [--verbose]
openclaw-1p-sdk-resolver config init [--default-vault <name>] [--write] [--force] [--json]
openclaw-1p-sdk-resolver openclaw check [--path <openclaw.json>] [--provider <alias>] [--json] [--strict] [--details]
openclaw-1p-sdk-resolver openclaw snippet [--provider <alias>] [--command <path>] [--explain] [--quiet]
openclaw-1p-sdk-resolver 1password check [--json] [--strict] [--details] [--probe-id <id>] [--probe-timeout-ms <n>] [--debug]
openclaw-1p-sdk-resolver 1password snippet [--default-vault <name>] [--full] [--json] [--explain] [--quiet]
openclaw-1p-sdk-resolver 1p <check|snippet> [...flags]
openclaw-1p-sdk-resolver resolve --id MyAPI/token [--id Other/item] [--stdin] [--json] [--debug] [--reveal --yes]Notes:
doctorreports config path resolution, effective config, provenance (default | config-file | env), validation warnings/errors, and token presence.config initis dry-run by default; pass--writeto persist.config initrequires--default-vault <name>unless an existing config file is already loaded withdefaultVault.config init --writerefuses overwrite unless--force.openclaw checkis the high-signal setup command:- validates OpenClaw config path and provider wiring (read-only)
- verifies 1Password connectivity sanity (token present + SDK init probe)
- returns findings with actionable next steps
openclaw check --detailsprovides deeper troubleshooting details including resolver config/provenance.openclaw snippetprints provider JSON only onstdoutso it can be pasted directly into OpenClaw config.1password checkis the high-signal 1Password readiness command:- validates resolver config
- checks token presence and SDK init status
- optionally probes a specific id/ref safely via
--probe-id(never prints values; probe id echoed only with--debug)
1password check --detailsprovides deep resolver and policy diagnostics.--strictturns findings into non-zero exit code (1) for automation gating.1password snippetprints resolver config JSON only onstdout(minimal by default, full config with--full).- This project is pre-release; removed CLI names/flags are not kept as compatibility aliases.
- Snippet instruction text is emitted on
stderronly:- default: only when
stderris a TTY --explain: force instructions onstderr--quiet: suppress instructions (--quietwins over--explain)- includes likely OpenClaw config path and path-source reasoning for paste guidance
- includes a blank separator line after instructions for readability
- default: only when
resolveis redacted by default and never prints secret values unless--revealis used.resolve --debugadds safe reason codes for unresolved ids (for examplepolicy-blocked,invalid-ref,sdk-unresolved) without revealing secrets.resolve --revealrequires explicit consent:- pass
--yesfor non-interactive runs - without
--yes, an interactive TTY confirmation prompt is required
- pass
- OpenClaw config path resolution precedence used by
openclaw check:
--path <openclaw.json>OPENCLAW_CONFIG_PATHOPENCLAW_STATE_DIR/openclaw.jsonOPENCLAW_HOME/openclaw.jsonHOME/.openclaw/openclaw.jsonos.homedir()/.openclaw/openclaw.json
Exit Codes
0(OK): command succeeded with no blocking issues.1(FINDINGS): command found non-fatal issues while running in--strictmode.2(ERROR): configuration/input problem (for example unreadable file or invalid config).3(RUNTIME): runtime dependency failure (for example SDK initialization failure).
Project Decision Artifacts
This repository keeps decision artifacts in two complementary forms:
- Formal Plans:
docs/plans/README.md- Pre-implementation plans for major/public changes.
- For AI-assisted work, this is the record produced in plan mode before execution.
- Serves as an evolution log of intended work and acceptance criteria.
- ADRs:
docs/adrs/README.md- Durable technical and architecture decisions with rationale.
- Captures enduring constraints that future plans and implementation must respect.
Versioning
This project follows Semantic Versioning 2.0.0. Pre-1.0 releases (0.y.z) may include breaking changes.
Release Workflow
Releases are CI-driven with Changesets.
- Releases are intentionally triggered via
Releaseworkflow dispatch (not on every merge). - Workflow modes:
prepare: opens/updates a release PR with version/changelog updates.publish: publishes frommainto npm via trusted publishing.
- Publishing runs from GitHub Actions (not developer machines).
Maintainer checklist (one-time setup):
- In npm package settings for
openclaw-1p-sdk-resolver, add GitHub repository trusted publishing for this repo/workflow. - In GitHub branch protection, require CI checks on
main. - Trigger
Releasewith modepreparefrommainto create/update the release PR. - Merge the release PR.
- Trigger
Releasewith modepublishfrommainto publish intentionally.
Contributor flow:
- Add a changeset in feature PRs:
pnpm changeset- Commit the generated
.changeset/*.mdfile with the code change.
Distribution
- Primary registry: npm (
openclaw-1p-sdk-resolver). - GitHub Releases are used for tagged source snapshots/release notes.
- GitHub Packages (npm registry) is not currently used for distribution.
dist/is generated at pack/publish time viaprepackand is not committed to git.
Minimal starter config generated by config init:
{
"defaultVault": "MainVault",
"vaultPolicy": "default_vault"
}Two-Sided Checks
- OpenClaw side:
openclaw-1p-sdk-resolver openclaw check --strictopenclaw-1p-sdk-resolver openclaw check --details --jsonopenclaw-1p-sdk-resolver openclaw snippet
- 1Password side:
openclaw-1p-sdk-resolver 1password check --strictopenclaw-1p-sdk-resolver 1password check --probe-id op://Vault/Item/field --jsonopenclaw-1p-sdk-resolver 1password check --details --jsonopenclaw-1p-sdk-resolver 1password snippet --default-vault MainVault
Architecture
Current source layout is intentionally small and split by responsibility:
src/protocol.ts- Parses stdin protocol JSON into a strict in-memory shape.
- Loads/normalizes config with defaults and hard caps.
- Provides effective config provenance and validation diagnostics.
- Formats response JSON.
src/sanitize.ts- Validates/sanitizes IDs from untrusted input.
- Maps IDs to
op://references. - Enforces vault policy checks for explicit refs.
src/onepassword.ts- Thin adapter over
@1password/sdkusing service account auth. - Uses
resolveAllwhen available, otherwise concurrency-limitedresolve. - Falls back to per-ref
resolvewhen bulk payloads are unsupported/empty. - Returns partial success maps; unresolved refs are omitted.
- Thin adapter over
src/cli.ts- Handles subcommand routing (
doctor,config,openclaw,1password,1p,resolve). - Keeps CLI output safe-by-default (no secret values unless explicit reveal).
- Handles subcommand routing (
src/openclaw.ts- Resolves OpenClaw config path precedence from env/flags.
- Parses OpenClaw config and validates resolver provider wiring.
- Builds provider snippet JSON for OpenClaw config.
src/resolver.ts- Entrypoint wiring:
- no subcommand: resolver mode
- recognized subcommand: CLI mode
- Orchestrates the runtime pipeline:
- read stdin with size/time caps
- parse request
- sanitize ids
- map/enforce vault policy
- resolve refs
- emit response
- Centralizes fail-closed behavior (always valid JSON, empty
valueson failure).
- Entrypoint wiring:
Limitations
- Protocol scope is intentionally narrow: OpenClaw
execprovider withjsonOnly: true. - Auth mode is service account token only (
OP_SERVICE_ACCOUNT_TOKEN). - Fail-closed behavior does not return per-id error details; unresolved IDs are simply omitted.
- Resolver may return partial success when some refs resolve and others fail.
- Timeouts are best-effort guards and can lead to empty responses under network/SDK delay.
- Vault access is constrained by configured
vaultPolicy(unless set to"any"). - This process necessarily places resolved secrets on stdout in the protocol response; stdout/stderr transcripts must be treated as sensitive.
Development Setup
pnpm install
pnpm buildpnpm publish runs prepack (pnpm build) to generate dist/ before packaging.
Test
pnpm testTests use fake resolver adapters and never call real 1Password.
Wrapper
A stable executable is provided at:
bin/openclaw-1p-sdk-resolver
It uses a portable Node shebang and a relative built resolver path:
#!/usr/bin/env node
import { runCli } from "../dist/resolver.js";
await runCli();If you want a user-global install, copy this wrapper to:
~/.local/bin/openclaw-1p-sdk-resolver
and update the import path if needed.
Smoke Test
echo '{"protocolVersion":1,"ids":["MyAPI/token"]}' | ./bin/openclaw-1p-sdk-resolverThis only returns values when OP_SERVICE_ACCOUNT_TOKEN is set and the target secret exists in an allowed vault.
Security Notes
- Never log resolved secret values.
- Never log raw stdin.
- Never pass service account token via CLI args or files.
config init --writeuses no-follow safe writes and refuses symlink paths.- Resolver should emit secrets only in protocol JSON on stdout.
- Errors are treated as unresolved (empty values) to avoid leaking details.
Troubleshooting
- Missing
OP_SERVICE_ACCOUNT_TOKEN: resolver returns{ "values": {} }. - Explicit
op://...ids outside vault policy are dropped before resolve. - Invalid JSON input: resolver returns empty values.
- Invalid IDs (empty, newline/NUL,
.., regex mismatch): dropped before resolve. - Vault permission issues / unknown ref: unresolved IDs are omitted.
- Timeout (
timeoutMstoo low or SDK/network delay): resolver returns empty values.
