@nibgate/sdk 0.2.0 → 0.2.2
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/SKILL.md +215 -18
- package/dist/nibgate.js +352 -362
- package/dist/nibgate.js.map +4 -4
- package/dist/nibgate.min.js +2 -2
- package/dist/nibgate.min.js.map +4 -4
- package/package.json +6 -3
- package/src/browser/access.js +168 -0
- package/src/browser/checkout.js +53 -0
- package/src/browser/client.js +97 -0
- package/src/browser/evm-gateway.js +180 -0
- package/src/browser/gate.js +63 -0
- package/src/browser/index.js +9 -770
- package/src/browser/rating-ui.js +161 -0
- package/src/browser/track.js +45 -0
- package/src/server/access.js +22 -32
- package/src/server/admin-page.html +168 -0
- package/src/server/admin.js +18 -180
- package/src/server/gateway.js +8 -3
- package/src/server/webhooks.js +2 -2
- package/src/server/runtime.js +0 -39
package/SKILL.md
CHANGED
|
@@ -119,6 +119,8 @@ export function GET(request: Request) {
|
|
|
119
119
|
|
|
120
120
|
**The `accessPath` is queried exactly as-is.** The SDK does not auto-append `?id=` or route params. If your access route needs a content identifier, include it in the path (e.g., `/api/nibgate/access/my-content-id`) or pass it as a query param in the resource's `accessPath` field.
|
|
121
121
|
|
|
122
|
+
**After successful gateway payment, `accessResponse` automatically emits `payment_completed` and `unlock_completed` events to the Hub** (via `emitHubEvent`). This requires `NIBGATE_SITE_ID`, `NIBGATE_SITE_TOKEN`, and `NIBGATE_API_BASE` to be set — see [Required Env Vars](#11-required-env-vars). Without those, the payment still processes and the unlock proof is returned, but unlock counts and revenue on the Hub will not update.
|
|
123
|
+
|
|
122
124
|
**Custom backends:** If you build your own access route instead of using `accessResponse()`, your route must:
|
|
123
125
|
- Return 402 with the challenge JSON when no payment proof is present
|
|
124
126
|
- Check for `payment-signature` header — if present and valid, return 200 with `{ ok: true, unlockProof: "...", payment: {...} }`
|
|
@@ -212,6 +214,21 @@ app.use(admin.router(express))
|
|
|
212
214
|
// app.post('/admin/nibgate/resources/:id', admin.handleUpdate)
|
|
213
215
|
```
|
|
214
216
|
|
|
217
|
+
### Postgres store
|
|
218
|
+
|
|
219
|
+
For production (Render, Railway, Fly), use the Postgres store instead of the file store (file changes get wiped on redeploy):
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import pkg from 'pg'
|
|
223
|
+
const { Pool } = pkg
|
|
224
|
+
|
|
225
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
|
226
|
+
const store = createPostgresStore(pool, { table: 'nibgate_settings' })
|
|
227
|
+
const admin = createAdminApi({ store })
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The Postgres store auto-creates the table. It uses JSONB for settings and `ON CONFLICT` upserts. Works with any `pg` Pool-like interface.
|
|
231
|
+
|
|
215
232
|
### Protect the admin route
|
|
216
233
|
|
|
217
234
|
Pass an `authorize` function to `createAdminApi`:
|
|
@@ -308,7 +325,18 @@ NIBGATE_BUYER_PRIVATE_KEY=0x... # Only for local testing
|
|
|
308
325
|
|
|
309
326
|
### For plain HTML/JS sites without a bundler
|
|
310
327
|
|
|
311
|
-
|
|
328
|
+
For plain HTML/JS sites without a bundler, use the pre-built CDN bundle:
|
|
329
|
+
|
|
330
|
+
```html
|
|
331
|
+
<script src="https://unpkg.com/@nibgate/sdk@0.2.0/dist/nibgate.min.js"></script>
|
|
332
|
+
<script>
|
|
333
|
+
const { nibgate, gate, mountRatingUI, createEvmGatewayUnlock, createOnchainRating } = Nibgate
|
|
334
|
+
</script>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The bundle includes all dependencies (viem, x402) compiled into a single IIFE script. All browser exports are on the `Nibgate` global. No esbuild step needed.
|
|
338
|
+
|
|
339
|
+
If you prefer to build your own bundle:
|
|
312
340
|
|
|
313
341
|
```bash
|
|
314
342
|
esbuild --bundle --format=iife --global-name=Nibgate \
|
|
@@ -316,22 +344,134 @@ esbuild --bundle --format=iife --global-name=Nibgate \
|
|
|
316
344
|
node_modules/@nibgate/sdk/src/browser/index.js
|
|
317
345
|
```
|
|
318
346
|
|
|
319
|
-
|
|
347
|
+
### Smart Contract Wallets
|
|
320
348
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
349
|
+
Some smart contract wallets (Gnosis Safe, Argent, etc.) may not support `eth_signTypedData_v4` required by the Gateway payment signing. Test with an EOA (MetaMask, Rabby, Coinbase Wallet) first. If your users primarily use smart contract wallets, use the server-side pay route (`payAndUnlockResponse`) with `NIBGATE_BUYER_PRIVATE_KEY` instead.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 9. AI Agent Content Discovery & Purchase (x402)
|
|
354
|
+
|
|
355
|
+
AI agents can discover and purchase content on Nibgate through the same x402 protocol used by browser wallets — no browser, HTML, or widget needed.
|
|
356
|
+
|
|
357
|
+
### Agent Flow
|
|
358
|
+
|
|
359
|
+
1. **Discover** — agent hits the Hub's explore API or polls creator `/nibgate.json` manifests:
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
GET https://nibgate.xyz/api/hub/explore/content?type=article&limit=100
|
|
363
|
+
Accept: application/json
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Response includes content with `price`, `currency`, `access` policy, `websiteDomain`, and the creator's `recipientWallet`.
|
|
367
|
+
|
|
368
|
+
2. **Access** — agent hits the content's access URL identifying as an agent:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
GET https://creator.example/api/nibgate/access?slug=my-article
|
|
372
|
+
x-nibgate-actor: agent
|
|
373
|
+
Accept: application/json
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
3. **402 Challenge** — if payment is required, server returns HTTP 402 with a `PAYMENT-REQUIRED` header containing a base64-encoded x402 v2 challenge with the `extra.verifyingContract` field (Gateway Wallet address).
|
|
377
|
+
|
|
378
|
+
4. **Sign Payment** — agent signs an EIP-3009 `TransferWithAuthorization` using an EVM wallet with USDC in Circle Gateway:
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
import { BatchEvmScheme } from '@circle-fin/x402-batching/client'
|
|
382
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
383
|
+
|
|
384
|
+
const wallet = privateKeyToAccount('0x...')
|
|
385
|
+
const scheme = new BatchEvmScheme(wallet)
|
|
386
|
+
const payload = await scheme.createPaymentPayload(2, gatewayOption)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
5. **Retry** — agent retries the request with the signed payment:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
GET https://creator.example/api/nibgate/access?slug=my-article
|
|
393
|
+
payment-signature: <base64 payload>
|
|
394
|
+
Accept: application/json
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
6. **Unlocked** — server returns `200` with `{ ok: true, unlockProof, payment, resource }`.
|
|
398
|
+
|
|
399
|
+
### Headless GatewayClient (Full Auto Flow)
|
|
400
|
+
|
|
401
|
+
The `GatewayClient` from `@circle-fin/x402-batching/client` handles steps 3-5 automatically:
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
import { GatewayClient } from '@circle-fin/x402-batching/client'
|
|
405
|
+
|
|
406
|
+
const agent = new GatewayClient({
|
|
407
|
+
chain: 'arcTestnet',
|
|
408
|
+
privateKey: '0x...',
|
|
409
|
+
rpcUrl: 'https://rpc.testnet.arc.network'
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
const result = await agent.pay('https://creator.example/api/nibgate/access?slug=my-article', {
|
|
413
|
+
headers: { 'x-nibgate-actor': 'agent' }
|
|
414
|
+
})
|
|
415
|
+
// { data: { ok: true, unlockProof, ... }, formattedAmount: '0.01', transaction: '0x...' }
|
|
326
416
|
```
|
|
327
417
|
|
|
328
|
-
###
|
|
418
|
+
### Agent Wallet Requirements
|
|
329
419
|
|
|
330
|
-
|
|
420
|
+
- **USDC in Circle Gateway** on Arc Testnet (chain ID 5042002) — `agent.deposit('10')` moves USDC from wallet → Gateway
|
|
421
|
+
- **Gas** — on Arc Testnet the native token IS USDC, so the wallet needs USDC for both Gateway deposits and transaction fees
|
|
422
|
+
- **Private key** — must be accessible to the agent process (env var, secure store, or derived from a mnemonic)
|
|
423
|
+
|
|
424
|
+
### Agent Identification
|
|
425
|
+
|
|
426
|
+
Agents identify themselves via headers (in priority order):
|
|
427
|
+
|
|
428
|
+
| Header | Value | Detected When |
|
|
429
|
+
|--------|-------|---------------|
|
|
430
|
+
| `x-nibgate-actor` | `agent` | Explicit agent declaration |
|
|
431
|
+
| `Accept` + `x402` | `application/json` + `true` | Accept header includes JSON and x402 is set |
|
|
432
|
+
| `User-Agent` | bot pattern | Matches `/bot|agent|llm|gpt|claude|perplexity/i` |
|
|
433
|
+
|
|
434
|
+
### Agent-Specific Pricing
|
|
435
|
+
|
|
436
|
+
Creators can set different prices for humans and agents in the resource's `access` policy:
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
const resource = {
|
|
440
|
+
access: { humans: 'free', agents: 'paid' }, // agents pay, humans free
|
|
441
|
+
price: '0.002',
|
|
442
|
+
// or
|
|
443
|
+
access: { humans: 'paid', agents: 'paid' }, // both pay
|
|
444
|
+
// or
|
|
445
|
+
access: { humans: 'paid', agents: 'blocked' }, // agents blocked entirely
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Stress Test Script
|
|
450
|
+
|
|
451
|
+
The repo includes a stress test script at `demo/stress-test-agents.mjs` that simulates N agents discovering content from the Hub and purchasing via x402:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
node demo/stress-test-agents.mjs \
|
|
455
|
+
--mnemonic "twelve word seed phrase here" \
|
|
456
|
+
--agents 50 --concurrency 10 \
|
|
457
|
+
--hub-url "https://nibgate.xyz" \
|
|
458
|
+
--access-template "https://{origin}/api/nibgate/access?slug={slug}" \
|
|
459
|
+
--fund --fund-amount 5 \
|
|
460
|
+
--loop --interval 300
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
The script:
|
|
464
|
+
- Derives deterministic wallets from the mnemonic (wallet[0] = master, wallet[1..N-1] = agents)
|
|
465
|
+
- Discovers content from the Hub explore API or from manifests
|
|
466
|
+
- Filters for agent-purchasable content (paid, not blocked, valid recipient)
|
|
467
|
+
- Funds agents from master via native USDC transfer + Gateway deposit (`--fund`)
|
|
468
|
+
- Purchases content via `GatewayClient.pay()` (full x402 auto flow)
|
|
469
|
+
- Reports per-agent results with aggregate stats
|
|
470
|
+
- Continues on loop with configurable interval
|
|
331
471
|
|
|
332
472
|
---
|
|
333
473
|
|
|
334
|
-
##
|
|
474
|
+
## 10. Onchain Reputation & Ratings
|
|
335
475
|
|
|
336
476
|
After a verified unlock, users can rate content onchain via the `NibgateReputation.sol` contract deployed on Arc Testnet.
|
|
337
477
|
|
|
@@ -365,7 +505,7 @@ Or buttons with `data-nibgate-rating-value="4"` attributes. Ratings are 1-5.
|
|
|
365
505
|
|
|
366
506
|
---
|
|
367
507
|
|
|
368
|
-
##
|
|
508
|
+
## 11. Hardcoded Or File-Based Content
|
|
369
509
|
|
|
370
510
|
Hardcoded content, MDX files, Markdown files, static JSON files, and repo-local media can be gated when the protected payload stays server-side until access is allowed.
|
|
371
511
|
|
|
@@ -382,37 +522,81 @@ Hardcoded content, MDX files, Markdown files, static JSON files, and repo-local
|
|
|
382
522
|
|
|
383
523
|
---
|
|
384
524
|
|
|
385
|
-
##
|
|
525
|
+
## 12. Required Env Vars
|
|
386
526
|
|
|
387
527
|
```bash
|
|
388
528
|
NIBGATE_SITE_ORIGIN=https://creator.example
|
|
389
|
-
NIBGATE_SITE_ID=site_...
|
|
390
|
-
NIBGATE_SITE_TOKEN=ngv_...
|
|
391
|
-
NIBGATE_API_BASE=https://
|
|
529
|
+
NIBGATE_SITE_ID=site_... # Hub website UUID — required for emitHubEvent to report unlocks
|
|
530
|
+
NIBGATE_SITE_TOKEN=ngv_... # Hub website verify token — required for emitHubEvent
|
|
531
|
+
NIBGATE_API_BASE=https://nibgate.xyz # Hub API base — required for emitHubEvent
|
|
392
532
|
NIBGATE_SECRET=server_only_unlock_secret
|
|
393
533
|
NIBGATE_PAYMENT_MODE=circle-gateway
|
|
394
534
|
NIBGATE_PAYMENT_NETWORK=eip155:5042002
|
|
395
535
|
NIBGATE_SELLER_ADDRESS=0xCreatorReceiver
|
|
396
536
|
```
|
|
397
537
|
|
|
538
|
+
**Without `NIBGATE_SITE_ID` and `NIBGATE_SITE_TOKEN`**, `accessResponse()` still processes x402 payments and returns unlock proofs, but `payment_completed` and `unlock_completed` events are NOT sent to the Hub. Unlock counts, revenue, and volume on Explore and leaderboards will not update. Set these to enable automatic event reporting after every successful gateway purchase.
|
|
539
|
+
|
|
398
540
|
Only expose values with `NEXT_PUBLIC_` or client-side env names when they are intentionally public (site id, public token, API base, reputation contract address). Never ship `NIBGATE_SECRET`, private keys, R2 credentials, Resend keys, database URLs, or privileged payment credentials to browser code.
|
|
399
541
|
|
|
400
542
|
---
|
|
401
543
|
|
|
402
|
-
##
|
|
544
|
+
## 13. Common Gotchas
|
|
403
545
|
|
|
404
546
|
- **Widget is only for analytics.** It does not pop up wallets or handle checkout. Use the SDK browser helpers for that.
|
|
405
547
|
- **accessPath is literal.** The SDK does not append `?id=` or route params. Include identifiers in the path or query string manually.
|
|
406
548
|
- **Custom backends must handle `payment-signature` header.** If you build your own access route without `accessResponse()`, your route must detect `payment-signature` header and return 200 on valid proof, not 402.
|
|
407
549
|
- **Cookie `[object Object]` bug.** The unlock proof must be a string (the token from `createUnlockToken`), not an object. `storePaymentProof` now handles both, but ensure your server returns `unlockProof` as a string.
|
|
408
550
|
- **Secrets must match.** `NIBGATE_SECRET` must be the same in `createNibgateServer()`, the access route, and the pay route. Mismatch causes unlock tokens to fail verification silently.
|
|
551
|
+
- **Unlock events need three env vars.** `accessResponse()` emits `payment_completed` and `unlock_completed` to the Hub after gateway payment, but only if `NIBGATE_SITE_ID`, `NIBGATE_SITE_TOKEN`, and `NIBGATE_API_BASE` are set. Without them, the x402 payment still works and the unlock proof is returned, but unlock counts and volume on Explore will not update.
|
|
409
552
|
- **Smart contract wallets** may not support `eth_signTypedData_v4`. Use an EOA or the server-side pay route.
|
|
410
553
|
- **Wallet extension conflicts** can cause signature corruption if multiple wallets are installed. Advise users to disable other wallet extensions when testing.
|
|
411
554
|
- **Deposit before testing locally.** Run `npx nibgate deposit 10` to fund the Gateway facilitator balance.
|
|
412
555
|
|
|
413
556
|
---
|
|
414
557
|
|
|
415
|
-
##
|
|
558
|
+
## 14. Webhooks (Server-to-Server Notifications)
|
|
559
|
+
|
|
560
|
+
Receive notifications when payments, unlocks, or ratings happen — without polling.
|
|
561
|
+
|
|
562
|
+
### Setup
|
|
563
|
+
|
|
564
|
+
```ts
|
|
565
|
+
import { createWebhookManager, createWebhookApi } from '@nibgate/sdk/server'
|
|
566
|
+
import express from 'express'
|
|
567
|
+
|
|
568
|
+
const manager = createWebhookManager({
|
|
569
|
+
webhookUrl: 'https://my-server.com/nibgate-webhook',
|
|
570
|
+
webhookSecret: process.env.NIBGATE_WEBHOOK_SECRET
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
// Mount admin API for subscribing webhooks
|
|
574
|
+
const webhookApi = createWebhookApi(manager, { adminKey: process.env.ADMIN_KEY })
|
|
575
|
+
app.use(webhookApi.router(express))
|
|
576
|
+
|
|
577
|
+
// Emit events from your code
|
|
578
|
+
await manager.emit('payment.completed', { contentId: 'post-1', amount: '0.01', payer: '0x...' })
|
|
579
|
+
await manager.emit('content.unlocked', { contentId: 'post-1', actor: 'human' })
|
|
580
|
+
await manager.emit('content.rated', { contentId: 'post-1', rating: 4, rater: '0x...' })
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Webhook payload format
|
|
584
|
+
|
|
585
|
+
```json
|
|
586
|
+
{
|
|
587
|
+
"event": "payment.completed",
|
|
588
|
+
"timestamp": "2026-07-09T12:00:00.000Z",
|
|
589
|
+
"data": { "contentId": "post-1", "amount": "0.01" }
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
The `x-nibgate-webhook-signature` header contains an HMAC-SHA256 signature of the body using your webhook secret. Verify it on the receiving end to confirm the webhook is authentic.
|
|
594
|
+
|
|
595
|
+
Events emitted automatically by `createNibgateServer`: `payment_completed`, `unlock_completed`.
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## 15. Never Do These
|
|
416
600
|
|
|
417
601
|
- Do not hide paid HTML with CSS or client state after rendering it publicly
|
|
418
602
|
- Do not fake payment IDs, receipts, unlocks, or successful access
|
|
@@ -423,10 +607,23 @@ Only expose values with `NEXT_PUBLIC_` or client-side env names when they are in
|
|
|
423
607
|
|
|
424
608
|
---
|
|
425
609
|
|
|
426
|
-
##
|
|
610
|
+
## 16. Framework Notes
|
|
427
611
|
|
|
428
612
|
- **Next.js**: Use route handlers for `/nibgate.json` and `/api/nibgate/access`. Protect content before rendering paid payloads. Use `server-only` for private modules.
|
|
429
613
|
- **Express/NestJS**: Use middleware, controllers, or guards around protected routes. Mount the admin router at `/admin/nibgate`.
|
|
614
|
+
|
|
615
|
+
For a one-line payment verification middleware:
|
|
616
|
+
|
|
617
|
+
```ts
|
|
618
|
+
import { verifyPayment } from '@nibgate/sdk/server'
|
|
619
|
+
|
|
620
|
+
app.get('/api/content/:id', verifyPayment({ resource: { id: 'my-post', price: '0.01' } }), (req, res) => {
|
|
621
|
+
// req.nibgate.verified is true
|
|
622
|
+
res.json({ content: 'Protected content here' })
|
|
623
|
+
})
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
The middleware checks `x-nibgate-payment-proof` (existing unlock) and `payment-signature` (new Gateway payment). Returns 402 with a challenge if neither is present.
|
|
430
627
|
- **Astro/SvelteKit/Remix**: Use SSR/server routes or endpoints. Plain static builds need a protected API or signed URL for private payloads.
|
|
431
628
|
- **CMS apps**: Save Nibgate resource settings beside each content record. Use `settingsToAccessPolicy()` and `settingsToUnlockPolicy()` to convert stored settings to resource shapes. Mount the admin panel for easy management.
|
|
432
629
|
- **Static/vanilla HTML/JS**: Compile the SDK into a bundle with esbuild (see section 8). Use `gate()` and `nibgate` from the global `Nibgate` namespace.
|