Skip to content

feat: capy add <VAR> --web — local browser secret intake#258

Open
cvince wants to merge 3 commits into
mainfrom
feat/add-web-intake
Open

feat: capy add <VAR> --web — local browser secret intake#258
cvince wants to merge 3 commits into
mainfrom
feat/add-web-intake

Conversation

@cvince

@cvince cvince commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Adds capy add <VAR> with a --web mode: a local browser form where you paste a secret value. The value goes browser → local CLI → encrypted .env → synced, and never touches stdout, logs, or (when driven by an agent) the model context.

Why

The safe way for an AI agent to get a human to supply a secret without the value entering the model. An MCP tool will shell out to capy add <VAR> --web, print/open the URL, and only ever see a success status — never the value.

Behavior

  • capy add <VAR> --web — starts a loopback server on 127.0.0.1 (random port), prints + auto-opens a single-use URL, serves a styled form (multiline <textarea>, so private keys / multi-line secrets work), and on submit runs the existing resolveContext() + writeAndSync() tail — i.e. reuses the exact encrypt → keep.lock merge → push → sync-state path that capy connect uses. No new crypto.
  • capy add <VAR> (no --web) — interactive single-line prompt.
  • Flags: --reason <text> and --help-url <url> (shown on the page), --no-open (print URL only, for headless/agents), --no-push, -f/--force, --non-tty.

Security

  • Single-use, constant-time nonce in the URL (timingSafeEqual).
  • Host + Origin pinned to the exact loopback address we bound — defends against DNS-rebinding from a malicious page. POST to /submit is rejected otherwise.
  • One submission only; 5-minute timeout; connections force-destroyed on cleanup (mirrors oauthServer.ts).
  • 1 MB body cap; var name validated; value HTML/JS-escaped where echoed in the page.
  • The value is returned in-process to writeAndSync — never printed or logged.

New/changed

  • src/commands/addCommand.ts (command + loopback intake server)
  • src/commands/intakeSecurity.ts (nonce / Host / Origin guards — unit-tested)
  • src/ui/intakePage.ts (the form, styled with the existing deploy-page CSS)
  • src/index.ts (command registration, mirrors connect)

Verification

  • tsc --noEmit clean, tsc build clean.
  • capy add --help lists all options.
  • 10 unit tests (nonce constant-time equality, Host/Origin pinning, form rendering + var-name escaping).
  • Please test the browser flow live before merging (open the URL, paste a value, confirm it lands encrypted in .env + syncs) — that path is inherently manual.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant