web-agent-bridge 3.8.1 → 3.9.1

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.ar.md CHANGED
@@ -15,6 +15,8 @@
15
15
 
16
16
  **البروتوكول والمنصة المفتوحة للتفاعل بين الذكاء الاصطناعي والويب — واجهة أوامر موحّدة، متصفح سيادي، درع هاتف، اكتشاف عبر DNS، شبكة وكلاء، وبوابة API موحّدة لتفاعل آمن بين الذكاء الاصطناعي والمواقع.**
17
17
 
18
+ > **جديد في الإصدار 3.9 — ATP (المُكوِّن الأساسي لمعاملات الوكلاء):** عقود نوايا موقَّعة، معاملات لا تتكرر (idempotent)، إيصالات Ed25519 قابلة للتحقق علنياً بدون مصادقة، وآلية تعويض صريحة. هذا هو **طبقة الثقة + المعاملات** التي تنقُص التجارة الوكيلة. [راجع المواصفة §21](docs/SPEC.md#21-agent-transaction-primitive-atp--v390) · [صفحة ATP العامة](public/atp.html).
19
+
18
20
  🌐 **الموقع الرسمي:** [https://webagentbridge.com](https://webagentbridge.com) — جرّب مساحة عمل الوكيل الذكي ولوحات التحكم والعديد من الميزات الأخرى مباشرة.
19
21
 
20
22
  يتيح WAB لأصحاب المواقع إضافة سكريبت يكشف واجهة `window.AICommands` لوكلاء الذكاء الاصطناعي. بدلاً من تحليل شيفرة HTML المعقدة، يقرأ الوكيل قائمة الإجراءات المتاحة وينفذها بدقة وأمان.
package/README.md CHANGED
@@ -3,12 +3,14 @@
3
3
  <img src="https://raw.githubusercontent.com/abokenan444/web-agent-bridge/master/public/images/wab-logo-large.png" alt="Web Agent Bridge Logo" width="180" />
4
4
 
5
5
  <h1>Web Agent Bridge (WAB)</h1>
6
- <p><b>The open AI Web protocol & agent platform.</b></p>
7
- <p><i>robots.txt told bots what NOT to do. WAB tells AI agents what they CAN do.</i></p>
6
+ <p><b>The trust + transaction layer for agentic commerce.</b></p>
7
+ <p><i>Signed intent contracts · idempotent transactions · Ed25519-verifiable receipts · explicit compensation.</i></p>
8
+ <p><i>robots.txt told bots what NOT to do. WAB tells AI agents what they CAN do — and proves what they did.</i></p>
8
9
 
9
10
  [![npm](https://img.shields.io/npm/v/web-agent-bridge?color=blue&style=flat-square)](https://www.npmjs.com/package/web-agent-bridge)
10
11
  [![License: Open Core](https://img.shields.io/badge/License-Open_Core-blue.svg?style=flat-square)](LICENSE)
11
- [![Tests](https://img.shields.io/badge/Tests-428%2F428_passing-22c55e?style=flat-square&logo=jest&logoColor=white)](tests)
12
+ [![Tests](https://img.shields.io/badge/Tests-445%2F445_passing-22c55e?style=flat-square&logo=jest&logoColor=white)](tests)
13
+ [![ATP](https://img.shields.io/badge/ATP-v3.9.0-7c3aed?style=flat-square)](docs/SPEC.md#21-agent-transaction-primitive-atp--v390)
12
14
  [![Discord](https://img.shields.io/badge/Discord-Join-5865F2?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/NnbpJYEF)
13
15
 
14
16
  <br />
@@ -31,6 +33,38 @@ AI agents today guess their way through the web — DOM scraping, brittle select
31
33
 
32
34
  ---
33
35
 
36
+ ## ATP — Agent Transaction Primitive *(new in v3.9)*
37
+
38
+ WAB v3.9 introduces the **Agent Transaction Primitive (ATP)** — the missing trust + transaction layer for agentic commerce.
39
+
40
+ ```
41
+ Intent → Authorize → Transact → Receipt → (Compensate)
42
+ contract single-use idempotent Ed25519- explicit
43
+ + scope nonce burn UNIQUE key signed rollback
44
+ + spend cap JSON
45
+ ```
46
+
47
+ - **Intent contracts** — the user's signed authorization (scope, spend cap, expiry, single-use nonce).
48
+ - **Idempotent execution** — `UNIQUE (intent_id, idempotency_key)`: retries can never double-execute.
49
+ - **Signed receipts** — Ed25519 over canonical JSON; verifiable via the **public** `/api/atp/receipts/verify` endpoint with zero auth.
50
+ - **Compensation** — explicit rollback that decrements the intent's spend counter.
51
+
52
+ ```js
53
+ const { ATPClient } = require('web-agent-bridge/sdk');
54
+ const atp = new ATPClient({ baseUrl: 'https://api.webagentbridge.com', token: USER_JWT });
55
+
56
+ const intent = await atp.createIntent({ purpose: 'buy 1 widget', scope: { actions: ['cart.add','checkout'] }, max_spend_cents: 5000, currency: 'EUR' });
57
+ await atp.authorizeIntent(intent.id);
58
+ const tx = await atp.beginTransaction({ intent_id: intent.id, idempotency_key: 'order-42', amount_cents: 4200 });
59
+ await atp.transition(tx.id, 'executing'); await atp.transition(tx.id, 'executed'); await atp.transition(tx.id, 'settled');
60
+ const r = await atp.issueReceipt(tx.id); // signed
61
+ const v = await atp.verifyReceipt({ receiptId: r.receipt_id }); // public, no auth
62
+ ```
63
+
64
+ Full spec: [`docs/SPEC.md` §21](docs/SPEC.md#21-agent-transaction-primitive-atp--v390) · Public docs page: [`/atp.html`](public/atp.html).
65
+
66
+ ---
67
+
34
68
  ## Quick start (60 seconds)
35
69
 
36
70
  ```bash
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "web-agent-bridge",
3
- "version": "3.8.1",
4
- "description": "Open AI↔Web protocol & agent platform: standardized command interface, sovereign browser, ShieldQR trust verifier, SSL health monitor, zero-config adoption (Cloudflare/Vercel/Netlify/Next.js), DNS discovery, agent mesh, and unified API gateway for safe AI–website interaction",
3
+ "version": "3.9.1",
4
+ "description": "Agent Transaction Bridge the trust + transaction layer for agentic commerce. Signed intent contracts, idempotent transactions, Ed25519-verifiable receipts, explicit compensation. Plus the original WAB stack: sovereign browser, ShieldQR, SSL health, DNS discovery, agent mesh, and unified gateway for safe AI–website interaction.",
5
5
  "author": "Web Agent Bridge <dev@webagentbridge.com>",
6
6
  "main": "server/index.js",
7
7
  "bin": {
@@ -0,0 +1,174 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Agent Transaction Primitive (ATP) · Web Agent Bridge</title>
7
+ <meta name="description" content="The first-class primitive for trusted agent execution: signed intent contracts, idempotent transactions, and cryptographically verifiable receipts." />
8
+ <link rel="stylesheet" href="/css/main.css" />
9
+ <style>
10
+ :root { --bg:#0a0e14; --fg:#e6edf3; --muted:#8b949e; --accent:#7ee787; --line:#30363d; --card:#161b22; }
11
+ body { background:var(--bg); color:var(--fg); font:16px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; margin:0; padding:0; }
12
+ main { max-width:980px; margin:0 auto; padding:48px 24px 96px; }
13
+ h1 { font-size:2.5rem; margin:0 0 8px; letter-spacing:-0.02em; }
14
+ h2 { font-size:1.5rem; margin:48px 0 12px; border-bottom:1px solid var(--line); padding-bottom:8px; }
15
+ h3 { font-size:1.1rem; margin:24px 0 8px; color:var(--accent); }
16
+ p.lede { font-size:1.2rem; color:var(--muted); margin:0 0 32px; max-width:720px; }
17
+ .badge { display:inline-block; background:#1f6feb22; color:#79c0ff; border:1px solid #1f6feb55; padding:2px 10px; border-radius:99px; font-size:0.85rem; font-weight:600; }
18
+ .grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:16px; margin:24px 0; }
19
+ .card { background:var(--card); border:1px solid var(--line); border-radius:8px; padding:20px; }
20
+ .card h3 { margin-top:0; color:var(--accent); }
21
+ .card p { color:var(--muted); font-size:0.95rem; margin:0; }
22
+ pre { background:#010409; border:1px solid var(--line); border-radius:6px; padding:16px; overflow-x:auto; font-size:0.85rem; color:#c9d1d9; }
23
+ code { font-family:ui-monospace,SFMono-Regular,Consolas,monospace; }
24
+ .lifecycle { display:flex; gap:8px; align-items:center; margin:24px 0; flex-wrap:wrap; }
25
+ .lifecycle .step { flex:1; min-width:140px; background:var(--card); border:1px solid var(--line); padding:16px; border-radius:6px; text-align:center; }
26
+ .lifecycle .step b { display:block; font-size:1.1rem; color:var(--accent); margin-bottom:4px; }
27
+ .lifecycle .arrow { color:var(--muted); font-size:1.5rem; }
28
+ table { width:100%; border-collapse:collapse; margin:16px 0; }
29
+ th, td { text-align:left; padding:10px 12px; border-bottom:1px solid var(--line); font-size:0.95rem; }
30
+ th { color:var(--accent); font-weight:600; }
31
+ .tier { display:inline-block; padding:1px 8px; border-radius:4px; font-size:0.8rem; font-weight:600; }
32
+ .tier-free { background:#3fb95022; color:#7ee787; border:1px solid #3fb95055; }
33
+ .tier-pro { background:#1f6feb22; color:#79c0ff; border:1px solid #1f6feb55; }
34
+ .tier-business { background:#bf8b3022; color:#e3b341; border:1px solid #bf8b3055; }
35
+ .tier-enterprise { background:#a371f722; color:#d2a8ff; border:1px solid #a371f755; }
36
+ nav.top { position:sticky; top:0; background:rgba(10,14,20,0.9); backdrop-filter:blur(8px); border-bottom:1px solid var(--line); padding:12px 24px; }
37
+ nav.top a { color:var(--fg); text-decoration:none; margin-right:20px; font-weight:500; }
38
+ nav.top a:hover { color:var(--accent); }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <nav class="top">
43
+ <a href="/">WAB</a>
44
+ <a href="/atp.html"><b>ATP</b></a>
45
+ <a href="/docs.html">Docs</a>
46
+ <a href="/premium.html">Pricing</a>
47
+ <a href="/login.html">Login</a>
48
+ </nav>
49
+ <main>
50
+ <span class="badge">v3.9.0 · the keystone primitive</span>
51
+ <h1>Agent Transaction Primitive</h1>
52
+ <p class="lede">
53
+ WAB is no longer just a discovery and execution protocol — it is the
54
+ <b>trust + transaction layer</b> for agentic commerce. ATP makes four
55
+ guarantees first-class: a signed intent contract from the human,
56
+ idempotent execution, a cryptographically verifiable receipt, and
57
+ explicit compensation.
58
+ </p>
59
+
60
+ <h2>The lifecycle</h2>
61
+ <div class="lifecycle">
62
+ <div class="step"><b>1 · Intent</b>The user declares scope, spend cap, expiry. Single-use nonce.</div>
63
+ <div class="arrow">→</div>
64
+ <div class="step"><b>2 · Authorize</b>The user confirms. The contract is now binding on the agent.</div>
65
+ <div class="arrow">→</div>
66
+ <div class="step"><b>3 · Transact</b>Idempotent execution under the intent. Every step is logged.</div>
67
+ <div class="arrow">→</div>
68
+ <div class="step"><b>4 · Receipt</b>Ed25519-signed canonical JSON. Anyone can verify it.</div>
69
+ <div class="arrow">→</div>
70
+ <div class="step"><b>5 · Compensate</b>If something goes wrong, the rollback path is explicit.</div>
71
+ </div>
72
+
73
+ <h2>Why this matters</h2>
74
+ <div class="grid">
75
+ <div class="card">
76
+ <h3>For users</h3>
77
+ <p>You see exactly what you authorized, what the agent did, and you can verify the receipt later — even without WAB online.</p>
78
+ </div>
79
+ <div class="card">
80
+ <h3>For agents</h3>
81
+ <p>No more retries causing double-charges. <code>idempotency-key</code> + the intent's spend cap make safe execution structural, not aspirational.</p>
82
+ </div>
83
+ <div class="card">
84
+ <h3>For sites</h3>
85
+ <p>Every transaction comes with a signed proof of consent. Disputes become deterministic, not he-said-she-said.</p>
86
+ </div>
87
+ <div class="card">
88
+ <h3>For ecosystems</h3>
89
+ <p>The protocol is open. The verification endpoint is public. The receipt format is canonical JSON. No vendor lock-in.</p>
90
+ </div>
91
+ </div>
92
+
93
+ <h2>Quick start</h2>
94
+ <pre><code>// Node 18+ — install: npm i web-agent-bridge
95
+ const { ATPClient } = require('web-agent-bridge/sdk');
96
+
97
+ const atp = new ATPClient({ baseUrl: 'https://webagentbridge.com', token: USER_JWT });
98
+
99
+ // 1) The human declares the contract.
100
+ const intent = await atp.createIntent({
101
+ purpose: 'Buy a book ≤ €30',
102
+ scope: { actions: ['search','add_to_cart','checkout'] },
103
+ spend_cap_cents: 3000,
104
+ ttl_seconds: 600,
105
+ });
106
+
107
+ // 2) The human confirms.
108
+ await atp.authorizeIntent(intent.id);
109
+
110
+ // 3) The agent executes — idempotent by key.
111
+ const tx = await atp.beginTransaction({
112
+ intent_id: intent.id, amount_cents: 1500,
113
+ idempotency_key: 'order-' + Date.now(),
114
+ });
115
+ await atp.transition(tx.id, 'executing');
116
+ await atp.step(tx.id, { action: 'checkout.confirm', evidence: { order_id: 'X1' } });
117
+ await atp.transition(tx.id, 'executed');
118
+ await atp.transition(tx.id, 'settled');
119
+
120
+ // 4) Signed receipt — anyone can verify.
121
+ const receipt = await atp.issueReceipt(tx.id);
122
+ const verification = await atp.verifyReceipt(receipt.id);
123
+ console.log(verification.verification.ok); // true</code></pre>
124
+
125
+ <h2>The signed receipt</h2>
126
+ <p>Every receipt is a canonical JSON document signed with Ed25519. The public verification endpoint (<code>POST /api/atp/receipts/verify</code>) requires <b>no authentication</b> — that is the whole point. Anyone can hold a receipt accountable.</p>
127
+ <pre><code>{
128
+ "type": "atp.receipt.v1",
129
+ "receipt_id": "atp_rcpt_…",
130
+ "transaction": { "id": "atp_tx_…", "status": "settled", "amount_cents": 1500, … },
131
+ "intent": { "id": "atp_int_…", "purpose": "Buy a book ≤ €30", "scope": { … }, … },
132
+ "steps": [ { "seq": 1, "action": "checkout.confirm", "state": "succeeded" } ],
133
+ "signature": {
134
+ "algorithm": "ed25519",
135
+ "value": "BASE64…",
136
+ "key_id": "16-char fingerprint",
137
+ "public_key":"BASE64…",
138
+ "signed_at": "ISO-8601"
139
+ }
140
+ }</code></pre>
141
+
142
+ <h2>Open core · paid features</h2>
143
+ <p>The protocol and verification stay open so the standard can spread. Higher throughput, persistent key binding, and enterprise features fund the work.</p>
144
+ <table>
145
+ <thead><tr><th>Capability</th><th>Tier</th><th>Notes</th></tr></thead>
146
+ <tbody>
147
+ <tr><td>Protocol spec &amp; SDK</td><td><span class="tier tier-free">open source</span></td><td>MIT licensed. Implement it anywhere.</td></tr>
148
+ <tr><td>Public receipt verification (no auth)</td><td><span class="tier tier-free">open source</span></td><td>Anyone can verify any ATP receipt.</td></tr>
149
+ <tr><td>Intent creation</td><td><span class="tier tier-free">free</span></td><td>10/day on Free, 50/day on Starter.</td></tr>
150
+ <tr><td>Receipts with persistent site key binding</td><td><span class="tier tier-pro">pro</span></td><td>500 intents/day. Continuity of trust across receipts.</td></tr>
151
+ <tr><td>Idempotent execution at scale</td><td><span class="tier tier-pro">pro</span></td><td>Higher quotas, audit export.</td></tr>
152
+ <tr><td>Compensation flows + retry classification</td><td><span class="tier tier-business">business</span></td><td>5 000 intents/day, webhook subscriptions.</td></tr>
153
+ <tr><td>HSM-backed signing, custom workflows, SLA</td><td><span class="tier tier-enterprise">enterprise</span></td><td>Unlimited, dedicated settlement queue.</td></tr>
154
+ </tbody>
155
+ </table>
156
+
157
+ <h2>Security posture</h2>
158
+ <ul>
159
+ <li><b>Single-use nonces.</b> Authorization burns the nonce in <code>atp_nonces</code> — replays fail at the DB layer.</li>
160
+ <li><b>State machine in the DB.</b> CHECK constraints make illegal transitions unrepresentable, not just unlikely.</li>
161
+ <li><b>Idempotency.</b> <code>UNIQUE (intent_id, idempotency_key)</code> means retries can never double-execute.</li>
162
+ <li><b>Spend cap on every transition.</b> Enforced server-side, not in the agent.</li>
163
+ <li><b>Canonical JSON signing.</b> RFC 8785-style key ordering means a tampered receipt cannot pass verification.</li>
164
+ <li><b>Public verify rate-limited.</b> 120 req/min per IP, cryptographic check is the answer.</li>
165
+ </ul>
166
+
167
+ <h2>Reference</h2>
168
+ <p>Full API in <a href="/docs.html">/docs.html</a> and machine-readable spec in <code>docs/SPEC.md</code>. Mount path: <code>/api/atp</code>. Source: <a href="https://github.com/abokenan444/web-agent-bridge">github.com/abokenan444/web-agent-bridge</a>.</p>
169
+
170
+ <h2>We run our own business on it</h2>
171
+ <p>WAB doesn't just publish this protocol &mdash; it bills its own customers with it. Every subscription processed through webagentbridge.com produces a publicly-verifiable Ed25519 receipt. Audit the books at <a href="/transparency.html"><b>/transparency.html</b></a>.</p>
172
+ </main>
173
+ </body>
174
+ </html>
@@ -0,0 +1,285 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>WAB Transparency — Live ATP Receipts for Every Subscription</title>
7
+ <meta name="description" content="WAB doesn't just build the trust layer for agentic commerce — it runs its own business on it. Every subscription processed through webagentbridge.com is an ATP transaction with a public Ed25519 receipt.">
8
+ <meta property="og:title" content="WAB Transparency — Live ATP Receipts">
9
+ <meta property="og:description" content="Every WAB subscription produces a publicly-verifiable Ed25519 receipt. Audit the books, byte by byte.">
10
+ <link rel="icon" href="/assets/favicon.svg" type="image/svg+xml">
11
+ <style>
12
+ :root {
13
+ --bg:#0b0d12; --bg2:#11141b; --fg:#e6e8ec; --muted:#8a93a6;
14
+ --accent:#7c5cff; --accent2:#22d3ee; --ok:#10b981; --bad:#ef4444;
15
+ --border:#1f2430;
16
+ }
17
+ * { box-sizing:border-box; }
18
+ body {
19
+ margin:0; font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif;
20
+ background: radial-gradient(1200px 600px at 20% -10%, rgba(124,92,255,.18), transparent 60%),
21
+ radial-gradient(900px 500px at 90% 10%, rgba(34,211,238,.12), transparent 55%),
22
+ var(--bg);
23
+ color:var(--fg); line-height:1.55; min-height:100vh;
24
+ }
25
+ a { color:var(--accent2); text-decoration:none; }
26
+ a:hover { text-decoration:underline; }
27
+ .container { max-width: 1100px; margin: 0 auto; padding: 0 24px; }
28
+ header.top {
29
+ padding: 22px 0; border-bottom:1px solid var(--border);
30
+ display:flex; align-items:center; justify-content:space-between;
31
+ }
32
+ .brand { font-weight:700; letter-spacing:.2px; }
33
+ .brand span { color: var(--accent); }
34
+ nav a { margin-left: 18px; color: var(--muted); font-size:14px; }
35
+ nav a:hover { color: var(--fg); }
36
+
37
+ .hero { padding: 70px 0 50px; text-align:center; }
38
+ .hero .eyebrow {
39
+ display:inline-block; padding:6px 14px; border-radius:999px;
40
+ background: rgba(124,92,255,.12); color:#c8bdff; font-size:12px;
41
+ letter-spacing:.18em; text-transform:uppercase; border:1px solid rgba(124,92,255,.35);
42
+ }
43
+ .hero h1 {
44
+ font-size: clamp(34px, 5vw, 56px); margin: 18px 0 14px; line-height:1.08;
45
+ background: linear-gradient(180deg, #fff 30%, #b8c0d4 100%);
46
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
47
+ font-weight: 800; letter-spacing:-.5px;
48
+ }
49
+ .hero p.sub { color: var(--muted); max-width: 780px; margin: 0 auto; font-size:18px; }
50
+ .hero p.tag { margin-top: 22px; font-size: 15px; color:#cdd5e3; }
51
+ .hero p.tag strong { color: var(--fg); }
52
+
53
+ .stats {
54
+ display:grid; grid-template-columns: repeat(4, 1fr); gap: 14px;
55
+ margin: 36px 0 12px;
56
+ }
57
+ .stat {
58
+ background: linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01));
59
+ border:1px solid var(--border); border-radius:14px; padding:18px;
60
+ }
61
+ .stat .k { color: var(--muted); font-size:12px; letter-spacing:.08em; text-transform:uppercase; }
62
+ .stat .v { font-size: 26px; font-weight: 700; margin-top:6px; }
63
+ .stat .v small { color: var(--muted); font-size:13px; font-weight:500; }
64
+ @media (max-width: 720px) { .stats { grid-template-columns: repeat(2, 1fr); } }
65
+
66
+ section { padding: 30px 0; }
67
+ h2 { font-size: 24px; margin: 28px 0 14px; letter-spacing:-.3px; }
68
+
69
+ .feed {
70
+ background: var(--bg2); border: 1px solid var(--border); border-radius: 14px; overflow:hidden;
71
+ }
72
+ table { width:100%; border-collapse: collapse; font-size: 14px; }
73
+ th, td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--border); }
74
+ th { background: rgba(255,255,255,.02); color: var(--muted); font-weight: 600;
75
+ font-size: 12px; letter-spacing:.08em; text-transform:uppercase; }
76
+ tr:last-child td { border-bottom: 0; }
77
+ tr:hover td { background: rgba(124,92,255,.04); }
78
+ .mono { font-family: ui-monospace, "JetBrains Mono", Menlo, monospace; font-size: 12.5px; }
79
+ .receipt-id { color: var(--accent2); }
80
+ .tier { display:inline-block; padding:3px 8px; border-radius:6px; font-size:11px;
81
+ font-weight:700; text-transform:uppercase; letter-spacing:.06em; }
82
+ .tier.starter { background: rgba(34,211,238,.15); color:#7ee9ff; }
83
+ .tier.pro { background: rgba(124,92,255,.18); color:#c8bdff; }
84
+ .tier.business { background: rgba(16,185,129,.18); color:#7feac3; }
85
+ .tier.enterprise{ background: rgba(245,158,11,.18); color:#ffd58a; }
86
+ .badge-verify {
87
+ display:inline-flex; align-items:center; gap:6px;
88
+ background: rgba(16,185,129,.12); color: var(--ok); padding: 4px 10px;
89
+ border-radius:999px; font-size:12px; font-weight:600; border:1px solid rgba(16,185,129,.3);
90
+ cursor:pointer; transition: all .15s;
91
+ }
92
+ .badge-verify:hover { background: rgba(16,185,129,.22); }
93
+ .badge-verify.bad { background: rgba(239,68,68,.12); color:var(--bad); border-color: rgba(239,68,68,.3); }
94
+ .badge-verify.loading { opacity:.6; }
95
+
96
+ .empty { padding:38px 18px; text-align:center; color: var(--muted); }
97
+ .empty code { background: var(--bg); padding:2px 6px; border-radius:4px; color: var(--accent2); }
98
+
99
+ .how {
100
+ background: var(--bg2); border:1px solid var(--border); border-radius:14px;
101
+ padding: 22px 24px; margin: 24px 0;
102
+ }
103
+ .how ol { padding-left: 20px; margin: 8px 0 0; }
104
+ .how li { margin: 8px 0; color:#cdd5e3; }
105
+ .how code { background: var(--bg); padding:2px 6px; border-radius:4px;
106
+ color: var(--accent2); font-size: 12.5px; }
107
+
108
+ footer {
109
+ border-top:1px solid var(--border); margin-top: 60px; padding: 30px 0;
110
+ color: var(--muted); font-size: 13px; text-align:center;
111
+ }
112
+ footer a { color: var(--muted); margin: 0 10px; }
113
+ </style>
114
+ </head>
115
+ <body>
116
+ <header class="top">
117
+ <div class="container" style="display:flex;align-items:center;justify-content:space-between;width:100%;">
118
+ <div class="brand">Web Agent Bridge <span>·</span> Transparency</div>
119
+ <nav>
120
+ <a href="/">Home</a>
121
+ <a href="/atp.html">ATP Spec</a>
122
+ <a href="/docs.html">Docs</a>
123
+ <a href="/premium.html">Pricing</a>
124
+ </nav>
125
+ </div>
126
+ </header>
127
+
128
+ <main>
129
+ <section class="hero">
130
+ <div class="container">
131
+ <div class="eyebrow">Eat your own cooking</div>
132
+ <h1>Every subscription is a public, signed receipt.</h1>
133
+ <p class="sub">
134
+ WAB doesn't just build the trust layer for agentic commerce — it runs its own
135
+ business on it. Every dollar that flows into webagentbridge.com is itself an
136
+ ATP transaction with a publicly-verifiable Ed25519 receipt.
137
+ </p>
138
+ <p class="tag">
139
+ <strong>This page reads directly from the production ATP ledger.</strong>
140
+ Click <em>Verify</em> on any row to re-check the signature in your browser.
141
+ </p>
142
+
143
+ <div class="stats" id="stats">
144
+ <div class="stat"><div class="k">Receipts issued</div><div class="v" id="s-count">—</div></div>
145
+ <div class="stat"><div class="k">Total settled</div><div class="v" id="s-total">—</div></div>
146
+ <div class="stat"><div class="k">First receipt</div><div class="v" id="s-first">—</div></div>
147
+ <div class="stat"><div class="k">Latest receipt</div><div class="v" id="s-last">—</div></div>
148
+ </div>
149
+ </div>
150
+ </section>
151
+
152
+ <section>
153
+ <div class="container">
154
+ <h2>Live receipt feed</h2>
155
+ <div class="feed">
156
+ <table>
157
+ <thead>
158
+ <tr>
159
+ <th>Issued</th>
160
+ <th>Tier</th>
161
+ <th>Amount</th>
162
+ <th>Receipt ID</th>
163
+ <th>Key</th>
164
+ <th style="text-align:right;">Signature</th>
165
+ </tr>
166
+ </thead>
167
+ <tbody id="rows">
168
+ <tr><td colspan="6" class="empty">Loading public ATP ledger…</td></tr>
169
+ </tbody>
170
+ </table>
171
+ </div>
172
+
173
+ <div class="how">
174
+ <h2 style="margin-top:0;">How to audit this yourself</h2>
175
+ <ol>
176
+ <li>List receipts: <code>GET /api/atp/platform/receipts?limit=50</code></li>
177
+ <li>Fetch full signed body: <code>GET /api/atp/receipts/&lt;receipt_id&gt;</code></li>
178
+ <li>Verify Ed25519 signature: <code>POST /api/atp/receipts/verify</code> with <code>{"receipt_id":"…"}</code></li>
179
+ <li>Or verify offline: every receipt embeds its own public key under <code>signature.public_key</code>. The canonical body is the receipt JSON with the <code>signature</code> field removed.</li>
180
+ </ol>
181
+ </div>
182
+ </div>
183
+ </section>
184
+ </main>
185
+
186
+ <footer>
187
+ <div class="container">
188
+ <div>WAB · <a href="/atp.html">ATP Protocol</a> · <a href="/docs.html">Docs</a> · <a href="/privacy.html">Privacy</a> · <a href="/terms.html">Terms</a></div>
189
+ <div style="margin-top:8px;">Receipts shown on this page are real, generated automatically when payments settle on Stripe.</div>
190
+ </div>
191
+ </footer>
192
+
193
+ <script>
194
+ const $ = (id) => document.getElementById(id);
195
+
196
+ function fmtMoney(cents, currency) {
197
+ const v = (cents || 0) / 100;
198
+ try { return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency || 'USD' }).format(v); }
199
+ catch { return `${v.toFixed(2)} ${currency || ''}`; }
200
+ }
201
+ function fmtDate(s) {
202
+ if (!s) return '—';
203
+ const d = new Date(s.replace(' ', 'T') + (s.includes('Z') ? '' : 'Z'));
204
+ if (isNaN(+d)) return s;
205
+ return d.toISOString().replace('T', ' ').slice(0, 16) + ' UTC';
206
+ }
207
+ function shortId(id, n = 12) { return id ? id.slice(0, n) + '…' : '—'; }
208
+
209
+ async function loadStats() {
210
+ try {
211
+ const r = await fetch('/api/atp/platform/stats').then(r => r.json());
212
+ const s = r.data || {};
213
+ $('s-count').innerHTML = (s.receipts || 0).toLocaleString();
214
+ // total_cents is mixed currency; show approximate USD-equivalent string with note
215
+ $('s-total').innerHTML = `${fmtMoney(s.total_cents || 0, 'USD')} <small>gross</small>`;
216
+ $('s-first').innerHTML = s.first_at ? fmtDate(s.first_at) : '—';
217
+ $('s-last').innerHTML = s.last_at ? fmtDate(s.last_at) : '—';
218
+ } catch (e) {
219
+ $('s-count').textContent = 'n/a';
220
+ }
221
+ }
222
+
223
+ async function verifyReceipt(receiptId, btn) {
224
+ btn.classList.add('loading');
225
+ btn.textContent = 'verifying…';
226
+ try {
227
+ const r = await fetch('/api/atp/receipts/verify', {
228
+ method: 'POST',
229
+ headers: { 'Content-Type': 'application/json' },
230
+ body: JSON.stringify({ receipt_id: receiptId })
231
+ }).then(r => r.json());
232
+ btn.classList.remove('loading');
233
+ if (r.ok && r.verification && r.verification.ok) {
234
+ btn.classList.remove('bad');
235
+ btn.innerHTML = '✓ signature valid';
236
+ } else {
237
+ btn.classList.add('bad');
238
+ btn.textContent = '✗ ' + ((r.verification && r.verification.reason) || 'invalid');
239
+ }
240
+ } catch (e) {
241
+ btn.classList.remove('loading');
242
+ btn.classList.add('bad');
243
+ btn.textContent = '✗ network';
244
+ }
245
+ }
246
+
247
+ async function loadReceipts() {
248
+ try {
249
+ const r = await fetch('/api/atp/platform/receipts?limit=50').then(r => r.json());
250
+ const rows = (r.data || []);
251
+ const tb = $('rows');
252
+ if (!rows.length) {
253
+ tb.innerHTML = `<tr><td colspan="6" class="empty">
254
+ No platform receipts yet. Receipts appear automatically when Stripe settles a subscription.<br>
255
+ Try the protocol with your own intent at <code>/atp.html</code>.
256
+ </td></tr>`;
257
+ return;
258
+ }
259
+ tb.innerHTML = rows.map(row => `
260
+ <tr>
261
+ <td>${fmtDate(row.issued_at)}</td>
262
+ <td><span class="tier ${(row.tier || '').toLowerCase()}">${row.tier || '—'}</span></td>
263
+ <td class="mono">${fmtMoney(row.amount_cents, row.currency)}</td>
264
+ <td class="mono receipt-id" title="${row.receipt_id}">
265
+ <a href="/api/atp/receipts/${row.receipt_id}" target="_blank" rel="noopener">${shortId(row.receipt_id, 22)}</a>
266
+ </td>
267
+ <td class="mono">${shortId(row.key_id, 10)}</td>
268
+ <td style="text-align:right;">
269
+ <button class="badge-verify" data-rid="${row.receipt_id}">Verify</button>
270
+ </td>
271
+ </tr>
272
+ `).join('');
273
+ tb.querySelectorAll('.badge-verify').forEach(btn => {
274
+ btn.addEventListener('click', () => verifyReceipt(btn.dataset.rid, btn));
275
+ });
276
+ } catch (e) {
277
+ $('rows').innerHTML = `<tr><td colspan="6" class="empty">Failed to load: ${e.message}</td></tr>`;
278
+ }
279
+ }
280
+
281
+ loadStats();
282
+ loadReceipts();
283
+ </script>
284
+ </body>
285
+ </html>
package/sdk/atp.js ADDED
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * WAB ATP Client — Agent Transaction Primitive (v3.9.0)
5
+ *
6
+ * A tiny, zero-dependency client (uses global fetch from Node 18+) that
7
+ * lets agents drive the four ATP lifecycle steps against a WAB server:
8
+ *
9
+ * 1. createIntent() — declare what the user authorized
10
+ * 2. authorizeIntent() — user confirms the contract
11
+ * 3. beginTransaction() / step() / transition() — idempotent execution
12
+ * 4. issueReceipt() — fetch the signed receipt
13
+ * 5. verifyReceipt() — public verification (no auth)
14
+ *
15
+ * Example:
16
+ * const atp = new ATPClient({ baseUrl: 'https://webagentbridge.com', token: jwt });
17
+ * const intent = await atp.createIntent({
18
+ * purpose: 'Buy a book ≤ €30',
19
+ * scope: { actions: ['search','add_to_cart','checkout'] },
20
+ * spend_cap_cents: 3000, ttl_seconds: 600,
21
+ * });
22
+ * await atp.authorizeIntent(intent.id);
23
+ * const tx = await atp.beginTransaction({
24
+ * intent_id: intent.id, amount_cents: 1500,
25
+ * idempotency_key: `order-${Date.now()}`,
26
+ * });
27
+ * await atp.transition(tx.id, 'executing');
28
+ * await atp.step(tx.id, { action: 'checkout.confirm', evidence: { order_id: 'X1' } });
29
+ * await atp.transition(tx.id, 'executed');
30
+ * await atp.transition(tx.id, 'settled');
31
+ * const receipt = await atp.issueReceipt(tx.id);
32
+ * const ok = await atp.verifyReceipt(receipt.id);
33
+ * console.log('verified?', ok.verification.ok);
34
+ */
35
+
36
+ class ATPError extends Error {
37
+ constructor(message, { status, code, body } = {}) {
38
+ super(message);
39
+ this.name = 'ATPError';
40
+ this.status = status;
41
+ this.code = code;
42
+ this.body = body;
43
+ }
44
+ }
45
+
46
+ class ATPClient {
47
+ constructor({ baseUrl, token = null, fetchImpl = null } = {}) {
48
+ if (!baseUrl) throw new Error('ATPClient: baseUrl required');
49
+ this.baseUrl = baseUrl.replace(/\/$/, '');
50
+ this.token = token;
51
+ this._fetch = fetchImpl || (typeof fetch !== 'undefined' ? fetch : null);
52
+ if (!this._fetch) throw new Error('ATPClient: no fetch available (Node 18+ or supply fetchImpl)');
53
+ }
54
+
55
+ async _req(method, path, { body, headers = {}, auth = true } = {}) {
56
+ const h = { 'content-type': 'application/json', ...headers };
57
+ if (auth && this.token) h.authorization = `Bearer ${this.token}`;
58
+ const res = await this._fetch(this.baseUrl + path, {
59
+ method,
60
+ headers: h,
61
+ body: body ? JSON.stringify(body) : undefined,
62
+ });
63
+ let json = null;
64
+ try { json = await res.json(); } catch { /* non-JSON body */ }
65
+ if (!res.ok || (json && json.ok === false)) {
66
+ throw new ATPError(json?.message || `HTTP ${res.status}`, {
67
+ status: res.status, code: json?.error, body: json,
68
+ });
69
+ }
70
+ return json && json.data !== undefined ? json.data : json;
71
+ }
72
+
73
+ // ── Intents ───────────────────────────────────────────────────────────────
74
+ createIntent(body) { return this._req('POST', '/api/atp/intents', { body }); }
75
+ listIntents(params = {}) {
76
+ const q = new URLSearchParams(params).toString();
77
+ return this._req('GET', '/api/atp/intents' + (q ? '?' + q : ''));
78
+ }
79
+ getIntent(id) { return this._req('GET', `/api/atp/intents/${encodeURIComponent(id)}`); }
80
+ authorizeIntent(id) { return this._req('POST', `/api/atp/intents/${encodeURIComponent(id)}/authorize`); }
81
+ revokeIntent(id, reason) { return this._req('POST', `/api/atp/intents/${encodeURIComponent(id)}/revoke`, { body: { reason } }); }
82
+
83
+ // ── Transactions ──────────────────────────────────────────────────────────
84
+ beginTransaction(body) {
85
+ const { idempotency_key, ...rest } = body;
86
+ const headers = idempotency_key ? { 'idempotency-key': idempotency_key } : {};
87
+ return this._req('POST', '/api/atp/transactions', { body: rest, headers });
88
+ }
89
+ getTransaction(id) { return this._req('GET', `/api/atp/transactions/${encodeURIComponent(id)}`); }
90
+ step(id, body) { return this._req('POST', `/api/atp/transactions/${encodeURIComponent(id)}/steps`, { body }); }
91
+ transition(id, to, extra = {}) { return this._req('POST', `/api/atp/transactions/${encodeURIComponent(id)}/transition`, { body: { to, ...extra } }); }
92
+ compensate(id, reason) { return this._req('POST', `/api/atp/transactions/${encodeURIComponent(id)}/compensate`, { body: { reason } }); }
93
+
94
+ // ── Receipts ──────────────────────────────────────────────────────────────
95
+ issueReceipt(txId) { return this._req('POST', `/api/atp/transactions/${encodeURIComponent(txId)}/receipt`); }
96
+ getReceipt(receiptId) { return this._req('GET', `/api/atp/receipts/${encodeURIComponent(receiptId)}`, { auth: false }); }
97
+ verifyReceipt(input) {
98
+ const body = typeof input === 'string' ? { id: input } : { receipt: input };
99
+ return this._req('POST', '/api/atp/receipts/verify', { body, auth: false });
100
+ }
101
+ }
102
+
103
+ module.exports = { ATPClient, ATPError };