feat(1password): bridge op CLI for devcontainer / remote use#624
Open
theoephraim wants to merge 1 commit intomainfrom
Open
feat(1password): bridge op CLI for devcontainer / remote use#624theoephraim wants to merge 1 commit intomainfrom
op CLI for devcontainer / remote use#624theoephraim wants to merge 1 commit intomainfrom
Conversation
Inside a devcontainer the `op` CLI can't reach the host's 1Password desktop app for biometric auth, forcing users to fall back to service account tokens. This adds a `varlock-op-bridge` binary shipped with @varlock/1password-plugin that runs on the host and proxies `op` invocations over TCP or a Unix socket. The plugin detects VARLOCK_OP_BRIDGE_SOCKET and routes every `op` call through the bridge transparently, so `op` doesn't even need to be installed inside the container and host biometric auth keeps working. - `varlock-op-bridge serve` + idempotent `ensure` subcommands (suitable for devcontainer `initializeCommand`) - Token-based auth: 32-byte random token rotated per `ensure`, 0600 on host, bind-mounted read-only into the container - TCP and Unix-socket transports; TCP recommended for Docker Desktop on macOS where Unix socket bind-mounts are unreliable - Client-side env blocklist so container-side HOME/USER/PATH can't clobber the host's values when running op - argv[0] renaming so 1Password's auth prompt shows "varlock-op-bridge" rather than "node" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: e28b181 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Inside a devcontainer the
opCLI can't reach the host's 1Password desktop app for biometric auth, forcing users to fall back to service account tokens. This PR adds avarlock-op-bridgebinary shipped with@varlock/1password-pluginthat runs on the host and proxiesopinvocations over TCP (or a Unix socket). The plugin detectsVARLOCK_OP_BRIDGE_SOCKETand routes everyopcall through the bridge transparently — soopdoesn't need to be installed in the container and host biometric auth keeps working.varlock-op-bridgeCLI withserve+ idempotentensure(suitable for devcontainerinitializeCommand)ensure, 0600 on host, bind-mounted read-only into the containerHOME/USER/PATHwhen runningopvarlock-op-bridgeinstead ofnodeArchitecture
Single chokepoint in the plugin (cli-helper.ts): if
VARLOCK_OP_BRIDGE_SOCKETis set, route through the bridge; else spawnoplocally. No behavior change for existing users.Setup (user-facing)
Prereqs on the host
No host install of anything else is required —
varlock-op-bridgeships with@varlock/1password-pluginand can be invoked vianpx/pnpm dlx/bunx.Add to your
devcontainer.json{ // Runs on the HOST before the container starts; spins up the bridge if not // already running and rotates the shared-secret token. Idempotent. "initializeCommand": "npx -y -p @varlock/1password-plugin varlock-op-bridge ensure --addr 0.0.0.0:7195", // Inside the container, point the plugin at the host bridge and the token. "containerEnv": { "VARLOCK_OP_BRIDGE_SOCKET": "host.docker.internal:7195", "VARLOCK_OP_BRIDGE_TOKEN_FILE": "/run/varlock-op-bridge.token" }, // Bind-mount the token file (0600 on host) read-only into the container. "mounts": [{ "source": "${localEnv:HOME}/.varlock-op-bridge.token", "target": "/run/varlock-op-bridge.token", "type": "bind", "readonly": true }], // Linux hosts need this so `host.docker.internal` resolves. Harmless on macOS/Windows. "runArgs": ["--add-host=host.docker.internal:host-gateway"] }The other package-manager variants of the
initializeCommand:Why TCP on
0.0.0.0:7195?host.docker.internalis the portable default.0.0.0.0(not127.0.0.1) because container connections arrive from Docker's VM gateway IP (e.g.192.168.65.254), not loopback.0.0.0.0doesn't widely expose the port. Still, the token auth is the real gate.--addr /tmp/varlock-op-bridge.sockand use the same path forVARLOCK_OP_BRIDGE_SOCKET+ themountssource.Confirming it's working
Inside the container:
First call per 1Password session pops a biometric prompt on the host — it says "varlock-op-bridge wants to access 1Password" (not "node"). Approving once covers all subsequent calls until 1Password's session expires.
Troubleshooting
connect ECONNREFUSED 192.168.65.254:7195— bridge is bound to 127.0.0.1 instead of 0.0.0.0. Use--addr 0.0.0.0:7195ininitializeCommand.invalid mount config for type "bind": bind source path does not exist: /socket_mnt/...— you're on macOS using Unix-socket mode; switch to TCP.cannot create directory "/home/<user>/.config/op"— container is leaking itsHOMEto the host. Fixed server-side via env-blocklist; pull latest.op bridge error: ENOENT: \op` not found on host** — install theop` CLI on the host.unauthorized: invalid or missing bridge token— themountsentry for the token file is missing, or theVARLOCK_OP_BRIDGE_TOKEN_FILEenv var path is wrong.~/.varlock-op-bridge.logon the host.Security notes
~/.varlock-op-bridge.tokenif they have the user's UID (0600 file perms). Same threat model as SSH keys.opsession for any caller with the token — it doesn't preserve 1Password's per-caller prompts. See discussion in src/bridge/server.ts.Test plan
opon PATH (success, non-zero exit →ExecError, env-overlay, ENOENT)ensureidempotent: fresh start, reuse, stale socket cleanup, token rotation restarts old bridgehost.docker.internalinside devcontainer (verified end-to-end in a throwaway repo)bun run typecheck,bun run build, andbun run lint:fixall clean🤖 Generated with Claude Code