@nibgate/sdk 0.1.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 ADDED
@@ -0,0 +1,605 @@
1
+ # nibgate
2
+
3
+ Framework-agnostic browser and server package for creator-owned paid content.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @nibgate/sdk
9
+ ```
10
+
11
+ Use one package with two entrypoints:
12
+
13
+ ```js
14
+ import { gate } from '@nibgate/sdk'; // browser/client events and UI helpers
15
+ import { createCircleGatewayServer, createNibgateServer } from '@nibgate/sdk/server'; // server-side access enforcement
16
+ ```
17
+
18
+ This works with Next.js, React apps with an API backend, Express, NestJS, Remix, SvelteKit, Astro SSR, custom servers, and CMS/plugin environments. Plain HTML can use the widget and browser events, but real gating still requires a server, edge function, API route, or signed file endpoint.
19
+
20
+ ## Usage
21
+
22
+ First paste the widget script from your Nibgate dashboard into your site:
23
+
24
+ ```html
25
+ <script async src="https://nibgate.xyz/widget.js" data-nibgate-site="SITE_ID" data-nibgate-token="PUBLIC_SITE_TOKEN" data-nibgate-api="https://api.nibgate.xyz"></script>
26
+ ```
27
+
28
+ Then define a resource and let the package handle the repeated browser wiring. Nibgate registers the content and reports unlock activity through the widget:
29
+
30
+ ```js
31
+ import { checkResourceAccess, trackResourcePage } from '@nibgate/sdk';
32
+
33
+ const premiumGuide = {
34
+ id: 'premium-guide',
35
+ title: 'Premium Guide',
36
+ type: 'article',
37
+ price: '0.01',
38
+ recipient: '0x558e7BFaF2Cf1A494F44E50D92431Afc060c9D12',
39
+ path: '/premium-guide',
40
+ access: {
41
+ humans: 'paid',
42
+ agents: 'paid'
43
+ },
44
+ unlock: {
45
+ mode: 'one_time'
46
+ }
47
+ };
48
+
49
+ trackResourcePage(premiumGuide, { source: 'creator-site' });
50
+
51
+ await checkResourceAccess(premiumGuide, {
52
+ accessPath: '/api/nibgate/access',
53
+ source: 'creator-site',
54
+ async createPaymentSignature({ paymentRequiredHeader, resource }) {
55
+ // Production path:
56
+ // Ask the connected user/agent wallet or Gateway adapter to sign/pay
57
+ // the PAYMENT-REQUIRED challenge returned by the creator server.
58
+ return walletGatewayAdapter.pay({
59
+ paymentRequiredHeader,
60
+ resource
61
+ });
62
+ },
63
+ onStatus(message) {
64
+ console.log(message);
65
+ }
66
+ });
67
+ ```
68
+
69
+ For plain browser pages, bind a button without writing custom event glue:
70
+
71
+ ```js
72
+ import { setupResourcePage } from '@nibgate/sdk';
73
+
74
+ setupResourcePage(premiumGuide, {
75
+ source: 'creator-site',
76
+ accessPath: '/api/nibgate/access',
77
+ createPaymentSignature: walletGatewayAdapter.pay,
78
+ button: '[data-nibgate-unlock]',
79
+ status: '[data-nibgate-status]'
80
+ });
81
+ ```
82
+
83
+ For a ready-made unlock button/controller, use `createWalletCheckout`. The package owns the UI state, retries, unlock events, and proof-backed access retry; your wallet/Gateway adapter only has to return the payment signature for the `PAYMENT-REQUIRED` challenge.
84
+
85
+ ```js
86
+ import { createCircleGatewayBrowserAdapter, createWalletCheckout } from '@nibgate/sdk';
87
+
88
+ const [address] = await walletClient.getAddresses();
89
+ const circle = await createCircleGatewayBrowserAdapter({
90
+ chainId: 5042002,
91
+ signer: {
92
+ address,
93
+ signTypedData: (params) => walletClient.signTypedData({
94
+ account: address,
95
+ ...params
96
+ })
97
+ }
98
+ });
99
+
100
+ createWalletCheckout(premiumGuide, {
101
+ button: '[data-nibgate-unlock]',
102
+ status: '[data-nibgate-status]',
103
+ accessPath: '/api/nibgate/access',
104
+ pay: circle.pay
105
+ }).mount();
106
+ ```
107
+
108
+ Do not replace the Gateway payment with a normal wallet message signature. Gateway/x402 payment signatures are payment proofs; wallet message signatures are only used for rating/reputation proof.
109
+
110
+ The browser Circle Gateway adapter expects the creator server to return Circle's real `PAYMENT-REQUIRED` batching challenge. Use the preset on your server route:
111
+
112
+ ```js
113
+ import { createCircleGatewayServer } from '@nibgate/sdk/server';
114
+
115
+ const nibgateServer = createCircleGatewayServer({
116
+ origin: 'https://creator.example',
117
+ secret: process.env.NIBGATE_SECRET,
118
+ network: 'eip155:5042002'
119
+ });
120
+
121
+ export function GET(request) {
122
+ return nibgateServer.accessResponse(request, premiumGuide);
123
+ }
124
+ ```
125
+
126
+ Equivalent manual config:
127
+
128
+ ```js
129
+ createNibgateServer({
130
+ paymentMode: 'circle-gateway',
131
+ network: 'eip155:5042002'
132
+ });
133
+ ```
134
+
135
+ If the server is left in fallback challenge mode, browser checkout will fail closed because there is no Circle `GatewayWalletBatched` verifying contract to sign.
136
+
137
+
138
+ Lower-level event helpers are still available when you need them:
139
+
140
+ ```js
141
+ import { nibgate } from '@nibgate/sdk';
142
+
143
+ nibgate.unlockCompleted('premium-guide', {
144
+ revenue: 0.01,
145
+ currency: 'USDC'
146
+ });
147
+ ```
148
+
149
+ After a verified unlock, a creator UI should submit an onchain rating for the same resource. The hub only counts it into reputation when it can connect the rating wallet to an unlock receipt/proof. Use the built-in controller when you want simple selector-based UI wiring:
150
+
151
+ ```js
152
+ import { createEvmGatewayUnlock, createOnchainRating } from '@nibgate/sdk';
153
+
154
+ const premiumGuide = {
155
+ id: 'premium-guide',
156
+ title: 'Premium Guide',
157
+ type: 'article',
158
+ price: '0.01',
159
+ recipient: post.recipientWallet,
160
+ url: `https://creator.example/blog/${post.slug}`,
161
+ path: `/blog/${post.slug}`
162
+ };
163
+
164
+ let lastPayment = null;
165
+
166
+ const rating = createOnchainRating(premiumGuide, {
167
+ contractAddress: process.env.NEXT_PUBLIC_NIBGATE_REPUTATION_CONTRACT,
168
+ siteId: process.env.NEXT_PUBLIC_NIBGATE_SITE_ID,
169
+ token: process.env.NEXT_PUBLIC_NIBGATE_SITE_TOKEN,
170
+ indexUrl: 'https://api.nibgate.xyz/api/hub/reputation/ratings/index',
171
+ ratingTarget: '[data-nibgate-rating]',
172
+ ratingButtons: '[data-rating]',
173
+ status: '[data-nibgate-status]',
174
+ visible: false,
175
+ getPaymentId: () => lastPayment?.paymentId,
176
+ getUnlockRef: () => lastPayment?.paymentId || lastPayment?.txHash || ''
177
+ });
178
+
179
+ createEvmGatewayUnlock(premiumGuide, {
180
+ accessPath: `/api/content/${post.slug}`,
181
+ connectButton: '[data-nibgate-connect]',
182
+ unlockButton: '[data-nibgate-unlock]',
183
+ walletLabel: '[data-nibgate-wallet]',
184
+ status: '[data-nibgate-status]',
185
+ onUnlock(result) {
186
+ lastPayment = result.payment;
187
+ rating.setPayment(lastPayment);
188
+ rating.setVisible(true);
189
+ }
190
+ });
191
+ ```
192
+
193
+ The lower-level `rateContentOnchain(resource, options)` function is also exported for custom UIs.
194
+
195
+ Ratings are proof-gated. Page views, time spent, scroll depth, and referrers are analytics signals; they should not become trust by themselves. Reputation-critical inputs use indexed onchain rating proofs. Signed ratings remain available only for local tests and migration tooling.
196
+
197
+ Nibgate reputation uses a versioned content identity:
198
+
199
+ ```text
200
+ keccak256("nibgate:content:v1|domain|externalContentId|canonicalUrl")
201
+ ```
202
+
203
+ The namespace lets future versions add metadata hashes, content version hashes, IPFS/Arweave pointers, or creator signatures without changing what old ratings mean.
204
+
205
+ The package talks to the widget through `window.nibgateHub`. If your app runs before the async widget finishes loading, events are queued and flushed once the widget is ready.
206
+
207
+ Content types are `music`, `video`, `article`, and `image`.
208
+
209
+ ## Discovery metadata quality
210
+
211
+ Nibgate can only make good Explore cards and agent-readable records from metadata the creator site provides. Pass the same shape whether content comes from MDX frontmatter, a CMS row, a media table, or a custom admin dashboard:
212
+
213
+ ```js
214
+ import { trackResourcePage, validateResourceMetadata } from '@nibgate/sdk';
215
+
216
+ const resource = {
217
+ id: post.id,
218
+ title: post.title,
219
+ description: post.excerpt,
220
+ type: 'article',
221
+ imageUrl: post.coverImageUrl,
222
+ tags: post.tags,
223
+ price: post.price,
224
+ currency: 'USDC',
225
+ recipient: post.recipientWallet,
226
+ path: `/blog/${post.slug}`,
227
+ url: `https://creator.example/blog/${post.slug}`,
228
+ access: { humans: 'paid', agents: 'paid' },
229
+ unlock: { mode: 'one_time' }
230
+ };
231
+
232
+ const quality = validateResourceMetadata(resource);
233
+ if (!quality.ok) console.warn(quality.errors);
234
+
235
+ trackResourcePage(resource);
236
+ ```
237
+
238
+ Required for clean discovery: `id`, `title`, `url`, `type`.
239
+
240
+ Recommended for rich cards: `description`, `imageUrl`, `tags`.
241
+
242
+ Required for paid content: `price` and `recipient`.
243
+
244
+ The package warns in the browser when important metadata is missing and sends a metadata quality summary to the hub with content events. The backend stores that summary so dashboards can surface setup issues instead of silently creating weak content cards.
245
+
246
+ ## Package UI included
247
+
248
+ The package includes small controller UIs, not a heavy design system:
249
+
250
+ - `createEvmGatewayUnlock(...)` wires connect wallet, disconnect, unlock, wallet label, status text, and unlocked content visibility.
251
+ - `createTransferCheckout(...)` supports direct Arc testnet transfer-style unlocks where Gateway is not used.
252
+ - `createOnchainRating(...)` wires rating buttons, status text, rating panel visibility, payment proof references, onchain rating submission, and hub indexing.
253
+ - `createNibgateContentSettings(...)` gives admin pages stable fields for content type, human/agent access, unlock mode, payment rail, price, recipient wallet, and license.
254
+
255
+ Creators keep their own UI/theme. Nibgate provides the hard parts: resource normalization, metadata validation, x402/Gateway unlock, transfer fallback, event streaming, proof storage, rating tx submission, and hub sync.
256
+
257
+ ## FAQ / integration gotchas
258
+
259
+ - `Does a creator need a server?` Yes for real paid gating. Static-only sites can register discovery events, but protected content needs a server, edge function, CMS webhook, or API route that can return `402` and verify payment proof.
260
+ - `Can every post pay a different wallet?` Yes. Set `recipient` or `payTo` per resource. The package does not force one site-wide receiver.
261
+ - `Can this work with DB blogs, MDX, CMS, plain HTML, or custom apps?` Yes. Convert each content item into a Nibgate resource with `id`, `title`, `type`, `url`, `path`, `price`, and `recipient`.
262
+ - `Why does content not show in Explore?` Usually the widget is missing, the site is not verified, the manifest has not synced yet, `url` is not absolute, or required metadata is missing.
263
+ - `Why does reputation not update?` The rating wallet must have an unlock/payment proof for that content, the rating tx must be onchain, and the backend indexer must sync the tx.
264
+ - `Are page views reputation?` No. Views, referrers, scroll depth, and time spent are analytics signals. Reputation-critical scores come from indexed onchain ratings tied to unlock proof.
265
+ - `Do agents use a different flow?` No. Agents discover the same resource metadata, pay through the same protected route, and can emit/index reputation using the same proof model.
266
+ - `Should hidden content live in browser HTML?` No. The browser should only receive teasers before payment. The full body/media URL should come from a protected route after proof verification.
267
+ - `What breaks most often locally?` Missing wallet provider, wrong chain, missing Gateway client module, CORS on the backend, wrong `NIBGATE_API_BASE`, wrong site token, or a demo route using fallback challenge mode instead of real Gateway mode.
268
+
269
+
270
+ ## Payment rails
271
+
272
+ Gateway is the default rail because it fits x402 and agent-paid HTTP best:
273
+
274
+ ```js
275
+ const resource = {
276
+ id: post.id,
277
+ title: post.title,
278
+ paymentRail: 'gateway',
279
+ price: '0.005',
280
+ recipient: post.recipientWallet
281
+ };
282
+ ```
283
+
284
+ Direct wallet transfer is also a first-class rail for creator apps that want a normal token/native transfer flow:
285
+
286
+ ```js
287
+ const resource = {
288
+ id: post.id,
289
+ title: post.title,
290
+ paymentRail: 'transfer',
291
+ price: '0.005',
292
+ recipient: post.recipientWallet
293
+ };
294
+ ```
295
+
296
+ Transfer unlocks are fail-closed. The browser helper can send a tx hash, but the creator server must verify it before issuing an unlock proof:
297
+
298
+ ```js
299
+ createNibgateServer({
300
+ paymentRail: 'transfer',
301
+ async verifyTransfer({ resource, txHash, payment }) {
302
+ // Verify onchain with viem/RPC/indexer:
303
+ // recipient matches resource.recipient
304
+ // amount/token/chain match resource price/currency/network
305
+ // tx hash has not already been used
306
+ return verified;
307
+ }
308
+ });
309
+ ```
310
+
311
+ ## Access policies
312
+
313
+ For CMS/database-driven sites, keep the gating fields in your own content table, then map each record into a Nibgate resource. Nibgate does not replace your CMS or DB.
314
+
315
+ If the creator has an admin dashboard, put Nibgate settings in that UI and save them beside the post/content record. The package exports canonical field metadata so each framework can render the same settings natively:
316
+
317
+ ```js
318
+ import { NIBGATE_CONTENT_SETTING_FIELDS, createNibgateContentSettings } from '@nibgate/sdk';
319
+
320
+ const defaults = createNibgateContentSettings({
321
+ recipient: creatorDefaultWallet
322
+ });
323
+ ```
324
+
325
+ Then save those values beside the content row:
326
+
327
+
328
+ ```txt
329
+ Nibgate settings
330
+ - Publish to Nibgate discovery
331
+ - Content type: article / music / image / video
332
+ - Human access: free / paid / blocked
333
+ - Agent access: free / paid / blocked
334
+ - Unlock mode: one_time for the MVP
335
+ - Price
336
+ - Currency
337
+ - Payment receiver for this exact content
338
+ - License or citation terms
339
+ ```
340
+
341
+ Example creator DB row:
342
+
343
+ ```js
344
+ const post = {
345
+ id: 'post_123',
346
+ slug: 'agent-economy',
347
+ title: 'The agent economy needs native payments',
348
+ price: '0.005',
349
+ recipientWallet: '0xPostSpecificReceiver',
350
+ humanAccess: 'paid',
351
+ agentAccess: 'paid',
352
+ body: 'Private content stays in your DB.'
353
+ };
354
+ ```
355
+
356
+ Map it before calling the package:
357
+
358
+ ```js
359
+ function postToNibgateResource(post) {
360
+ return {
361
+ id: post.id,
362
+ title: post.title,
363
+ type: 'article',
364
+ price: post.price,
365
+ recipient: post.recipientWallet,
366
+ path: `/blog/${post.slug}`,
367
+ access: {
368
+ humans: post.humanAccess,
369
+ agents: post.agentAccess
370
+ },
371
+ unlock: {
372
+ mode: 'one_time'
373
+ }
374
+ };
375
+ }
376
+ ```
377
+
378
+ `recipient` is resource-level on purpose. A creator can run a blog, marketplace, media library, or API product where each post, video, image pack, or API route pays a different wallet from the database. `recipient`, `payTo`, `receiver`, `receiverAddress`, and `creatorWallet` are accepted aliases. The server-level `recipient` or `NIBGATE_SELLER_ADDRESS` should be treated as a fallback, not the only way to route money.
379
+
380
+ This means Nibgate can fit different creator architectures:
381
+
382
+ - hardcoded MDX posts with frontmatter
383
+ - CMS posts from Sanity, WordPress, or a custom admin
384
+ - DB-backed blogs with per-resource payout wallets
385
+ - paid media/download routes
386
+ - agent-readable API routes
387
+
388
+ The creator app maps its own record into a Nibgate resource; Nibgate does not force a hosted marketplace schema.
389
+
390
+ Use `access` to decide who can read the origin payload before payment:
391
+
392
+ ```js
393
+ access: {
394
+ humans: 'free' | 'paid' | 'blocked',
395
+ agents: 'free' | 'paid' | 'blocked'
396
+ }
397
+ ```
398
+
399
+ Examples:
400
+
401
+ ```js
402
+ // Humans and agents both need payment proof.
403
+ access: { humans: 'paid', agents: 'paid' }
404
+
405
+ // Humans can read publicly, agents need x402/payment proof to crawl or cite.
406
+ access: { humans: 'free', agents: 'paid' }
407
+
408
+ // Humans can pay, agents cannot access this route.
409
+ access: { humans: 'paid', agents: 'blocked' }
410
+ ```
411
+
412
+ Real locking must happen on the server. If you render the full protected payload into HTML and hide it with CSS, crawlers can still scrape it. `@nibgate/sdk/server` is what prevents the protected response from being returned until the request is free, paid with proof, or explicitly allowed by policy.
413
+
414
+ ## Unlock modes
415
+
416
+ The MVP unlock is intentionally simple:
417
+
418
+ ```txt
419
+ pay once -> verify receipt/proof -> issue unlock token -> serve content -> report receipt
420
+ ```
421
+
422
+ Use this today:
423
+
424
+ ```js
425
+ unlock: {
426
+ mode: 'one_time'
427
+ }
428
+ ```
429
+
430
+ The resource shape already has room for future unlock modes, but they should not be presented as production-ready until the payment/session adapters exist:
431
+
432
+ ```js
433
+ unlock: { mode: 'metered_stream', unit: 'second', pricePerUnit: '0.0001' }
434
+ unlock: { mode: 'metered_read', unit: 'paragraph', pricePerUnit: '0.00005' }
435
+ unlock: { mode: 'time_pass', duration: '24h' }
436
+ unlock: { mode: 'agent_quota', maxReads: 20 }
437
+ ```
438
+
439
+ Those later modes let Nibgate grow into background video/audio streaming payments, partial article reads, time passes, and agent usage quotas without changing the one-package architecture.
440
+
441
+ ## Server protection
442
+
443
+ Use `@nibgate/sdk/server` for real route protection. The server layer creates x402-style payment challenges, verifies your payment receipt, and issues a short-lived Nibgate unlock token for the route.
444
+
445
+ ```js
446
+ import { createNibgateServer } from '@nibgate/sdk/server';
447
+
448
+ const nibgateServer = createNibgateServer({
449
+ secret: process.env.NIBGATE_SECRET,
450
+ origin: 'https://creator.example',
451
+ recipient: process.env.NIBGATE_SELLER_ADDRESS, // fallback only
452
+ async verifyPayment({ resource, payment }) {
453
+ // Plug Circle/x402 verification here.
454
+ return Boolean(payment.paymentId);
455
+ }
456
+ });
457
+
458
+ export const GET = nibgateServer.protect({
459
+ id: 'premium-guide',
460
+ title: 'Premium Guide',
461
+ type: 'article',
462
+ price: '0.01',
463
+ recipient: '0x558e7BFaF2Cf1A494F44E50D92431Afc060c9D12',
464
+ path: '/premium-guide',
465
+ access: {
466
+ humans: 'paid',
467
+ agents: 'paid'
468
+ },
469
+ unlock: {
470
+ mode: 'one_time'
471
+ }
472
+ }, async () => {
473
+ return new Response('Premium content');
474
+ });
475
+ ```
476
+
477
+ For JSON API routes, use the smaller helpers:
478
+
479
+ ```js
480
+ import { createNibgateServer, manifestResponse } from '@nibgate/sdk/server';
481
+
482
+ const nibgateServer = createNibgateServer({
483
+ secret: process.env.NIBGATE_SECRET,
484
+ origin: 'https://creator.example',
485
+ recipient: process.env.NIBGATE_SELLER_ADDRESS // fallback only
486
+ });
487
+
488
+ export function GET() {
489
+ return manifestResponse({
490
+ name: 'Creator site',
491
+ origin: 'https://creator.example',
492
+ content: [premiumGuide]
493
+ });
494
+ }
495
+
496
+ export function access(request) {
497
+ return nibgateServer.accessResponse(request, premiumGuide);
498
+ }
499
+
500
+ export function pay(request) {
501
+ return nibgateServer.payAndUnlockResponse(request, premiumGuide, {
502
+ accessPath: '/api/nibgate/access'
503
+ });
504
+ }
505
+ ```
506
+
507
+ `accessResponse` is the production route. It returns a real `PAYMENT-REQUIRED` header, verifies the returned `Payment-Signature`, issues a signed `unlockProof`, and accepts future access through the `x-nibgate-payment-proof` header.
508
+
509
+ `payAndUnlockResponse` is only for local test harnesses and server-side agent tests where you intentionally configure a funded tester key. In `circle-gateway` mode it requires:
510
+
511
+ ```bash
512
+ NIBGATE_PAYMENT_MODE=circle-gateway
513
+ NIBGATE_SELLER_ADDRESS=0xCreatorReceiver
514
+ NIBGATE_BUYER_PRIVATE_KEY=0xFundedTesterPrivateKey
515
+ NIBGATE_BUYER_CHAIN=arcTestnet
516
+ ```
517
+
518
+ The handler calls Gateway, verifies the returned payment, and returns a signed `unlockProof`. It is for controlled server/agent harnesses, not human browser UX.
519
+
520
+ Do not ship `NIBGATE_BUYER_PRIVATE_KEY` in a public creator website. In production, the buyer is the visitor or agent. They connect their own wallet/Gateway, sign/pay the `PAYMENT-REQUIRED` challenge, and send the resulting payment signature back to the creator route.
521
+
522
+ Gateway balance, deposit, and withdraw helpers are also available from the same package:
523
+
524
+ ```js
525
+ import { depositToGateway, getGatewayBalances, withdrawFromGateway } from '@nibgate/sdk/server';
526
+
527
+ const balances = await getGatewayBalances({
528
+ buyerPrivateKey: process.env.NIBGATE_BUYER_PRIVATE_KEY
529
+ });
530
+
531
+ await depositToGateway('1', {
532
+ buyerPrivateKey: process.env.NIBGATE_BUYER_PRIVATE_KEY
533
+ });
534
+
535
+ await withdrawFromGateway('0.5', {
536
+ buyerPrivateKey: process.env.NIBGATE_BUYER_PRIVATE_KEY,
537
+ recipient: '0xCreatorOrBuyer'
538
+ });
539
+ ```
540
+
541
+ For the MVP, browser demos should use the package wallet checkout helper. Server-side funded tester keys are only for command/API harnesses and agent/server tests.
542
+
543
+ For command/API-only demos, package helpers can emit the same standard event sequence to the hub:
544
+
545
+ ```js
546
+ import { emitTestEvents } from '@nibgate/sdk/testing';
547
+
548
+ await emitTestEvents(premiumGuide, {
549
+ origin: 'https://creator.example',
550
+ source: 'creator-site'
551
+ });
552
+ ```
553
+
554
+ The browser `gate(...)` API gives creators the simple unlock UX. The server API is what should enforce real paid access.
555
+
556
+ Payments are non-custodial in the Nibgate model. The receiving address belongs to the creator site/package config, and different sites can use different receiving addresses. Nibgate Hub records paid unlock events and payment metadata; it does not hold funds or provide withdrawals.
557
+
558
+ ## Payment receipts
559
+
560
+ Nibgate supports two receipt paths today:
561
+
562
+ - `circle-gateway`: store the Circle payment id and a `receiptUrl` only when Circle or your gateway layer returns a real public/internal receipt URL.
563
+ - `arc-testnet`: store the Arc transaction hash and optional `chainExplorerUrl`, usually an Arcscan transaction URL.
564
+
565
+ Do not fabricate gateway receipt URLs. If no provider receipt URL exists, send the payment id/hash and Nibgate will show the recorded payment with the best available explorer link.
566
+
567
+ On Arc testnet, Gateway payments carry a signed authorization payload rather than a simple transfer hash for every unlock. Nibgate stores that signed payment payload as the payment id/receipt metadata. If your provider exposes a memo, transaction hash, or explorer URL, pass it as `memo`, `txHash`, or `chainExplorerUrl`; the hub will keep it with the payment event.
568
+
569
+ ## End-to-end product flow
570
+
571
+ 1. Creator installs `@nibgate/sdk`.
572
+ 2. Creator maps posts, media, downloads, API routes, or CMS records into Nibgate resources.
573
+ 3. Creator adds the widget snippet from the Nibgate hub.
574
+ 4. Creator exposes `nibgate.json` with package helpers.
575
+ 5. Nibgate backend verifies the widget and discovers resources.
576
+ 6. Human visitors or AI agents hit a protected route.
577
+ 7. Creator server returns `402 PAYMENT-REQUIRED`.
578
+ 8. Human wallet or agent wallet/Gateway pays and returns `Payment-Signature`.
579
+ 9. Creator server verifies the payment, issues a signed unlock proof, and serves content.
580
+ 10. Browser requests include that proof through `x-nibgate-payment-proof` for future access checks.
581
+ 11. Package/widget emits view, content, unlock, payment, and engagement events to the hub.
582
+ 12. After unlock, the visitor or agent can submit an onchain content rating tied to the same proof.
583
+ 13. Hub stores metrics for content, site, and creator analytics, then indexes onchain ratings into content, site, and creator reputation.
584
+
585
+ ## Local demo
586
+
587
+ The repo includes a plain Express creator site that uses the package without any framework adapter:
588
+
589
+ ```bash
590
+ npm run dev:demo
591
+ ```
592
+
593
+ The demo imports `@nibgate/sdk/server`, serves the browser client locally, registers article/music/image/video content, and protects `/articles/premium-agent-economy`.
594
+
595
+ It also includes local routes for database/custom CMS, MDX/frontmatter, headless CMS, static teaser/protected API, media/file, and agent/API publishing styles under `/examples`.
596
+
597
+ Real unlocks are fail-closed. Set these env vars before using the local Gateway payment button:
598
+
599
+ ```bash
600
+ NIBGATE_PAYMENT_MODE=circle-gateway
601
+ NIBGATE_SELLER_ADDRESS=0xYourSellerAddress
602
+ NIBGATE_BUYER_PRIVATE_KEY=0xYourFundedBuyerPrivateKey
603
+ ```
604
+
605
+ Without a real Gateway payment, the server will not issue a Nibgate unlock token.
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@nibgate/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Framework-agnostic browser and server package for creator-owned gated content, unlock events, and receipts.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./src/index.js",
8
+ "module": "./src/index.js",
9
+ "types": "./src/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./src/index.d.ts",
13
+ "import": "./src/index.js",
14
+ "default": "./src/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./src/server.d.ts",
18
+ "import": "./src/server.js",
19
+ "default": "./src/server.js"
20
+ },
21
+ "./tracking": {
22
+ "types": "./src/tracking.d.ts",
23
+ "import": "./src/tracking.js",
24
+ "default": "./src/tracking.js"
25
+ },
26
+ "./testing": {
27
+ "types": "./src/testing.d.ts",
28
+ "import": "./src/testing.js",
29
+ "default": "./src/testing.js"
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
33
+ "scripts": {
34
+ "pack:check": "npm pack --dry-run",
35
+ "prepublishOnly": "npm run pack:check"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "files": [
41
+ "src",
42
+ "README.md"
43
+ ],
44
+ "keywords": [
45
+ "nibgate",
46
+ "creator",
47
+ "paywall",
48
+ "analytics",
49
+ "x402"
50
+ ],
51
+ "license": "MIT",
52
+ "dependencies": {
53
+ "@circle-fin/x402-batching": "^3.0.4",
54
+ "@x402/core": "^2.17.0",
55
+ "@x402/evm": "^2.17.0",
56
+ "viem": "^2.54.1"
57
+ }
58
+ }
@@ -0,0 +1,3 @@
1
+ export function browserWindow() {
2
+ return typeof window === 'undefined' ? null : window;
3
+ }