+ 3
+ db-tunnel
+ tcp
+ 192.168.1.42:22
+ 3d ago
+
+
+
+
+
+
+ 1-7select
+ Rrescan local
+ Lrescan LAN
+ Ashow all
+ Mbookmark
+ ↑↓navigate
+ ↵start
+ Bback
+
+ tunnel setup
+
+
+
+
+
+
+ screen tunnel setup (pick a target)
+ grid 168×42 cells · jetbrains mono
+ palette rosé pine
+ toggle use the Tweaks button to flip protocol / theme / tier
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index 1d8328f..ee56f7a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "magical-meteor",
"version": "0.0.1",
"dependencies": {
+ "@astrojs/sitemap": "3.6.1",
"@astrojs/starlight": "^0.37.2",
"astro": "^5.6.1",
"sharp": "^0.34.2"
diff --git a/package.json b/package.json
index b05a790..a4c7f1c 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,9 @@
"astro": "astro"
},
"dependencies": {
+ "@astrojs/sitemap": "3.6.1",
"@astrojs/starlight": "^0.37.2",
"astro": "^5.6.1",
"sharp": "^0.34.2"
}
-}
\ No newline at end of file
+}
diff --git a/public/JavaScript-logo.png b/public/JavaScript-logo.png
new file mode 100644
index 0000000..0da1dcd
Binary files /dev/null and b/public/JavaScript-logo.png differ
diff --git a/public/chains/ethereum.png b/public/chains/ethereum.png
new file mode 100644
index 0000000..19f0aa0
Binary files /dev/null and b/public/chains/ethereum.png differ
diff --git a/public/chains/icp.png b/public/chains/icp.png
new file mode 100644
index 0000000..ba5346a
Binary files /dev/null and b/public/chains/icp.png differ
diff --git a/public/chains/solana.svg b/public/chains/solana.svg
new file mode 100644
index 0000000..14c18b3
--- /dev/null
+++ b/public/chains/solana.svg
@@ -0,0 +1,11 @@
+
diff --git a/public/fonts/OFL-JetBrainsMono.txt b/public/fonts/OFL-JetBrainsMono.txt
new file mode 100644
index 0000000..3f34847
--- /dev/null
+++ b/public/fonts/OFL-JetBrainsMono.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/OFL-SpaceGrotesk.txt b/public/fonts/OFL-SpaceGrotesk.txt
new file mode 100644
index 0000000..014b75d
--- /dev/null
+++ b/public/fonts/OFL-SpaceGrotesk.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/fonts/jetbrains-mono-variable.woff2 b/public/fonts/jetbrains-mono-variable.woff2
new file mode 100644
index 0000000..2c68c08
Binary files /dev/null and b/public/fonts/jetbrains-mono-variable.woff2 differ
diff --git a/public/fonts/space-grotesk-variable.woff2 b/public/fonts/space-grotesk-variable.woff2
new file mode 100644
index 0000000..ef68853
Binary files /dev/null and b/public/fonts/space-grotesk-variable.woff2 differ
diff --git a/public/logo-light.svg b/public/logo-light.svg
new file mode 100644
index 0000000..838623f
--- /dev/null
+++ b/public/logo-light.svg
@@ -0,0 +1,73 @@
+
diff --git a/public/opentui/nodes.html b/public/opentui/nodes.html
new file mode 100644
index 0000000..90c0930
--- /dev/null
+++ b/public/opentui/nodes.html
@@ -0,0 +1,483 @@
+
+
+
+
+
+ Consensus · Nodes (opentui-faithful)
+
+
+
+
+
+
+
+
+
+
+
+
consensus — ~/code/consensus — 168×46
+
$ consensus nodes
+
+
+
+
+
+
+
+ ▲ CONSENSUS
+ your private network, on demand
+
+
+ connected
+ acctbob
+ bal$24.18
+ v2.4.1
+
+
+
+
+
+
+
+ ▸
+ Nodes
+
+
lease a node to pin all tunnel, proxy & socket traffic to it
+
+
148 nodes · 5 regions · refreshed 12s ago
+
+
+
+
+
+ No lease active — traffic uses automatic node selection
+
+
+
+
+
+
+
+
+
+
+ sort: score ↓
+
+
+
+
+
Available nodes
+
+
+
+
+
Node
+
Domain
+
Region
+
IP
+
Score
+
Latency
+
Load
+
Caps
+
+
+
+
+
+ ↑↓ navigate
+ ↵ lease selected
+ ·
+ showing 8 of 148 · pin all proxy/tunnel/ws traffic to this node
+
+ screen websocket live (metered socket)
+ grid 168×42 cells · jetbrains mono
+ palette rosé pine
+ try press S to compose · type · ↵ to send (server echoes back)
+
+
+
+
+
+
+
+
+
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..210df35
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,6 @@
+# robots.txt — https://docs.consensus.canister.software
+# Full access for all crawlers, including search engines and AI answer/training bots.
+User-agent: *
+Allow: /
+
+Sitemap: https://docs.consensus.canister.software/sitemap-index.xml
diff --git a/public/seo/consensus-node-provider-card.png b/public/seo/consensus-node-provider-card.png
new file mode 100644
index 0000000..16961c3
Binary files /dev/null and b/public/seo/consensus-node-provider-card.png differ
diff --git a/public/seo/consensus-protocol-seo-card.png b/public/seo/consensus-protocol-seo-card.png
new file mode 100644
index 0000000..ba5dc66
Binary files /dev/null and b/public/seo/consensus-protocol-seo-card.png differ
diff --git a/public/typescript_logo.webp b/public/typescript_logo.webp
new file mode 100644
index 0000000..d90a4c9
Binary files /dev/null and b/public/typescript_logo.webp differ
diff --git a/src/components/Head.astro b/src/components/Head.astro
index 33e2753..a7bd84a 100644
--- a/src/components/Head.astro
+++ b/src/components/Head.astro
@@ -31,6 +31,10 @@ const hasTwitterCard = hasMeta('name', 'twitter:card');
{!hasTwitterImage && }
{!hasTwitterCard && }
+
+
+
+
diff --git a/src/content/docs/index.mdx b/src/content/docs/index.mdx
deleted file mode 100644
index 05a3ef5..0000000
--- a/src/content/docs/index.mdx
+++ /dev/null
@@ -1,24 +0,0 @@
----
-title: Consensus Protocol
-description: Get started building with Consensus.
-template: splash
-hero:
- tagline: A decentralised x402 HTTPS proxy and websocket service network.
- image:
- dark: ../../assets/logo-light.svg
- light: ../../assets/logo-dark.svg
- actions:
- - text: Learn more
- link: /protocol/info/
- icon: right-arrow
- - text: Read the Whitepaper
- link: "#whitepaper"
- variant: minimal
- attrs:
- class: wp-cta
- aria-haspopup: dialog
----
-
-import WhitepaperModal from "../../components/WhitepaperModal.astro";
-
-
diff --git a/src/content/docs/protocol/api.md b/src/content/docs/protocol/api.md
index 567f4d8..b379730 100644
--- a/src/content/docs/protocol/api.md
+++ b/src/content/docs/protocol/api.md
@@ -154,7 +154,7 @@ Routes an outbound HTTP request through the Consensus network. Identical request
| `x-node-domain` | `string` | Route through a specific node domain |
| `x-node-exclude` | `string` | Exclude a specific node domain from selection |
| `x-idempotency-key` | `string` | Manual deduplication key. Auto-generated if omitted |
-| `x-api-key` | `string` | Scopes the deduplication key — requests with different keys never share cache entries |
+| `x-api-key` | `string` | **Deprecated and inert.** Stripped before the upstream call; it no longer scopes the deduplication key or carries any identity. |
**Payment**
diff --git a/src/content/docs/protocol/architecture.md b/src/content/docs/protocol/architecture.md
index 2f7c0fd..12071d8 100644
--- a/src/content/docs/protocol/architecture.md
+++ b/src/content/docs/protocol/architecture.md
@@ -45,7 +45,7 @@ Supporting decisions:
- **Request:** `POST /proxy` with `{ target_url | target_ref, method, headers, body }`.
- **Response:** `{ status, statusText, data }` by default, or the full `{ status, statusText, headers, data, meta }` (`ProxyResponse`) when the request carries `x-verbose: true`.
-- **Client-controlled headers** that influence routing/caching, all stripped before the upstream call: `x-cache-ttl`, `x-verbose`, `x-api-key`, `x-idempotency-key`, `x-node-region`, `x-node-domain`, `x-node-exclude`.
+- **Client-controlled headers** that influence routing/caching, all stripped before the upstream call: `x-cache-ttl`, `x-verbose`, `x-idempotency-key`, `x-node-region`, `x-node-domain`, `x-node-exclude`. The deprecated `x-api-key` is also stripped, but carries no identity or deduplication behaviour.
### Node control tunnel
diff --git a/src/content/docs/protocol/concepts.md b/src/content/docs/protocol/concepts.md
index 8fe6921..2108aab 100644
--- a/src/content/docs/protocol/concepts.md
+++ b/src/content/docs/protocol/concepts.md
@@ -80,7 +80,7 @@ registerExactIcpScheme(client, { signer })
The deduplication key is the core fingerprint that identifies a unique request. It is a SHA-256 hash of the canonicalized request, computed before any payment is checked.
-The key is derived from five inputs:
+The key is derived from four inputs:
| Input | How it's canonicalized |
|---|---|
@@ -88,13 +88,16 @@ The key is derived from five inputs:
| **URL** | Lowercased scheme and host, default ports removed, query params sorted alphabetically, fragment stripped |
| **Semantic headers** | Only `accept` and `content-type` are included, lowercased and trimmed |
| **Body** | SHA-256 hash of the body — JSON objects are deep-sorted before hashing for stability |
-| **Scope** | SHA-256 of the `x-api-key` header value, or `"global"` if absent |
-Two requests produce the same deduplication key if and only if all five inputs are equivalent after canonicalization. This means:
+Two requests produce the same deduplication key if and only if all four inputs are equivalent after canonicalization. This means:
- `https://api.example.com/prices?a=1&b=2` and `https://api.example.com/prices?b=2&a=1` produce the **same key**
- The same JSON body with different key ordering produces the **same key**
-- The same request with different `x-api-key` values produces **different keys** — they never share a cache entry
+- Two callers issuing the same request produce the **same key** and share the cache entry — the scope is always global
+
+:::note[No caller scoping]
+The deduplication scope is global. There is no per-caller namespace: the same canonical request is the same key no matter who sends it, which is what makes deduplication pay off across users. Earlier versions scoped the key by `x-api-key`; that behaviour has been removed, and the header is now stripped without effect. Do not send anything caller-specific in `accept` or `content-type`, the only headers that enter the key.
+:::
The key is computed twice per request: once before the payment check (to serve cache hits for free) and once after payment to store the response.
diff --git a/src/content/docs/quickstart/usage.mdx b/src/content/docs/quickstart/usage.mdx
index e41a416..b01b3c8 100644
--- a/src/content/docs/quickstart/usage.mdx
+++ b/src/content/docs/quickstart/usage.mdx
@@ -30,8 +30,8 @@ npm install @canister-software/consensus-cli
| Option | Type | Default | Description |
|---|---|---|---|
-| `mode` | `"inclusive" \| "exclusive"` | `"inclusive"` | `inclusive` proxies all routes **except** those in `routes`. `exclusive` proxies **only** the listed routes. |
-| `routes` | `string[]` | `[]` | Route paths to include or exclude depending on `mode`. |
+| `mode` | `"only" \| "except"` | `"except"` | `only` proxies **only** the listed `routes` (allowlist). `except` proxies everything **but** the listed `routes` (denylist). Required whenever `routes` is non-empty. |
+| `routes` | `string[]` | `[]` | Path rules selected by `mode`. Query params are ignored; matching is on path only. |
| `matchSubroutes` | `boolean` | `false` | When `true`, a route match also applies to all sub-paths beneath it. |
| `strategy` | `"auto" \| "manual"` | `"auto"` | `auto` intercepts `fetch()` calls transparently. `manual` exposes `req.consensus.fetch()` for explicit control. |
| `cache_ttl` | `number` | — | TTL in seconds for node-level response caching. |
@@ -46,6 +46,29 @@ npm install @canister-software/consensus-cli
Spend tracking uses the fixed server price of **$0.0001** per paid `/proxy` request. Cached responses are not charged.
:::
+### Route Filtering
+
+`mode` decides how `routes` is read. The two are exact inverses:
+
+```ts
+ProxyClient(fetchWithPayment, { mode: 'only', routes: ['/api'] }) // proxy only /api
+ProxyClient(fetchWithPayment, { mode: 'except', routes: ['/health'] }) // proxy everything but /health
+ProxyClient(fetchWithPayment, {}) // proxy everything
+```
+
+`mode` is **required whenever `routes` is non-empty**. `{ routes: ['/api'] }` reads equally well as "proxy `/api`" and "don't proxy `/api`", and picking one silently would mean billing you for the opposite of what you meant. Misconfigurations are rejected when the client is constructed, rather than quietly changing what you pay for:
+
+| Config | Result |
+|---|---|
+| `{ routes: ['/api'] }` | throws — say which `mode` you meant |
+| `{ mode: 'exclusve', routes: ['/api'] }` | throws — unrecognized `mode`, no silent fallback |
+| `{ mode: 'only', routes: [] }` | throws — an empty allowlist would disable proxying entirely |
+| `{ mode: 'except', routes: [] }` | fine — proxies everything (the default) |
+
+:::caution[Renamed in 0.2.0]
+`mode` was `"inclusive" | "exclusive"`. Those names described the proxy's breadth while sitting next to `routes`, so they read as the inverse of what they did — under `"inclusive"`, listing a route *excluded* it from proxying. `"exclusive"` is now `"only"` and `"inclusive"` is now `"except"`. The old names still work as deprecated aliases with identical behaviour.
+:::
+
### Auto Strategy
In `auto` mode, `ProxyClient` intercepts the global `fetch()` within the request context — your route handlers require no changes.
@@ -59,7 +82,7 @@ const app = express()
// Proxy only /price — all other routes use direct fetch
app.use(
ProxyClient(fetchWithPayment, {
- mode: 'exclusive',
+ mode: 'only',
routes: ['/price'],
matchSubroutes: false,
strategy: 'auto',
@@ -108,13 +131,62 @@ const response = await req.consensus.fetch(
)
```
+### Batch Requests
+
+`batch()` runs many proxy requests as one group, either concurrently or strictly one at a time:
+
+```ts
+const results = await proxy.batch(
+ [
+ 'https://api.example.com/users/1',
+ 'https://api.example.com/users/2',
+ { target_url: 'https://api.example.com/events', method: 'POST', body: { kind: 'sync' } },
+ ],
+ { mode: 'parallel', concurrency: 4 }
+)
+```
+
+| Option | Default | Description |
+|---|---|---|
+| `mode` | `"parallel"` | `"parallel"` dispatches concurrently; `"sequential"` runs strictly one at a time, in input order. |
+| `concurrency` | `8` | Max requests in flight in `"parallel"` mode. Ignored by `"sequential"`. |
+| `signal` | — | An `AbortSignal` that stops dispatching further items. |
+
+Items accept a bare URL string or a full `request()` payload. Any per-request option (`profile`, `cache_ttl`, `verbose`, `node_region`, `node_domain`, `node_exclude`, `direct`) can be set for the whole batch and overridden on a single item through its `options` key.
+
+**Every item settles.** `batch()` never rejects because one request failed — results come back in input order as a discriminated union, so a partial failure is a normal, inspectable outcome:
+
+```ts
+for (const result of results) {
+ if (result.ok) console.log(result.index, result.value.status, result.value.data)
+ else console.error(result.index, result.error.status, result.error.message)
+}
+
+const succeeded = results.filter((r) => r.ok).map((r) => r.value)
+```
+
+It *does* reject before dispatching anything if the input itself is malformed — a missing `target_url`, an unrecognized `mode`, a non-positive `concurrency`. That is a programming error, and finding it halfway through a paid batch would be an expensive way to learn about a typo.
+
+#### Choosing a mode
+
+Each item is an independent paid `/proxy` request, so caching, profiles, node routing, and the spend limit all behave exactly as they do for a single `request()`. The one behavioural difference between the modes is the budget:
+
+- **`"sequential"`** — the spend guard sees every response before the next request goes out, so `limit_usd` is enforced **exactly**. Also the right choice for rate-limited upstreams.
+- **`"parallel"`** — faster, but up to `concurrency` requests are already in flight when the limit is reached, so spend can overshoot `limit_usd` by up to `concurrency - 1` requests. Keep `concurrency` low when running close to a hard cap.
+
+Items that arrive after the limit is reached stand down to a direct `fetch`, exactly as a single request would, and are reported as successes carrying `meta.bypassed === true`.
+
+:::note[Client-side fan-out]
+Batching currently fans out over the existing `POST /proxy` endpoint — one payment per item. The API is shaped so a future server-side `POST /proxy/batch`, with one payment and one routing pass for the whole group, can back it without a breaking change.
+:::
+
### Framework-Agnostic Usage
Use `runWithPath()` to scope interception in any server framework and `createFetch()` for explicit route-scoped fetch — no Express required:
```ts
const proxy = ProxyClient(fetchWithPayment, {
- mode: 'exclusive',
+ mode: 'only',
routes: ['/api'],
limit_usd: 1.25,
})
diff --git a/src/content/docs/x402proxy/networks.md b/src/content/docs/x402proxy/networks.md
deleted file mode 100644
index 3efbf00..0000000
--- a/src/content/docs/x402proxy/networks.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-title: Supported Tokens & Networks
-description: Define supported tokens and networks
----
-
-
\ No newline at end of file
diff --git a/src/content/docs/x402proxy/x402.md b/src/content/docs/x402proxy/x402.md
deleted file mode 100644
index ab4796d..0000000
--- a/src/content/docs/x402proxy/x402.md
+++ /dev/null
@@ -1,112 +0,0 @@
----
-title: What is x402?
-description: The HTTP payment protocol that powers Consensus — how 402 challenges work, what clients and servers exchange, and how the flow is implemented
-sidebar:
- order: 1
----
-
-x402 is an open payment protocol built on top of HTTP. It gives any HTTP endpoint the ability to require a micropayment before responding, using the standard `402 Payment Required` status code as a machine-readable payment challenge.
-
-The protocol is network-agnostic — the same flow works over ICP, EVM, and Solana. Consensus implements x402 on both sides: the server issues challenges and the client handles them transparently.
-
----
-
-## The Payment Flow
-
-Every x402 interaction follows four steps:
-
-```
-Client Resource Server Facilitator
- │ │ │
- ├─── GET /api/data ────────────►│ │
- │ │ │
- │◄── 402 Payment Required ──────┤ │
- │ X-Payment-Required: {...} │ │
- │ │ │
- │ (client signs payment) │ │
- │ │ │
- ├─── GET /api/data ────────────►│ │
- │ X-Payment: {...} │ │
- │ ├─── verify payment ────────►│
- │ │◄── verified ───────────────┤
- │◄── 200 OK ────────────────────┤ │
-```
-
-**Step 1 — Initial request**
-The client sends a normal HTTP request. No special headers are required.
-
-**Step 2 — Payment challenge**
-The server responds `402 Payment Required` with a `X-Payment-Required` header containing a JSON object that describes what payment is needed:
-
-```json
-{
- "scheme": "exact",
- "network": "icp:1:xafvr-biaaa-aaaai-aql5q-cai",
- "maxAmountRequired": "100000",
- "payTo": "",
- "description": "Premium data endpoint",
- "mimeType": "application/json"
-}
-```
-
-**Step 3 — Client pays**
-The client reads the challenge, signs and submits a payment on the specified network, then retries the original request with the signed proof in the `X-Payment` header.
-
-**Step 4 — Verification and response**
-The server forwards the payment proof to the facilitator. The facilitator verifies on-chain settlement and returns confirmation. The server then processes the request and returns the response.
-
-From the client's perspective, steps 2–4 are invisible. `wrapFetchWithPayment` intercepts the `402`, handles the payment, and returns the final `200` response.
-
----
-
-## The `X-Payment-Required` Header
-
-When the server issues a `402`, the `X-Payment-Required` header contains a JSON object with the following fields:
-
-| Field | Type | Description |
-|---|---|---|
-| `scheme` | `string` | Payment scheme — always `"exact"` for Consensus |
-| `network` | `string` | Network identifier — see [Supported Networks](/x402proxy/networks/) |
-| `maxAmountRequired` | `string` | Maximum amount the server will accept as payment |
-| `payTo` | `string` | Address or principal that receives the payment |
-| `description` | `string` | Human-readable description of what is being paid for |
-| `mimeType` | `string` | MIME type of the response once payment is accepted |
-
-A single route can advertise multiple accepted networks simultaneously. The client picks one.
-
----
-
-## The `X-Payment` Header
-
-After paying, the client attaches proof in the `X-Payment` header. The exact structure is scheme- and network-specific, but always includes:
-
-| Field | Type | Description |
-|---|---|---|
-| `scheme` | `string` | Must match the scheme from the challenge |
-| `network` | `string` | Must match the network from the challenge |
-| `payload` | `string` | Signed payment proof — format is network-specific |
-
-The server does not validate this itself — it forwards it to the facilitator, which does the chain-level verification.
-
----
-
-## The `exact` Scheme
-
-Consensus uses the `exact` scheme exclusively. Under this scheme:
-
-- The client pays **exactly** the amount specified in `maxAmountRequired`
-- No range, no estimation, no partial payment
-- Payment goes directly to `payTo` on-chain
-- The facilitator confirms that the exact transfer occurred before the server responds
-
-This makes pricing deterministic and auditable — both the client and server know the cost before any payment is made.
-
----
-
-## Implementation
-
-For complete setup guides on both sides of the payment flow:
-
-- **[Facilitator — Resource Server](/facilitator/resource-server/)** — protect Express routes with `paymentMiddleware`, connect to the facilitator, accept multiple networks
-- **[Facilitator — Client](/facilitator/client/)** — build a client with `wrapFetchWithPayment` across ICP, EVM, and Solana
-- **[Supported Networks](/facilitator/networks/)** — every network and token the facilitator accepts
diff --git a/src/pages/index.astro b/src/pages/index.astro
new file mode 100644
index 0000000..5aa2fe4
--- /dev/null
+++ b/src/pages/index.astro
@@ -0,0 +1,538 @@
+
+
+
+
+
+ Consensus | Tunnels, proxies, and stable IPs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Put a local port or device on a public HTTPS URL in seconds. No signup, monitor every request in real time.
+
+
+
+
+
+
+
+ 02
+
+ PROXIES: Wrap fetch(). Ship secured.
+
+
+
+
+
+
+
+
import { ProxyClient } from'@canister-software/consensus-cli'
+
+// intercepts outbound HTTP transparently
+const fetch = ProxyClient.wrap(globalThis.fetch)
+
+const res = await fetch('https://external.com', {
+ region: 'us-east',
+ caching: true,
+ spendCap: '0.05', // stands down to direct fetch when hit
+})
+
+
+
Drop-in SDK
+
Wrap fetch() once and every outbound call routes through Consensus: protected, deduplicated, cached, and metered per request. Existing route handlers stay untouched.
+
+
+
+
+
+
+
+ 03
+
+ STABLE IPs: Verify and pin traffic to a static IP
+
+
+
+
+
+
+
+
$ consensus ip list --region us-east
+NODE ID DOMAIN REGION SCORE
+7f3a91 cobalt-heron.consensus.canister.software us-east 94
+
+$ consensus ip lease cobalt-heron.consensus.canister.software
+✓ Leased: cobalt-heron.consensus.canister.software (traffic pinned)
+
+
+
Pin the IP
+
Lease a verified, performant node and pin every tunnel, proxy, and socket to one static IP. Whitelist once, reconnect automatically, stop fighting dynamic firewalls.
+
+
+
+
+
+
+
+ 04
+
+ WEBSOCKETS: Prepaid, metered sessions for stateless apps
+
Prepay time, data, or both. Each persistent session is metered by the protocol and closed cleanly once the budget runs out. No accounts, no state.
+
+
+
+
+
+
+
+
+
+
Pay on any of three networks.
+ one facilitator · the exact scheme
+
+
+
+
+
+ Ethereum
+ Mainnet
+
+
+
+
+
+ Solana
+ Mainnet
+
+
+
+
+
+ Internet Computer
+ Mainnet
+
+
+
+
+
+
+
+
+
+
+
+
Pure application-level routing with a Cache.
+
+
Wire native proxy handlers directly into your JavaScript/TypeScript processes using clean dependency injection, and manage parameters locally via secure profiles.
+
+
+
+
+
+
+ Integration
+ 01
+
+
Drop-in SDK, or dependency injection
+
Two ways to wire into a codebase. Import the SDK and call ProxyClient or SocketClient as middleware directly in application code, or hand the protocol native handlers through dependency injection. Both paths run at the application level and never decrypt HTTPS, so existing request logic and TLS stay exactly as written.
+
+
+
+
+ Configuration
+ 02
+
+
No accounts. Local profiles.
+
Nothing to sign up for and no keys to rotate. Configuration lives in local profiles on the machine. Create named profiles, customize defaults and spend limits per project, and switch between them instantly, with nothing held on a remote server.
+
+
+
+
+ Security
+ 03
+
+
Vetted nodes, encrypted channels
+
Every node earns its place. Before serving any traffic a node clears a performance benchmark, and a heartbeat every five minutes keeps only healthy, responsive nodes in rotation. Traffic between the runtime and each node travels over encrypted channels, so data in transit stays protected across every hop.
+
+
+
+
+
+ Built for JavaScript & TypeScript runtimes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Built for speed · shipped via the CLI
+
One command. You're on the network.
+
+
+ $
+ npm i -g @canister-software/consensus-cli
+
+
Forward HTTP/TCP traffic for tunnels opened through your node and earn for the bytes you carry.
+
+
+
EARN 02
+
Serve the proxy
+
Execute deduplicated HTTP for the network and collect the per-request fee on every call you handle.
+
+
+
EARN 03
+
Lease your IP
+
Offer a verified, long-lived IP into the shared pool and earn when apps route through it for whitelisting.
+
+
+
EARN 04
+
Serve WebSockets
+
Host prepaid, metered sessions. Clients pay up front; you earn for the time and data you serve.
+
+
+
+
+
+
+
+
The bar to join
+
Every node is vetted, to join and to stay.
+
Consensus enforces quality rather than assuming it. A machine clears a baseline of cores, memory, and storage, then passes a live stress test to be admitted. The vetting never stops: the network tracks availability and performance, and re-tests nodes during their downtime.
How fast the machine encrypts and signs the requests it serves.
+
+
+
60s @ 16 KB
+
Sustained speed
+
Holds a steady rate through a full minute of load, without throttling.
+
+
+
p99 < 10 ms
+
Responsiveness
+
Picks up new work without stalls, even on a busy host.
+
+
+
< 2%
+
Packet loss
+
Keeps connections stable for live, metered sessions.
+
+
+
Admission is not the finish line: a heartbeat every 5 minutes proves availability, and the network re-tests nodes during their downtime.
+
+
+
+
+
+
+
From install to earning
+
Three steps to a live node.
+
+
+
+
01
+
+
Clone and install
+
Pull the node runtime and install its dependencies with Bun.
+
+
+ $ git clone https://github.com/Demali-876/consensus-node.git
+$ cd consensus-node
+$ bun install
+
+
+
+
02
+
+
Run guided setup
+
One command installs the approved release, runs the live benchmark, verifies email, collects payout addresses, and registers. A domain is issued on pass.
+
Prefer a browser? bun run setup:wizard runs the same flow on a local page.
+
+
+ $ bun run setup
+
+
+
+
03
+
+
Keep it running
+
Start the always-on control tunnel under pm2 and save it, so the node survives reboots and stays in the active set.