{
  "markdown": "# Nibgate — open protocol for paid content\n\n[Website](https://nibgate.xyz) | [Docs](https://docs.nibgate.xyz) | [X](https://x.com/nibgate)\n\nNibgate is a verified content discovery, unlock, and reputation layer for creator-owned work. Creators keep their content on their own domains. Nibgate verifies the source, indexes structured public metadata, records unlock/payment signals, and helps humans and AI agents discover quality content without moving it into a closed marketplace.\n\nBuilt on ARC testnet and the x402 protocol, with two payment rails: Circle Gateway (EIP-3009 facilitator) and direct USDC transfer to the creator's receiver.\n\n**Key packages:**\n\n- **SDK (`packages/nibgate/`)** — `@nibgate/sdk` npm package for gating paid content on any creator site. Browser and server entrypoints, x402/Gateway unlocks, event streaming, onchain ratings. Includes the shared access-control rule (`canAccess`, `normalizeWhitelist`, `paidCutoffWallets`) used by both rails and the encryption primitives (`crypto.js`).\n- **Wallet (`packages/wallet/`)** — `@nibgate/wallet` npm package with a `./react` entry: shared Reown AppKit (AppKit + wagmi) wallet provider, SIWE sign-in, and the `<NibgateUnlock>`/`GatewayWallet` single-checkout used by the hub header, Nibshare, and Subblogs.\n- **Subblogs (`subblogs/`)** — Full blog platform for creators at `*.nibgate.xyz`. Articles, photos, music, video, documents, free and paid posts. Next.js frontend (port 3002), Express backend (port 4000), PostgreSQL. SIWE wallet auth, whitelists/invite-only tiers, AES-256-GCM encryption at rest.\n- **Hub (`frontend/` + `backend/`)** — The main `nibgate.xyz` app: public site, creator dashboard, Explore discovery, analytics, API, Nibshare quick-share rail, and widget hosting.\n- **CLI (`packages/cli/`)** — Internal tooling for local dev, site verification, and hub connection.\n- **Docs (`docs/`)** — Documentation site at docs.nibgate.xyz.\n\n## Product Thesis\n\nThe open web needs a content discovery standard for paid, high-quality resources. Nibgate provides the verified layer that says:\n\n1. this domain is controlled by this creator\n2. this content exists at this route\n3. this content can be unlocked by humans or agents\n4. this creator has real reputation signals from verified activity\n5. this payment history routes directly to the configured receiver for the resource\n\nThe creator-owned site remains the source of truth, while the hub becomes the public index, analytics surface, and reputation graph around verified content.\n\nIt consists of four connected parts:\n\n1. **The Widget**: Creators paste one script on their site. It proves domain ownership, creates visitor/session context, and sends browser-safe page activity to Nibgate.\n2. **The Package**: Creators install the `@nibgate/sdk` npm package to gate real content on their own site. The package knows the content, unlock, and payment lifecycle, then reports those events through the widget bridge.\n3. **The Hub**: `nibgate.xyz` is the creator dashboard, discovery surface, and analytics layer. It verifies sites, stores content metadata, aggregates metrics, and shows profile/site/content/earnings data.\n4. **The Discovery Layer**: Explore, public profiles, content metadata, receipts, and reputation signals make verified creator content readable by people and AI agents.\n\n## Repo Layout\n\n```txt\nbackend/       Express hub API — payment verification, hosted-pay resolution,\n               metrics, site/manifest sync, revenue keeper (fee-wallet sweeps)\nfrontend/      Next.js hub UI — public site, dashboard, Explore, leaderboards\nsubblogs/      Subblogs — full blog platform for creators (Express+Prisma + Next.js)\npackages/      @nibgate/sdk npm package + wallet package + CLI tooling\ncontracts/     Solidity + Foundry: reputation contracts and the per-creator\n               GatewayFeeWallet / GatewayFeeWalletFactory (revenue model)\ndocs/          Nextra docs site for docs.nibgate.xyz\nscripts/       E2E flows and deployment tooling (reputation, revenue factory)\nnibgate.config.json  Sample CLI config (routes to local demo content)\n```\n\nLocal-only, not tracked: `e2e/` (Playwright browser harness), `swarm/` (agent wallets), `video/`, `v2-labs/`, `revenue-model/` (research/poc). See `.gitignore`.\n\n## Workspace Shape\n\n### `backend/` & `frontend/` (The Hub)\n\nThe Hub is the main Nibgate app and API surface. It acts as the creator dashboard, public site, and discovery directory:\n\n- `/explore` The discovery masonry grid indexing all creator content.\n- `/ledger` Public activity feed — every unlock, payment, and onchain rating across all sites, searchable and filterable. New entries slide in live.\n- `/discovery.md` Agent guidance — plain-language description of Nibgate endpoints, payment flow, and rating flow for AI agents.\n- `/auth/*` Sign-In with Ethereum (SIWE) authentication.\n- `/hub/*` Hub connection, sync, verification, and event ingestion.\n- `/hub/ledger?domain=X` Public ledger endpoint with optional domain filter for per-site activity.\n\n### `subblogs/` (Subblogs — Creator Blog Platform)\n\nThis is an example creator blog with paid content gating. Two services:\n\n- **`subblogs/backend/`** (Express + Prisma) — serves content pages, handles payments via the hub, issues unlock proofs. When R2 is configured, all post bodies and media are encrypted at rest (free and paid); free content is decrypted server-side and served to anyone, paid content only after onchain proof verification. The access endpoint (`GET /api/<type>/<slug>`, a short mirror of `GET /api/nibgate/access`) is the single source of truth for premium content — returns post body only after proof verification. Each post page also self-describes for agents: `GET /api/nibgate/manifest?path=/<type>/<slug>` returns the per-post machine-readable contract (advertised via a `<link rel=\"alternate\">` element and the `Link` response header).\n- **`subblogs/frontend/`** (Next.js) — public blog UI. Premium content is **never** in the HTML. The `NibgateUnlock` component fetches content from the protected access endpoint after proof verification. Admin panel (`/admin/posts`) has a per-post stats sheet showing unlocks, revenue in USDC, and the underlying receipts for each post.\n\n**Critical rule:** The `GET /api/blog/posts/:slug` endpoint decrypts and returns the body for **free** posts, and strips the `body` from **paid** posts. Premium body is only returned by the access endpoint (`GET /api/<type>/<slug>` or `GET /api/nibgate/access`) after valid proof. This prevents paid content from ever appearing in page source, while free content still reads publicly.\n\n### `nibshare` (Quick-Share Gated Content)\n\nA hosted quick-share rail inside the hub: a wallet owner publishes an encrypted payload with an optional price, wallet whitelist, and a required expiry (max 7 days out), and gets a short link at `nibgate.xyz/ns/<slug>`. No domain required. Bodies and media are always AES-256-GCM encrypted at rest in Cloudflare R2; unlock is x402 USDC on Arc via the server-side decrypt proxy (free public shares read openly, invite-only shares require a session-corroborated whitelisted wallet, paid shares after payment). This is a **private** product — it is never indexed in hub discovery, the ledger, or reputation. The share page self-describes for agents: `GET /nibshare/:slug/manifest` (also reachable at `https://api.nibgate.xyz/ns/<slug>`, the short read route) returns the machine-readable contract (advertised via `<meta name=\"nibgate:*\">`, JSON-LD, `data-nibgate-*` attributes, a `<link rel=\"alternate\">` element, and the `Link` response header), and the Nibgate MCP server exposes it as the `resolve_share` tool.\n\nServer source is at `backend/src/server/nibshare/{controller,service,utils,routes}.js`; it delegates access decisions to the SDK's `access-policy.js` (possession rule, pay-before-deny, idempotent receipt granting) and exposes the same entitlement/ban/revoke finance as Subblogs.\n\nDocs live with the implementation, not in a separate top-level folder:\n\n- `backend/src/server/nibshare/README.md` — product, threat model, auth, env\n- `backend/src/server/nibshare/API.md` — the HTTP contract\n- `backend/src/server/nibshare/STORAGE.md` — R2 layout, encryption, media serving\n- `backend/src/server/nibshare/STORAGE-TIER-PLAN.md` — planned Arweave/Lit tiers (not shipped)\n\n### `packages/nibgate/`\n\nThis is the public creator package. It is intentionally tiny and framework-agnostic:\n\n```bash\nnpm install @nibgate/sdk\n```\n\nAgents and coding assistants should read the compact integration guide before editing a creator site:\n\n- package copy: `node_modules/@nibgate/sdk/SKILL.md`\n- public copy: `https://nibgate.xyz/skill.md`\n\nIt owns:\n\n- browser entrypoint: `createGate(...)`, `nibgate.content(...)`, `nibgate.view(...)`, `nibgate.unlockStarted(...)`, `nibgate.unlockCompleted(...)`, and `nibgate.paymentCompleted(...)`\n- server entrypoint: `createNibgateServer(...)`, `protect(...)`, `nibgateServer.accessFor(...)`, payment challenges, and unlock token verification\n- queueing events until the Hub widget is ready\n- normalizing content types to `music`, `video`, `article`, `image`, and `document`\n- access policies for humans and agents: `free`, `paid`, or `blocked`\n- unlock policies that start with `one_time` for the MVP and leave room for metered reading, streaming, passes, and agent quotas later\n\n### `packages/cli/`\n\nThe CLI package is private internal tooling for local development and future setup automation. It owns:\n\n- `npx nibgate`\n- local status and setup checks\n- config generation helpers\n- hub connection and domain verification commands\n- future scaffolding around widget/package setup\n\nThe public `@nibgate/sdk` package owns route protection, payment challenge metadata, unlock tokens, and package event APIs.\n\n### `demo/`\n\nLocal-only and not tracked in this repo. An origin app that behaves like a creator-owned site, used to validate the install flow, DB-backed content mapping, package events, and protected content flow without polluting hub code. Real-template examples belong under `demo/examples/*`, where each example starts from a recognizable starter repo and adds the Nibgate package integration. If your checkout has no `demo/` directory, skip the demo commands below — everything else runs without it.\n\n## Run\n\nInstall once:\n\n```bash\nnpm install\n```\n\nStart the backend:\n\n```bash\nnpm run dev:backend\n```\n\nStart the frontend:\n\n```bash\nnpm run dev:frontend\n```\n\nStart the example creator origin (requires the local-only `demo/` directory — skip if your checkout doesn't have it):\n\n```bash\ncd demo/examples/next-mdx-blog\nNIBGATE_SITE_ORIGIN=http://localhost:4301 npm run dev -- -p 4301\n```\n\nOpen:\n\n- [http://localhost:3001](http://localhost:3001)\n- [http://localhost:3001/explore](http://localhost:3001/explore)\n- [http://localhost:4301](http://localhost:4301)\n\n## Environment\n\nBackend variables:\n\n```bash\nDATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/railway\nCORS_ORIGIN=https://nibgate.xyz,http://localhost:3001\nBLOG_OWNER_WALLET=0x558e7BFaF2Cf1A494F44E50D92431Afc060c9D12\nRESEND_API_KEY=your_resend_api_key\nRESEND_NEWSLETTER_SEGMENT_ID=seg_your_newsletter_segment_id\nRESEND_NEWSLETTER_TOPIC_ID=topic_your_newsletter_topic_id\nMETRIC_HASH_SALT=generate_a_long_random_secret\nTRACKING_RATE_LIMIT_MAX=180\nTRACKING_RATE_LIMIT_WINDOW_MS=60000\nTRACKING_VIEW_DEDUPE_WINDOW_MS=1800000\nMANIFEST_SYNC_INTERVAL_MS=900000\nMANIFEST_SYNC_RETRY_AFTER_MS=1800000\nMANIFEST_SYNC_BATCH_SIZE=100\n```\n\nFor production, attach a Railway Postgres database to the backend service and use Railway's `DATABASE_URL` value. The Prisma datasource is PostgreSQL-only now, so every backend environment must provide `DATABASE_URL`.\n\n`BLOG_OWNER_WALLET` is the single signed wallet that can create, edit, publish, draft, or delete posts from `/dashboard/blog`. No other wallet can access the editor APIs.\n\nNewsletter signups are stored in the local `NewsletterSubscriber` table first. If `RESEND_API_KEY` is configured, the backend also syncs each signup into Resend Contacts. `RESEND_NEWSLETTER_SEGMENT_ID` and `RESEND_NEWSLETTER_TOPIC_ID` are optional, but recommended so newsletter signups are grouped separately from transactional contacts. Without Resend envs, signups still save locally with a pending sync status.\n\n`METRIC_HASH_SALT` is used to create privacy-preserving server-side visitor hashes for analytics dedupe. Use a stable secret in production; rotating it resets unique visitor continuity. Tracking dedupe defaults are 30 minutes for page/resource views, 24 hours for content registration and payment/unlock payment ids, 5 minutes for time events, and 30 seconds for engagement events. The backend also rate-limits `/hub/evt` (the legacy `/api/hub/track` alias still works) per site/IP/visitor bucket.\n\nManifest sync keeps Explore and dashboard metadata fresh when creators change titles, descriptions, images, prices, tags, or routes. The backend reads verified-site manifests from `/nibgate.json`, `/.well-known/nibgate.json`, `/v1/nibgate/manifest`, or `/v1/nibgate/nibgate.json`. Creators can also refresh a site manually from `/dashboard/sites`; event traffic from the widget/package still updates the same content record whenever the stable content id is seen again.\n\nFrontend variables:\n\n```bash\nNEXT_PUBLIC_API_URL=https://api.nibgate.xyz\n```\n\nWhen running locally, point `NEXT_PUBLIC_API_URL` at the local backend so `/api/*` rewrites and public server-rendered blog pages read from the same API.\n\nAfter changing the Prisma schema, sync the database and regenerate the client before running the backend:\n\n```bash\nDATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/railway \\\npnpm --filter @nibgate/cli exec prisma db push\n\nDATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/railway \\\npnpm --filter @nibgate/cli exec prisma generate\n```\n\nProduction deploys that change `packages/cli/prisma/schema.prisma` need the same `prisma db push` against the Railway database before the new backend starts handling traffic.\n\n## CLI\n\nUseful local commands:\n\n```bash\nnpm run routes\nnpx nibgate status\nnpx nibgate connect\nnpx nibgate verify\nnpx nibgate event resource_view premium-article\nnpx nibgate balance\nnpx nibgate deposit 1.0\n```\n\nPackage-facing shape:\n\n```bash\nnpx nibgate init\nnpx nibgate dev\nnpx nibgate routes\n```\n\n## Product Flow\n\nNibgate uses a verified discovery event architecture:\n\n```txt\nCreator site\n  Hub widget\n    - verifies ownership\n    - owns siteId/token/session context\n    - sends events to the backend\n\n  Nibgate package\n    - gates real creator content\n    - knows content metadata and unlock/payment lifecycle\n    - calls window.nibgateHub when content is viewed, unlocked, or paid for\n\nNibgate backend\n  - validates siteId/token/origin\n  - stores content metadata\n  - stores metric events\n  - aggregates dashboard data\n  - updates discovery and reputation signals\n```\n\nWhen a creator installs `@nibgate/sdk` on their own site, the package is responsible for:\n\n1. protecting paid routes and gated content on the creator origin with server-side access checks\n2. handling x402/Circle unlock logic\n3. registering content metadata for `music`, `video`, `article`, `image`, and `document`\n4. emitting content-level events such as `resource_view`, `unlock_started`, `unlock_completed`, and `payment_completed`\n5. passing resource ids, titles, prices, and paths to the Hub widget when users interact with protected content\n\nFor real blogs and CMS-backed sites, gating fields should live beside the creator's content record in their own database or admin UI. Nibgate maps that row into a resource:\n\n```js\n{\n  id,\n  title,\n  type,\n  price,\n  path,\n  access: {\n    humans: 'free' | 'paid' | 'blocked',\n    agents: 'free' | 'paid' | 'blocked'\n  },\n  unlock: {\n    mode: 'one_time'\n  }\n}\n```\n\nThis works across Next.js, React plus an API backend, Express, NestJS, Remix, SvelteKit, Astro SSR, MDX server rendering, headless CMS apps, and traditional CMS/plugin environments. Plain static HTML can use the widget for verification and events, but protected content still needs a server, edge function, API route, or signed URL.\n\nFor the hackathon MVP, unlocks should be simple and real:\n\n```txt\npay once -> verify receipt/proof -> issue unlock token -> serve content -> report receipt to Nibgate\n```\n\nThe package keeps an `unlock` policy field so future versions can add richer modes without changing the creator integration shape:\n\n```js\nunlock: { mode: 'one_time' }       // MVP\nunlock: { mode: 'metered_stream' } // later: pay by watched seconds/minutes\nunlock: { mode: 'metered_read' }   // later: pay by section/paragraph/token window\nunlock: { mode: 'time_pass' }      // later: pay for time-limited access\nunlock: { mode: 'agent_quota' }    // later: pay for agent reads/crawls\n```\n\nOnly `one_time` should be treated as production-ready for the first release.\n\nThe Hub widget is responsible for:\n\n1. proving site ownership with one script tag on the creator domain\n2. automatically sending page views\n3. detecting content markers from `data-nibgate-*` attributes or `nibgate:*` meta tags\n4. exposing `window.nibgateHub.registerContent(...)`, `window.nibgateHub.track(...)`, and unlock/payment helpers so the package can stream individual resource events\n5. attaching site id, public token, visitor id, session id, URL, path, referrer, and scroll depth before sending to the backend\n\nThe hub is responsible for:\n\n1. registering the site\n2. verifying domain ownership by fetching the creator homepage and checking for the widget token\n3. ingesting events for page views, content views, unlocks, revenue, and performance\n4. indexing resource metadata from streamed package/widget events\n\nThat keeps real content and enforcement on the creator domain while the hub stores only the metadata, events, receipts, and aggregates needed for discovery, analytics, earnings, and reputation.\n\n## Discovery and Reputation\n\nNibgate discovery is not just a public gallery. It is the index of verified creator-owned content that humans and agents can trust enough to browse, cite, unlock, and route payments toward.\n\nThe public ledger (`/ledger`) provides a live, auditable feed of every view, unlock, payment, and rating with on-chain proof links. Each row is expandable for full detail — wallet addresses, tx hashes, timestamps. Stats totals animate on update.\n\nThe hub can rank and filter content using:\n\n1. verified domain ownership\n2. content type: `music`, `video`, `article`, `image`, or `document`\n3. creator profile and username\n4. page views, content views, unlock attempts, and paid unlocks\n5. direct x402 or Arc testnet receipt metadata\n6. freshness, tags, source routes, and referral signals\n7. future human and agent feedback tied to real interactions\n\nReputation starts at the content level. In the current MVP, content/site/creator reputation combines verified package/widget activity with stronger payment, unlock receipt, and indexed onchain rating signals. Reputation-critical actions are proof-gated: payment/unlock receipts need provider proof, and ratings are accepted through the onchain reputation contract/indexer after unlock eligibility is proven.\n\nThe intended unlock model remains creator-site native:\n\n1. creator publishes content metadata from their own site\n2. buyer or agent pays/unlocks the content through x402, Gateway, direct wallet payment, or another site-owned rail\n3. the package reports the unlock/payment receipt to Nibgate\n4. the verified receipt makes that wallet eligible to rate\n5. the wallet submits an onchain rating transaction for the content\n6. Nibgate indexes the rating event, matches it to the unlock proof, and updates reputation alongside analytics context\n\nPublic content reputation can be shown as a `0.0-5.0` star rating. Site reputation and creator reputation then roll up from content ratings plus verification health, consistency, receipts, and source quality.\n\nCreator reputation should be a `1-100` score for the wallet/account that owns verified sites and the content under them. That means creator trust can grow from actual unlocks, payment receipts, ratings from eligible wallets/agents, useful agent feedback, and content quality signals connected back to the verified source. It also means a creator can have a strong overall reputation while a specific new piece is still earning trust.\n\n## Local Connect Flow\n\n1. Sign in to the Hub with a wallet.\n2. Add a site from the dashboard.\n3. Copy the generated widget script.\n4. Paste it into the creator site HTML.\n5. Deploy the creator site.\n6. Click verify in the Hub.\n\nWidget snippet shape:\n\n```html\n<script\n  async\n  src=\"https://www.nibgate.xyz/widget.js\"\n  data-nibgate-site=\"SITE_ID\"\n  data-nibgate-token=\"PUBLIC_SITE_TOKEN\"\n  data-nibgate-api=\"https://api.nibgate.xyz\"\n></script>\n```\n\nContent-level registration and tracking should be emitted by the package through the widget:\n\n```js\nimport { createGate } from '@nibgate/sdk';\n\nconst premiumGuide = createGate({\n  id: \"premium-guide\",\n  title: \"Premium Guide\",\n  type: \"article\",\n  price: \"0.01\",\n  path: \"/premium-guide\",\n  access: {\n    humans: \"paid\",\n    agents: \"paid\"\n  },\n  unlock: {\n    mode: \"one_time\"\n  }\n});\n\npremiumGuide.content();\npremiumGuide.view();\n\nawait premiumGuide.unlock(async () => {\n  // Run payment and server-side verification here.\n  return {\n    paymentId: \"payment_123\",\n    paymentProvider: \"arc-testnet\",\n    txHash: \"0x...\",\n    chainExplorerUrl: \"https://testnet.arcscan.app/tx/0x...\",\n    revenue: 0.01,\n    currency: \"USDC\"\n  };\n});\n```\n\nThe lower-level bridge remains available for advanced integrations:\n\n```js\nimport { nibgate } from '@nibgate/sdk';\n\nnibgate.unlockCompleted(\"premium-guide\", {\n  revenue: 0.01,\n  currency: \"USDC\"\n});\n```\n\nThe package talks to `window.nibgateHub` under the hood. If creator code runs before the async widget finishes loading, package events are queued in the browser and flushed when the widget becomes available.\n\nServer-side protection lives under `@nibgate/sdk/server`:\n\n```js\nimport { createCircleGatewayServer } from '@nibgate/sdk/server';\n\nconst nibgateServer = createCircleGatewayServer({\n  secret: process.env.NIBGATE_SECRET,\n  recipient: process.env.NIBGATE_SELLER_ADDRESS,\n  async verifyPayment({ payment }) {\n    // Plug Circle/x402 verification here.\n    return Boolean(payment.paymentId);\n  }\n});\n\nexport const GET = nibgateServer.protect({\n  id: \"premium-guide\",\n  title: \"Premium Guide\",\n  type: \"article\",\n  price: \"0.01\",\n  path: \"/premium-guide\"\n}, async () => {\n  return new Response(\"Premium content\");\n});\n```\n\nEarnings are non-custodial. The package/server flow should send payment to the receiving address configured for that creator site. One creator can connect multiple sites, and each site can use a different receiver. Nibgate Hub stores the payment/unlock records for analytics and accounting; it does not custody funds or expose a withdraw flow.\n\nReceipt handling is provider-aware:\n\n- Circle Gateway payments should store `paymentProvider: \"circle-gateway\"`, `paymentId`, and `receiptUrl` only if Circle or the gateway integration returns a real receipt URL.\n- Arc testnet payments should store `paymentProvider: \"arc-testnet\"`, `txHash`, `chainId`, and optionally `chainExplorerUrl` for the Arcscan transaction page.\n- If neither a Circle receipt URL nor an Arc explorer URL exists, the hub shows the internal recorded payment id/hash instead of inventing a fake receipt.\n\nOr declared in markup:\n\n```html\n<article\n  data-nibgate-resource\n  data-nibgate-id=\"premium-guide\"\n  data-nibgate-title=\"Premium Guide\"\n  data-nibgate-type=\"article\"\n  data-nibgate-price=\"0.01\"\n>\n  ...\n</article>\n```\n\nHub endpoints:\n\n- `POST /hub/sites/register`\n- `POST /hub/sites/:websiteId/verify`\n- `POST /hub/track`\n- `GET /hub/sites`\n- `GET /hub/dashboard/content`\n- `GET /hub/dashboard/analytics`\n- `GET /hub/dashboard/earnings`\n\n## Tracking Model\n\nThe widget does not treat a whole site as one undifferentiated blob. It can record:\n\n- page views for each route where the script loads\n- resource views when a page has `data-nibgate-resource` markup or `nibgate:*` meta tags\n- package events when the installed `nibgate` runtime calls `window.nibgateHub.track(...)`\n- unlock and payment events tied to a specific resource id\n\nThat means one domain can have many tracked resources, but Nibgate content types are intentionally limited to `music`, `video`, `article`, `image`, and `document`.\n\n## Storage Model\n\nThe hub store is PostgreSQL through Prisma:\n\n- creator content still lives on the creator site\n- resource metadata is created or updated from streamed widget/package events\n- site, content, analytics, and earnings records live in Postgres\n\n## Payments\n\n\nFor browser wallet checkout, the creator access route must return Circle Gateway's real `PAYMENT-REQUIRED` batching challenge. The simplest safe setup is `createCircleGatewayServer(...)`; the manual equivalent is `createNibgateServer({ paymentMode: 'circle-gateway', network: 'eip155:5042002' })`.\n\n### Revenue model (fee wallets + protocol fee)\n\nOn Nibgate-hosted surfaces (hub widget checkout, Subblogs), payments go to a per-creator `GatewayFeeWallet` contract instead of the creator EOA. The contract is immutable (no proxy/admin), keeps 99% for the creator, and routes a 1% protocol fee to the treasury on `distribute()` — enforced on-chain with a hard 5% cap set at deploy. A background keeper sweeps settled balances to creator wallets. Full model, diagrams, and env reference: [docs/revenue-model](https://docs.nibgate.xyz/revenue-model) and `contracts/GatewayFeeWallet.sol`.\n\n```bash\nNIBGATE_HOSTED_PAY=true \\\nNIBGATE_FEE_WALLET_FACTORY=0xYourFactoryAddress \\\nNIBGATE_TREASURY=0xYourTreasuryAddress \\\nNIBGATE_FEE_KEEPER=1 \\\nNIBGATE_KEEPER_PRIVATE_KEY=0xyourKeeperPrivateKey \\\nnpm run dev\n```\n\nCircle Gateway mode (requires the local-only `demo/` directory):\n\n```bash\nNIBGATE_PAYMENT_MODE=circle-gateway \\\nNIBGATE_SELLER_ADDRESS=0xYourSellerWallet \\\nNIBGATE_BUYER_PRIVATE_KEY=0xyourBuyerPrivateKey \\\nNIBGATE_BUYER_CHAIN=arcTestnet \\\nnpm run dev:demo\n```\n\nGateway balance helpers:\n\n```bash\nNIBGATE_PAYMENT_MODE=circle-gateway \\\nNIBGATE_BUYER_PRIVATE_KEY=0xyourBuyerPrivateKey \\\nNIBGATE_BUYER_CHAIN=arcTestnet \\\nnpx nibgate balance\n\nNIBGATE_PAYMENT_MODE=circle-gateway \\\nNIBGATE_BUYER_PRIVATE_KEY=0xyourBuyerPrivateKey \\\nNIBGATE_BUYER_CHAIN=arcTestnet \\\nnpx nibgate deposit 1.0\n```\n\n### Direct rail (transfer)\n\nThe second payment rail sends USDC straight from the buyer's wallet to the creator's `payTo` address — no Gateway facilitator. The browser checkout broadcasts the transfer and submits the tx hash as `x-nibgate-transfer-tx` on the access retry; the server verifies the mined receipt on-chain (USDC `Transfer` log to the seller for at least the price) before minting the unlock proof. Receipts are stored with `paymentProvider: 'direct-transfer'`. In the unlock UI, `NibgateUnlock`/`useNibgateUnlock` switch between the two rails via the `paymentRail` option (`'gateway'` or `'transfer'`).\n\nBecause a broadcast transfer is public chain data, the retry must also carry an **ownership proof**: an EIP-191 signature (made by the paying wallet) over\n\n```\nNibgate transfer ownership\ntx:<txHash lowercased>\nresource:<resource path or url>\n```\n\nsent as the `x-nibgate-tx-owner` header (`transferOwnershipMessage()` builds this string). Missing/mismatched proofs fail with `transfer-ownership-proof-required` / `transfer-owner-mismatch`. Self-hosters can opt out with `NIBGATE_TX_OWNER_PROOF_OPTIONAL=true`; hosted surfaces additionally claim each txHash single-use per content id.\n\n```js\nimport { payWithTransfer, createTransferCheckout } from '@nibgate/sdk'\n\nawait payWithTransfer(resource, {\n  accessPath: '/api/nibgate/access',\n  checkout: createTransferCheckout(resource, {\n    sendTransfer: async ({ recipient, amount }) => /* broadcast USDC transfer, return { txHash } */\n  })\n})\n```\n\n### Wallet-tied access\n\nPaid unlocks are wallet-bound, not device-bound. After paying, the wallet has a lifetime entitlement backed by its receipt; reconnect the wallet (and sign in via SIWE) on any device and the server re-verifies the receipt and ban status and re-serves the content — no re-pay. The unlock UI never grants from a device-stored proof unless a wallet is connected, and it relocks (tears down the payload + clears the proof) on disconnect.\n\n## Local URLs\n\n- Hub frontend: `http://localhost:3001`\n- Hub Explore: `http://localhost:3001/explore`\n- Hub backend API: `http://localhost:3000`\n- Subblogs frontend: `http://localhost:3002`\n- Subblogs backend API: `http://localhost:4000`\n- Example origin: `http://localhost:4301`\n- Demo premium route: `http://localhost:4301/hello-world`\n- Hub widget: `http://localhost:3001/widget.js`\n\nRun the backend first, then run the local package-to-hub proof script:\n\n```bash\nnpm run dev:backend\nnpm run e2e:nibgate\n```\n",
  "bytes": 28852,
  "sha": "a373584cbddb62853b49a1b1e270a7960ed576e3a4412ab05508282618bc478b",
  "repo_slug": "fortunesayspump/nibgate",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_fortunesayspump_nibgate_279f84c4/readme"
}