pop-pay 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,220 +9,11 @@
9
9
  # Point One Percent — pop-pay
10
10
  <p align="left"><i>it only takes <b>0.1%</b> of Hallucination to drain <b>100%</b> of your wallet.</i></p>
11
11
 
12
- ### The runtime security layer for AI agent commerce.
12
+ The runtime security layer for AI agent commerce. Card credentials are injected directly into the browser DOM via CDP — they never enter the agent's context window. One hallucinated prompt can't drain a wallet it can't see.
13
13
 
14
- > Your card never enters the agent's context. One hallucinated prompt can't drain a wallet it can't see.
14
+ ## Getting Started
15
15
 
16
- <p align="center">
17
- <img src="assets/runtime_demo.gif" alt="Point One Percent — live CDP injection demo" width="800">
18
- </p>
19
-
20
- pop-pay is an open-source (MIT) runtime security layer that protects AI agents during online purchases. It works with OpenClaw, NemoClaw, Claude Code, OpenHands, and any MCP-compatible framework.
21
-
22
- ## Architecture: Five Security Primitives
23
-
24
- | Primitive | What it does |
25
- |-----------|-------------|
26
- | **Context Isolation Layer** | Card credentials are injected directly into the browser DOM via CDP — they never enter the agent's process or LLM context window. Prompt injection can't steal what the agent doesn't have. |
27
- | **Intent Verification Engine** | Hybrid keyword + LLM guardrail evaluates whether a purchase *should* happen — not just whether it *can*. [95% accuracy on 20-scenario benchmark.](./docs/GUARDRAIL_BENCHMARK.md) |
28
- | **Human Trust Anchor** | Configurable human-in-the-loop approval for high-value or unrecognized transactions. |
29
- | **Zero-Knowledge Card Surface** | Agent only sees masked tokens (`****-4242`). Real data is stored in an AES-256-GCM encrypted vault. |
30
- | **Ephemeral Authorization Scope** | Each payment approval is single-use with TOCTOU domain guard — an approved session can't be redirected to a malicious merchant. |
31
-
32
- > See [THREAT_MODEL.md](./docs/THREAT_MODEL.md) for the full STRIDE analysis and [COMPLIANCE_FAQ.md](./docs/COMPLIANCE_FAQ.md) for enterprise compliance details.
33
-
34
- ## Guardrail Benchmark
35
-
36
- | Layer | Score | Notes |
37
- |-------|-------|-------|
38
- | Keyword only | 14/20 (70%) | Fast, zero-cost, catches obvious violations |
39
- | **Hybrid (Keyword + LLM)** | **19/20 (95%)** | LLM resolves 5 of 6 keyword failures |
40
-
41
- | Feature | AgentPayy | AgentWallet | Prava | **pop-pay** |
42
- |---------|-----------|-------------|-------|------------|
43
- | Enforcement | Mock alert() | Rule-based | Spending limits | **Semantic validation** |
44
- | Intent check | None | Agent-provided text | None | **Context-aware LLM** |
45
- | Injection-proof | No | No | No | **Yes** |
46
-
47
- ## Two Deployment Modes
48
-
49
- ### BYOC — Bring Your Own Card (Local)
50
- The agent **never** receives the true card number — it only sees `****-4242`. When checkout is reached, the Context Isolation Layer attaches via CDP, traverses all cross-origin iframes (Stripe Elements, Adyen, etc.), and injects credentials directly into the DOM. Runs entirely on your machine via Node.js — no SaaS, no login, no external account.
51
-
52
- ### Enterprise — Stripe Issuing
53
- For cloud-hosted AI fleets: programmatically issue single-use virtual cards via Stripe API, with per-agent budgets and full audit trails.
54
-
55
- ---
56
-
57
- ## Ecosystem Position
58
-
59
- pop-pay is the agent's **Policy Enforcement Point** — it evaluates, approves, and injects. It does NOT navigate websites or solve CAPTCHAs — that's the browser agent's job.
60
-
61
- ### The Handshake: How Point One Percent and Browser Agents Work Together
62
-
63
- The real power emerges when Point One Percent is paired with a browser automation agent (e.g., OpenHands, browser-use, Skyvern). The workflow is a clean division of labor:
64
-
65
- ```
66
- 1. [Browser Agent] Navigates to a site, scrapes product info, reaches checkout.
67
-
68
- │ (Hit a paywall / payment form)
69
-
70
- 2. [Browser Agent → POP MCP] Calls request_virtual_card(amount, vendor, reasoning)
71
-
72
- │ (Point One Percent evaluates: budget OK? vendor approved? no hallucination?)
73
-
74
- 3. [POP] Issues a one-time virtual card (Stripe mode) or uses BYOC vault credentials.
75
- Full card credentials handled only by the local trusted process —
76
- never exposed to the agent or LLM context.
77
-
78
-
79
- 4. [POP] Injects real credentials into the checkout form via CDP.
80
- The agent receives only a transaction confirmation — no card details.
81
-
82
-
83
- 5. [Browser Agent] Clicks the submit button to complete the transaction.
84
-
85
-
86
- 6. [The Vault] Logs the transaction. Card session is immediately burned.
87
- ```
88
-
89
- ### Supported Integrations
90
-
91
- | Integration path | Works with |
92
- |---|---|
93
- | **MCP Tool** | Claude Code, OpenClaw, NemoClaw, OpenHands, any MCP-compatible host |
94
- | **Node.js SDK** | Custom Playwright scripts, Puppeteer automation, gemini-cli |
95
-
96
- > **Any browser-capable agent** (Claude Code, OpenClaw, browser-use, Skyvern, etc.) gets full CDP injection — card is auto-filled into the payment form, the agent only ever sees the masked confirmation (`****-****-****-4242`). See the **[Integration Guide](./docs/INTEGRATION_GUIDE.md)** for setup instructions and System Prompt templates.
97
-
98
- ---
99
-
100
- ## Installation
101
-
102
- ```bash
103
- npm install pop-pay
104
- ```
105
-
106
- ## Quick Start for Claude Code / OpenHands
107
-
108
- If you're using Claude Code, OpenHands, or any MCP-compatible agentic framework, you can get Point One Percent running in under 2 minutes:
109
-
110
- ### Step 1: Initialize the Credential Vault
111
-
112
- Credentials are stored in an AES-256-GCM encrypted vault — no plaintext `.env` required.
113
-
114
- ```bash
115
- npx pop-init-vault
116
- ```
117
-
118
- This will prompt for your card credentials (input is hidden), encrypt them into `~/.config/pop-pay/vault.enc`, and securely wipe any existing `.env`. The MCP server auto-decrypts the vault at startup.
119
-
120
- **Passphrase mode (stronger — protects against agents with shell access):**
121
-
122
- ```bash
123
- npx pop-init-vault --passphrase # one-time setup
124
- npx pop-unlock # run once before each MCP server session
125
- ```
126
-
127
- `pop-unlock` derives the key from your passphrase and stores it in the OS keyring. The MCP server reads it automatically at startup.
128
-
129
- **Security levels (lowest → highest):**
130
-
131
- | Mode | Protects against |
132
- |---|---|
133
- | `.env` file (legacy) | Nothing — plaintext on disk |
134
- | Vault, machine key, OSS source | File-read agents |
135
- | Vault, machine key, `npm install pop-pay` | File-read agents + casual shell inspection |
136
- | Vault + passphrase | File-read agents + shell agents |
137
- | Stripe Issuing (commercial) | All local threats — no credentials stored |
138
-
139
- > **Policy & non-credential config** (allowed vendors, spending limits, CDP URL) is still read from `~/.config/pop-pay/.env`. Only card credentials moved to the vault.
140
-
141
- ### Step 2: Launch Chrome & Get MCP Commands
142
-
143
- ```bash
144
- npx pop-launch --print-mcp
145
- ```
146
-
147
- This launches Chrome with CDP enabled and prints the exact `claude mcp add` commands to run.
148
-
149
- ### Step 3: Add to Claude Code
150
-
151
- ```bash
152
- claude mcp add pop-pay -- npx pop-pay launch-mcp
153
- ```
154
-
155
- > `--scope user` (optional) stores the registration in `~/.claude.json` — available in every Claude Code session.
156
-
157
- ### Step 4: Configure Policy
158
-
159
- Edit `~/.config/pop-pay/.env` to set your spending limits and allowed vendors:
160
-
161
- | Variable | Default | Description |
162
- |---|---|---|
163
- | `POP_ALLOWED_CATEGORIES` | `["aws","cloudflare"]` | Vendors the agent is allowed to pay — see [Categories Cookbook](./docs/CATEGORIES_COOKBOOK.md) |
164
- | `POP_MAX_PER_TX` | `100.0` | Max $ per transaction |
165
- | `POP_MAX_DAILY` | `500.0` | Max $ per day |
166
- | `POP_BLOCK_LOOPS` | `true` | Block hallucination/retry loops |
167
- | `POP_AUTO_INJECT` | `true` | Enable CDP card injection |
168
- | `POP_GUARDRAIL_ENGINE` | `keyword` | Guardrail engine: `keyword` (zero-cost, default) or `llm` (semantic, two-layer) — see [Guardrail Mode](#guardrail-mode-keyword-vs-llm) |
169
- | `POP_BILLING_FIRST_NAME` / `POP_BILLING_LAST_NAME` | _(empty)_ | Auto-fill name fields on checkout pages |
170
- | `POP_BILLING_EMAIL` | _(empty)_ | Auto-fill email |
171
- | `POP_BILLING_PHONE` | _(empty)_ | E.164 format — auto-fill combined phone input |
172
- | `POP_BILLING_PHONE_COUNTRY_CODE` | _(empty)_ | ISO code (`"US"`) or dial prefix (`"+1"`) — fills country code dropdown |
173
- | `POP_BILLING_STREET` / `POP_BILLING_CITY` / `POP_BILLING_STATE` / `POP_BILLING_COUNTRY` / `POP_BILLING_ZIP` | _(empty)_ | Auto-fill address fields; state and country matched fuzzily |
174
- | `POP_ALLOWED_PAYMENT_PROCESSORS` | `[]` | Extra third-party payment processor domains to trust (pop-pay ships with 20 built-in) |
175
- | `POP_WEBHOOK_URL` | _(empty)_ | Webhook URL for Slack/Teams/PagerDuty notifications |
176
-
177
- > **After editing `.env`, fully close and reopen Claude Code.** The MCP server loads configuration at startup — `!claude mcp list` alone is not sufficient to pick up `.env` changes.
178
-
179
- #### Guardrail Mode: Keyword vs LLM
180
-
181
- Point One Percent ships with two guardrail engines. You switch between them with a single env var:
182
-
183
- | | `keyword` (default) | `llm` |
184
- |---|---|---|
185
- | **How it works** | Blocks requests whose `reasoning` string contains suspicious keywords (e.g. "retry", "failed again", "ignore previous instructions") | Sends the agent's `reasoning` to an LLM for deep semantic analysis |
186
- | **What it catches** | Obvious loops, hallucination phrases, prompt injection attempts | Subtle off-topic purchases, logical inconsistencies, policy violations that keyword matching misses |
187
- | **Cost** | Zero — no API calls, instant | One LLM call per `request_virtual_card` invocation |
188
- | **Dependencies** | None | Any OpenAI-compatible endpoint |
189
- | **Best for** | Development, low-risk workflows, cost-sensitive setups | Production, high-value transactions, untrusted agent pipelines |
190
-
191
- > **Tip:** `keyword` mode requires no extra config. To enable LLM mode, see the [full configuration reference in the Integration Guide §1](./docs/INTEGRATION_GUIDE.md#guardrail-mode-configuration).
192
-
193
- ### Step 5: Use It
194
-
195
- Your agent now has access to these tools:
196
-
197
- | Tool | When to use |
198
- |---|---|
199
- | `request_purchaser_info` | Billing/contact info page (name, email, phone, address) — no card fields visible yet |
200
- | `request_virtual_card` | Payment page — card fields are visible. Prompt injection scan runs automatically inside this call. |
201
-
202
- **Single-page checkout** (e.g. Wikipedia donate): agent calls `request_virtual_card`.
203
- **Two-page checkout** (e.g. billing info → payment): agent calls `request_purchaser_info` first, then `request_virtual_card`.
204
-
205
- When it encounters a paywall:
206
-
207
- ```
208
- Agent: "I need to purchase an API key from AWS for $15 to continue."
209
- [Tool Call] request_virtual_card(amount=15.0, vendor="AWS", reasoning="Need API key for deployment")
210
- [POP] Payment approved. Card Issued: ****4242, Expiry: 12/25, Amount: 15.0
211
- Agent: "Purchase successful, continuing workflow."
212
- ```
213
-
214
- If the agent hallucinates or tries to overspend:
215
- ```
216
- Agent: "Let me retry buying compute... the previous attempt failed again."
217
- [Tool Call] request_virtual_card(amount=50.0, vendor="AWS", reasoning="failed again, retry loop")
218
- [POP] Payment rejected. Reason: Hallucination or infinite loop detected in reasoning
219
- ```
220
-
221
- ---
222
-
223
- ## Setup
224
-
225
- **Standard config** works across most MCP-compatible tools:
16
+ Add pop-pay to your MCP client config:
226
17
 
227
18
  ```json
228
19
  {
@@ -242,7 +33,7 @@ Agent: "Let me retry buying compute... the previous attempt failed again."
242
33
  }
243
34
  ```
244
35
 
245
- [<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20MCP%20Server&color=0098FF" alt="Install in VS Code">](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522pop-pay%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522pop-pay%2522%252C%2522launch-mcp%2522%255D%252C%2522env%2522%253A%257B%2522POP_CDP_URL%2522%253A%2522http%253A%252F%252Flocalhost%253A9222%2522%257D%257D) [<img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20MCP%20Server&color=24bfa5">](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522pop-pay%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522pop-pay%2522%252C%2522launch-mcp%2522%255D%252C%2522env%2522%253A%257B%2522POP_CDP_URL%2522%253A%2522http%253A%252F%252Flocalhost%253A9222%2522%257D%257D)
36
+ [<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20MCP%20Server&color=0098FF" alt="Install in VS Code">](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522pop-pay%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522pop-pay%2522%252C%2522launch-mcp%2522%255D%252C%2522env%2522%253A%257B%2522POP_CDP_URL%2522%253A%2522http%253A%252F%252Flocalhost%253A9222%2522%257D%257D) [<img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20MCP%20Server&color=24bfa5">](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522pop-pay%2522%252C%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522pop-pay%2522%252C%2522launch-mcp%2522%255D%252C%2522env%2522%253A%257B%2522POP_CDP_URL%2522%253A%2522http%253A%252F%252Flocalhost%253A9222%2522%257D%257D) [<img src="https://img.shields.io/badge/Cursor-Cursor?style=flat-square&label=Install%20MCP%20Server&color=5C2D91" alt="Install in Cursor">](cursor://anysphere.cursor-deeplink/mcp/install?name=pop-pay&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInBvcC1wYXkiLCJsYXVuY2gtbWNwIl0sImVudiI6eyJQT1BfQ0RQX1VSTCI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTIyMiJ9fQ==)
246
37
 
247
38
  <details>
248
39
  <summary>Claude Code</summary>
@@ -251,7 +42,7 @@ Agent: "Let me retry buying compute... the previous attempt failed again."
251
42
  claude mcp add pop-pay -- npx -y pop-pay launch-mcp
252
43
  ```
253
44
 
254
- To configure spending limits and allowed vendors, set environment variables:
45
+ With environment variables:
255
46
 
256
47
  ```bash
257
48
  claude mcp add pop-pay \
@@ -263,91 +54,33 @@ claude mcp add pop-pay \
263
54
  -- npx -y pop-pay launch-mcp
264
55
  ```
265
56
 
266
- Add `--scope user` to make the registration available across all projects.
267
-
268
57
  </details>
269
58
 
270
59
  <details>
271
60
  <summary>Cursor</summary>
272
61
 
273
- [<img src="https://img.shields.io/badge/Cursor-Cursor?style=flat-square&label=Install%20MCP%20Server&color=5C2D91" alt="Install in Cursor">](cursor://anysphere.cursor-deeplink/mcp/install?name=pop-pay&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInBvcC1wYXkiLCJsYXVuY2gtbWNwIl0sImVudiI6eyJQT1BfQ0RQX1VSTCI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTIyMiJ9fQ==)
274
-
275
- Or add manually to `~/.cursor/mcp.json`:
276
-
277
- ```json
278
- {
279
- "mcpServers": {
280
- "pop-pay": {
281
- "command": "npx",
282
- "args": ["-y", "pop-pay", "launch-mcp"],
283
- "env": {
284
- "POP_CDP_URL": "http://localhost:9222",
285
- "POP_ALLOWED_CATEGORIES": "[\"aws\",\"cloudflare\"]",
286
- "POP_MAX_PER_TX": "100.0",
287
- "POP_MAX_DAILY": "500.0",
288
- "POP_GUARDRAIL_ENGINE": "keyword"
289
- }
290
- }
291
- }
292
- }
293
- ```
62
+ Add to `~/.cursor/mcp.json` using the standard config block above.
294
63
 
295
64
  </details>
296
65
 
297
66
  <details>
298
67
  <summary>Windsurf</summary>
299
68
 
300
- Add to `~/.codeium/windsurf/mcp_config.json`:
301
-
302
- ```json
303
- {
304
- "mcpServers": {
305
- "pop-pay": {
306
- "command": "npx",
307
- "args": ["-y", "pop-pay", "launch-mcp"],
308
- "env": {
309
- "POP_CDP_URL": "http://localhost:9222",
310
- "POP_ALLOWED_CATEGORIES": "[\"aws\",\"cloudflare\"]",
311
- "POP_MAX_PER_TX": "100.0",
312
- "POP_MAX_DAILY": "500.0",
313
- "POP_GUARDRAIL_ENGINE": "keyword"
314
- }
315
- }
316
- }
317
- }
318
- ```
69
+ Add to `~/.codeium/windsurf/mcp_config.json` using the standard config block above.
319
70
 
320
71
  </details>
321
72
 
322
73
  <details>
323
74
  <summary>VS Code (Copilot)</summary>
324
75
 
325
- Add to `.vscode/mcp.json` in your project root:
326
-
327
- ```json
328
- {
329
- "mcpServers": {
330
- "pop-pay": {
331
- "command": "npx",
332
- "args": ["-y", "pop-pay", "launch-mcp"],
333
- "env": {
334
- "POP_CDP_URL": "http://localhost:9222",
335
- "POP_ALLOWED_CATEGORIES": "[\"aws\",\"cloudflare\"]",
336
- "POP_MAX_PER_TX": "100.0",
337
- "POP_MAX_DAILY": "500.0",
338
- "POP_GUARDRAIL_ENGINE": "keyword"
339
- }
340
- }
341
- }
342
- }
343
- ```
76
+ Add to `.vscode/mcp.json` in your project root using the standard config block above.
344
77
 
345
78
  </details>
346
79
 
347
80
  <details>
348
81
  <summary>OpenClaw / NemoClaw</summary>
349
82
 
350
- pop-pay works as an MCP tool with OpenClaw and NemoClaw. Use the standard config above, or see the [Integration Guide §4](./docs/INTEGRATION_GUIDE.md) for detailed setup instructions and System Prompt templates.
83
+ Compatible with any MCP host. See the [Integration Guide](./docs/INTEGRATION_GUIDE.md) for setup instructions and System Prompt templates.
351
84
 
352
85
  </details>
353
86
 
@@ -358,57 +91,80 @@ pop-pay works as an MCP tool with OpenClaw and NemoClaw. Use the standard config
358
91
  docker-compose up -d
359
92
  ```
360
93
 
361
- Runs pop-pay MCP server + headless Chromium with CDP. Mount your encrypted vault from the host. See `docker-compose.yml` for configuration.
94
+ Runs the MCP server + headless Chromium with CDP. Mount your encrypted vault from the host. See `docker-compose.yml` for configuration.
362
95
 
363
96
  </details>
364
97
 
365
- > **Environment variables reference:** See [ENV_REFERENCE.md](./docs/ENV_REFERENCE.md) for the full list of `POP_*` variables (guardrail engine, LLM config, billing info, card credentials, webhooks, and more).
98
+ ## Vault Setup
99
+
100
+ Credentials are stored in an AES-256-GCM encrypted vault — plaintext card data never touches disk.
101
+
102
+ ```bash
103
+ npx pop-init-vault
104
+ ```
105
+
106
+ **Passphrase mode** (recommended — protects against agents with shell access):
107
+
108
+ ```bash
109
+ npx pop-init-vault --passphrase # one-time setup
110
+ npx pop-unlock # run once before each MCP session
111
+ ```
366
112
 
367
- ---
113
+ `pop-unlock` derives the key from your passphrase and stores it in the OS keyring. The MCP server reads it automatically at startup.
368
114
 
369
115
  ## MCP Tools
370
116
 
371
117
  | Tool | Description |
372
118
  |:---|:---|
373
- | `request_virtual_card` | Issue a one-time virtual card for an automated purchase. Runs security scan on the checkout page. |
374
- | `request_purchaser_info` | Auto-fill billing/contact info from pre-configured profile. |
119
+ | `request_virtual_card` | Issue a virtual card and inject credentials into the checkout page via CDP. |
120
+ | `request_purchaser_info` | Auto-fill billing/contact info (name, address, email, phone). |
375
121
  | `request_x402_payment` | Pay for API calls via the x402 HTTP payment protocol. |
376
- | `page_snapshot` | Security scan a checkout page for hidden prompt injections and anomalies. |
122
+ | `page_snapshot` | Scan a checkout page for hidden prompt injections or anomalies. |
377
123
 
378
- ## Providers
124
+ ## Configuration
379
125
 
380
- | Provider | Description |
381
- |:---|:---|
382
- | **BYOC** (default) | Bring Your Own Card — uses your encrypted vault credentials for local CDP injection. |
383
- | **Stripe Issuing** | Real virtual cards via Stripe Issuing API. Requires `POP_STRIPE_KEY`. |
384
- | **Lithic** | Multi-issuer adapter skeleton (Stripe Issuing / Lithic). |
385
- | **Mock** | Test mode with generated card numbers for development. |
126
+ Core variables in `~/.config/pop-pay/.env`. See [ENV_REFERENCE.md](./docs/ENV_REFERENCE.md) for the full list.
386
127
 
387
- **Provider priority (high low):** Stripe Issuing → BYOC Local → Mock.
128
+ | Variable | Default | Description |
129
+ |---|---|---|
130
+ | `POP_ALLOWED_CATEGORIES` | `["aws","cloudflare"]` | Approved vendor categories — see [Categories Cookbook](./docs/CATEGORIES_COOKBOOK.md) |
131
+ | `POP_MAX_PER_TX` | `100.0` | Max USD per transaction |
132
+ | `POP_MAX_DAILY` | `500.0` | Max USD per day |
133
+ | `POP_BLOCK_LOOPS` | `true` | Block hallucination/retry loops |
134
+ | `POP_AUTO_INJECT` | `true` | Enable CDP card injection |
135
+ | `POP_GUARDRAIL_ENGINE` | `keyword` | `keyword` (zero-cost) or `llm` (semantic) |
388
136
 
389
- If `POP_STRIPE_KEY` is set, Stripe takes precedence. If `POP_BYOC_NUMBER` is set (but no Stripe key), `LocalVaultProvider` is used. If neither is set, `MockProvider` is used for development.
137
+ ### Guardrail Mode
138
+
139
+ | | `keyword` (default) | `llm` |
140
+ |---|---|---|
141
+ | **Mechanism** | Keyword matching on reasoning string | Semantic analysis via LLM |
142
+ | **Cost** | Zero — no API calls | One LLM call per request |
143
+ | **Best for** | Development, low-risk workflows | Production, high-value transactions |
390
144
 
391
- > **CDP injection limitation with Stripe Issuing:** The Stripe Issuing API returns only the last 4 digits of the card number for security reasons. CDP auto-injection (`POP_AUTO_INJECT=true`) requires the full PAN and therefore **does not work** with Stripe Issuing. Use BYOC (`POP_BYOC_NUMBER`) if you need CDP injection; use Stripe Issuing if you need a real card and will handle form submission yourself.
145
+ > To enable LLM mode, see [Integration Guide §1](./docs/INTEGRATION_GUIDE.md#guardrail-mode-configuration).
392
146
 
393
- ---
147
+ ## Providers
394
148
 
395
- ## Security Statement
149
+ | Provider | Description |
150
+ |:---|:---|
151
+ | **BYOC** (default) | Bring Your Own Card — encrypted vault credentials, local CDP injection. |
152
+ | **Stripe Issuing** | Real virtual cards via Stripe API. Requires `POP_STRIPE_KEY`. |
153
+ | **Lithic** | Multi-issuer adapter (Stripe Issuing / Lithic). |
154
+ | **Mock** | Test mode with generated card numbers for development. |
396
155
 
397
- Security is a first-class citizen in pop-pay. The SDK **masks card numbers by default** (e.g., `****-****-****-4242`) when returning authorization results to the agent.
156
+ **Priority:** Stripe Issuing BYOC Local Mock.
398
157
 
399
- **Defense-in-depth hardening:**
158
+ ## Security
400
159
 
401
160
  | Layer | Defense |
402
161
  |---|---|
403
- | **Encrypted vault** | Card credentials stored as AES-256-GCM ciphertext (`vault.enc`); plaintext never touches disk after `pop-init-vault` |
404
- | **Passphrase mode** | Key derived from user passphrase via scrypt; stored in OS keyring — agents with shell access cannot derive the key |
405
- | **Database** | SQLite only stores masked card (`****-4242`); `card_number` and `cvv` columns removed entirely |
406
- | **Injection-time TOCTOU guard** | Domain verified against guardrail-approved vendor at the moment of injection — prevents redirect-to-attacker attacks |
407
- | **Repr redaction** | Masked card output in all logs and responses; credentials cannot leak via tracebacks |
408
- | **Process isolation** | Agent communicates via MCP JSON-RPC as a separate process — cannot access MCP server memory or env vars through the protocol |
409
- | **Native security layer** | XOR-split salt storage and scrypt key derivation handled in a stripped Rust binary (napi-rs) |
162
+ | **Context Isolation** | Card credentials never enter the agent's context window or logs |
163
+ | **Encrypted Vault** | AES-256-GCM with XOR-split salt and native scrypt key derivation (Rust) |
164
+ | **TOCTOU Guard** | Domain verified at the moment of CDP injection blocks redirect attacks |
165
+ | **Repr Redaction** | Automatic masking (`****-4242`) in all MCP responses, logs, and tracebacks |
410
166
 
411
- See [THREAT_MODEL.md](./docs/THREAT_MODEL.md) for the full STRIDE analysis and red team results.
167
+ See [THREAT_MODEL.md](./docs/THREAT_MODEL.md) for the full STRIDE analysis and [COMPLIANCE_FAQ.md](./docs/COMPLIANCE_FAQ.md) for enterprise details.
412
168
 
413
169
  ## Architecture
414
170
 
@@ -420,10 +176,10 @@ See [THREAT_MODEL.md](./docs/THREAT_MODEL.md) for the full STRIDE analysis and r
420
176
  ## Documentation
421
177
 
422
178
  - [Threat Model](docs/THREAT_MODEL.md) — STRIDE analysis, 5 security primitives, 10 attack scenarios
423
- - [Guardrail Benchmark](docs/GUARDRAIL_BENCHMARK.md) — 95% accuracy across 20 test scenarios, competitive comparison
424
- - [Compliance FAQ](docs/COMPLIANCE_FAQ.md) — Enterprise security and PCI DSS/SOC 2/GDPR details
179
+ - [Guardrail Benchmark](docs/GUARDRAIL_BENCHMARK.md) — 95% accuracy across 20 test scenarios
180
+ - [Compliance FAQ](docs/COMPLIANCE_FAQ.md) — PCI DSS, SOC 2, GDPR details
425
181
  - [Environment Reference](docs/ENV_REFERENCE.md) — All POP_* environment variables
426
- - [Integration Guide](docs/INTEGRATION_GUIDE.md) — Detailed setup for Claude Code, Node.js SDK, and browser agents
182
+ - [Integration Guide](docs/INTEGRATION_GUIDE.md) — Setup for Claude Code, Node.js SDK, and browser agents
427
183
  - [Categories Cookbook](docs/CATEGORIES_COOKBOOK.md) — POP_ALLOWED_CATEGORIES patterns and examples
428
184
 
429
185
  ## License
@@ -0,0 +1,193 @@
1
+ :root {
2
+ --bg-color: #0d1117;
3
+ --card-bg: #161b22;
4
+ --text-primary: #c9d1d9;
5
+ --text-secondary: #8b949e;
6
+ --accent-green: #3fb950;
7
+ --warning-amber: #d29922;
8
+ --danger-red: #f85149;
9
+ --border-color: #30363d;
10
+ }
11
+
12
+ * {
13
+ box-sizing: border-box;
14
+ margin: 0;
15
+ padding: 0;
16
+ }
17
+
18
+ body {
19
+ background-color: var(--bg-color);
20
+ color: var(--text-primary);
21
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
22
+ line-height: 1.6;
23
+ padding: 20px 0;
24
+ }
25
+
26
+ .container {
27
+ max-width: 1200px;
28
+ margin: 0 auto;
29
+ padding: 0 20px;
30
+ }
31
+
32
+ header {
33
+ margin-bottom: 40px;
34
+ }
35
+
36
+ header .container {
37
+ display: flex;
38
+ justify-content: space-between;
39
+ align-items: center;
40
+ border-bottom: 1px solid var(--border-color);
41
+ padding-bottom: 20px;
42
+ }
43
+
44
+ h1 {
45
+ font-size: 1.5rem;
46
+ font-weight: 700;
47
+ letter-spacing: 1px;
48
+ }
49
+
50
+ .subtitle {
51
+ color: var(--accent-green);
52
+ font-family: 'Courier New', Courier, monospace;
53
+ font-size: 0.9rem;
54
+ margin-left: 10px;
55
+ }
56
+
57
+ .metrics {
58
+ display: grid;
59
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
60
+ gap: 20px;
61
+ margin-bottom: 30px;
62
+ }
63
+
64
+ .card {
65
+ background-color: var(--card-bg);
66
+ border: 1px solid var(--border-color);
67
+ border-radius: 6px;
68
+ padding: 20px;
69
+ }
70
+
71
+ .card h3 {
72
+ color: var(--text-secondary);
73
+ font-size: 0.75rem;
74
+ letter-spacing: 1px;
75
+ margin-bottom: 10px;
76
+ text-transform: uppercase;
77
+ }
78
+
79
+ .value {
80
+ font-family: 'Courier New', Courier, monospace;
81
+ font-size: 2rem;
82
+ font-weight: 700;
83
+ }
84
+
85
+ .progress-bar {
86
+ background-color: var(--border-color);
87
+ height: 8px;
88
+ border-radius: 4px;
89
+ margin-top: 15px;
90
+ overflow: hidden;
91
+ }
92
+
93
+ .progress-fill {
94
+ height: 100%;
95
+ width: 0%;
96
+ background-color: var(--accent-green);
97
+ transition: width 0.5s ease;
98
+ }
99
+
100
+ .settings {
101
+ margin-bottom: 30px;
102
+ }
103
+
104
+ .input-group {
105
+ display: flex;
106
+ align-items: center;
107
+ gap: 15px;
108
+ margin-top: 10px;
109
+ }
110
+
111
+ label {
112
+ font-size: 0.85rem;
113
+ color: var(--text-secondary);
114
+ }
115
+
116
+ input[type="number"] {
117
+ background-color: var(--bg-color);
118
+ border: 1px solid var(--border-color);
119
+ color: var(--text-primary);
120
+ padding: 8px 12px;
121
+ border-radius: 4px;
122
+ width: 150px;
123
+ font-family: 'Courier New', Courier, monospace;
124
+ }
125
+
126
+ .btn {
127
+ background-color: transparent;
128
+ border: 1px solid var(--border-color);
129
+ color: var(--text-primary);
130
+ padding: 8px 16px;
131
+ border-radius: 6px;
132
+ cursor: pointer;
133
+ font-size: 0.85rem;
134
+ font-weight: 600;
135
+ transition: all 0.2s;
136
+ }
137
+
138
+ .btn:hover {
139
+ background-color: var(--card-bg);
140
+ border-color: var(--text-secondary);
141
+ }
142
+
143
+ .btn-small {
144
+ padding: 6px 12px;
145
+ }
146
+
147
+ .table-container {
148
+ overflow-x: auto;
149
+ margin-top: 15px;
150
+ }
151
+
152
+ table {
153
+ width: 100%;
154
+ border-collapse: collapse;
155
+ font-size: 0.85rem;
156
+ }
157
+
158
+ th {
159
+ text-align: left;
160
+ padding: 12px;
161
+ border-bottom: 1px solid var(--border-color);
162
+ color: var(--text-secondary);
163
+ text-transform: uppercase;
164
+ cursor: pointer;
165
+ }
166
+
167
+ th:hover {
168
+ color: var(--text-primary);
169
+ }
170
+
171
+ td {
172
+ padding: 12px;
173
+ border-bottom: 1px solid var(--border-color);
174
+ font-family: 'Courier New', Courier, monospace;
175
+ }
176
+
177
+ .seals, .rejected {
178
+ margin-bottom: 40px;
179
+ }
180
+
181
+ .rejected table {
182
+ border: 1px solid var(--danger-red);
183
+ }
184
+
185
+ .rejected td {
186
+ color: var(--danger-red);
187
+ }
188
+
189
+ @media (max-width: 768px) {
190
+ .metrics {
191
+ grid-template-columns: 1fr;
192
+ }
193
+ }