Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs by Sentinel-Bluebuilder · Pull Request #16 · Sentinel-Bluebuilder/x402 · GitHub
Skip to content

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs - #16

Open
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata
Open

Resolve tester feedback: live /nodes metadata, protocol selection, +1d grace docs#16
Sentinel-Bluebuilder wants to merge 1 commit into
masterfrom
fix/tester-feedback-protocol-nodes-metadata

Conversation

@Sentinel-Bluebuilder

Copy link
Copy Markdown
Owner

Resolves the issues + recommendations from the x402 Sentinel dVPN end-to-end test report (2026-06-22, RESULT: PASS).

Issue #1/nodes returned empty metadata in production

The live endpoint reported online: 0, protocol: null for all 726 nodes, and byCountry: {}. Traced to 4 compounding bugs in the node status probe (server/src/sentinel.ts):

BugFix
new URL("host:port") threw / misparsed bare addressesnew normalizeRemoteAddrToUrl() helper (mirrors the SDK's resolveNodeUrl)
probed /status → 404 on v3 nodesprobe the v3 root path /
read numeric status.type for protocol (v3 uses a string)read service_type string
read status.max_peersread qos.max_peers

Now probes allremote_addrs (first reachable wins) and surfaces real address, online, protocol, country, city, moniker, peers, maxPeers, version, lastSeen per node, with byCountryandbyProtocol summaries.

Issue #2 — protocol mismatch (v2ray instructions on a WireGuard node)

Implemented protocol selection end-to-end:

  • request body protocol: "v2ray" | "wireguard" pins the tunnel type
  • pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no USDC is spent on a guaranteed-fail request
  • node matching honors the requested protocol
  • 200 response returns the node's actualnodeProtocol + full node metadata
  • instructions are generated from the selected node's real protocol
  • GET /nodes gains ?protocol= and ?country= filters

Issue #7 — 1-day grant granting ~48h

Confirmed intentional: the +1 day is a deliberate fee-grant grace buffer so the grant outlives the paid window (a session started at the very end can still pay gas). Now documented and surfaced as a distinct expiresAtPaid alongside expiresAt.

Docs (recs #5 / #6)

  • docs/llms.txt: added a no-root V2Ray worked example and a Linux WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0", "::/0"] workaround.
  • docs/index.html: mirrored the new request/response fields and /nodes filters in the JSON-LD spec.

Not in this PR

  • Issue WIP #3 / rec WIP #4 (blue-js-sdk duplicate WireGuard interface on Linux full-tunnel) is an SDK-side fix and lands as a separate PR against blue-js-sdk.

⚠️ Deploy note

The /manifest + provisioning changes are server code and require an operator Docker redeploy to take effect on x402.sentinel.co. The docs/ changes go live on the next GitHub Pages publish.

…d grace docs
Addresses the x402 dVPN end-to-end test report (2026-06-22).
Issue #1 — /nodes returned empty metadata in production (online: 0,
protocol: null, byCountry: {}). Root cause was 4 compounding bugs in the
node status probe (server/src/sentinel.ts):
- new URL() on a bare host:port threw / misparsed -> normalizeRemoteAddrToUrl()
- probed /status (404) instead of the v3 root path /
- read numeric status.type instead of v3 string service_type for protocol
- read status.max_peers instead of qos.max_peers
Now probes all remote_addrs (first reachable wins) and surfaces real
address, online, protocol, country, city, moniker, peers, maxPeers,
version and lastSeen per node, with byCountry + byProtocol summaries.
Issue #2 — provisioning could instruct a protocol the selected node did
not speak (v2ray instructions on a WireGuard node). Implemented protocol
selection end-to-end:
- request body `protocol: "v2ray" | "wireguard"` pinned before payment
- pre-payment validation (INVALID_PROTOCOL / PROTOCOL_UNAVAILABLE) so no
USDC is spent on a guaranteed-fail request
- node matching honors the requested protocol
- 200 response returns the node's ACTUAL nodeProtocol + full node metadata
- instructions are generated from the selected node's real protocol
- GET /nodes gains ?protocol= and ?country= filters
Issue #7 — clarified the 1-day-grant-grants-~48h behavior: the +1 day is a
deliberate fee-grant grace buffer (the grant must outlive the paid window so
a session started at the very end can still pay gas). Documented and now
surfaced as a distinct expiresAtPaid alongside expiresAt.
Docs (recs #5/#6): added a no-root V2Ray worked example and a Linux
WireGuard full-tunnel example using the splitIPs: ["0.0.0.0/0","::/0"]
workaround in docs/llms.txt; mirrored the new request/response fields and
/nodes filters in docs/index.html JSON-LD.
NOTE: the /manifest + provisioning changes are server code and require an
operator Docker redeploy to take effect on x402.sentinel.co; the docs/
changes go live on the next GitHub Pages publish.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Sentinel-Bluebuilder