// Reverse-proxy public base URL (api.youraitools.co/<prefix>): see src/publicbase.js.
import test from 'node:test';
import assert from 'node:assert/strict';
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm';
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
import { forwardedBase, publicBaseHandler, rewriteHtml } from '../src/publicbase.js';
import { createApp } from '../src/app.js';
import { fakeWeb, PAGE } from './helpers.js';
const WORKERS_DEV = 'https://webread.agentgate.workers.dev'; const PREFIX = '/webread';
const PAID_PATH = '/v1/read'; const PAID = '/v1/read?url=https://site.example.org/page'; const AMOUNT = '2000';
const appOpts = async () => ({ fetchImpl: fakeWeb({ 'https://site.example.org/robots.txt': { body: 'User-agent: *\\nAllow: /', headers: { 'content-type': 'text/plain' } }, 'https://site.example.org/page': { body: PAGE } }).fetchImpl, dns: false, payTo: '0x3c1A1E01e790Cd326495631B3b38Bd7068F08d6F', network: 'eip155:8453', price: '$0.002' });
const NETWORK = 'eip155:8453';
const ENV = { PUBLIC_URL: WORKERS_DEV, PROXY_HOSTS: 'api.youraitools.co', PROXY_PREFIX: PREFIX };
const PROXY_BASE = `https://api.youraitools.co${PREFIX}`;
const FWD = { 'x-forwarded-host': 'api.youraitools.co', 'x-forwarded-prefix': PREFIX, 'x-forwarded-proto': 'https' };
const decode = (h) => JSON.parse(Buffer.from(h, 'base64').toString('utf8'));
const req = (path, headers = {}, init = {}) => new Request(`${WORKERS_DEV}${path}`, { ...init, headers: { ...headers, ...(init.headers || {}) } });
function facilitator() {
const f = { verified: [], settled: [],
async getSupported() { return { kinds: [{ x402Version: 2, scheme: 'exact', network: NETWORK }], extensions: ['bazaar'], signers: {} }; },
async verify(payload, r) { f.verified.push({ payload, r }); const a = payload?.payload?.authorization; return a && a.to.toLowerCase() === r.payTo.toLowerCase() && String(a.value) === String(r.amount) ? { isValid: true, payer: a.from } : { isValid: false, invalidReason: 'fake_invalid' }; },
async settle(payload, r) { f.settled.push({ payload, r }); return { success: true, transaction: '0x' + 'ab'.repeat(32), network: r.network, payer: payload.payload.authorization.from }; },
};
return f;
}
async function handler() {
const fac = facilitator(); const opts = await appOpts(); const made = [];
const h = publicBaseHandler(ENV, (publicUrl) => { made.push(publicUrl); return createApp({ ...opts, facilitatorClient: fac, publicUrl }); });
return { h, fac, made };
}
test('forwardedBase: only the allowlisted host with this Worker\'s prefix is honoured', () => {
const r = (h) => new Request(`${WORKERS_DEV}/`, { headers: h });
assert.deepEqual(forwardedBase(r(FWD), ENV), { host: 'api.youraitools.co', prefix: PREFIX, base: PROXY_BASE });
assert.equal(forwardedBase(r({ ...FWD, 'x-forwarded-host': 'API.YourAITools.co' }), ENV).base, PROXY_BASE);
assert.equal(forwardedBase(r({}), ENV), null);
assert.equal(forwardedBase(r({ ...FWD, 'x-forwarded-host': 'evil.example' }), ENV), null);
assert.equal(forwardedBase(r({ ...FWD, 'x-forwarded-host': 'api.youraitools.co.evil.example' }), ENV), null);
assert.equal(forwardedBase(r({ ...FWD, 'x-forwarded-prefix': '/other' }), ENV), null);
assert.equal(forwardedBase(r({ ...FWD, 'x-forwarded-prefix': '' }), ENV), null);
assert.equal(forwardedBase(r({ ...FWD, 'x-forwarded-prefix': '/x/../../evil' }), ENV), null);
assert.equal(forwardedBase(r(FWD), { ...ENV, PROXY_PREFIX: undefined }), null);
});
test('rewriteHtml: root-relative links and sister-tool links get the proxy prefix, absolute links stay', () => {
const out = rewriteHtml('<a href="/health">h</a><a href="//cdn.x/y">c</a><a href="https://x.example/">x</a><script>fetch(\'/v1/x\')</script><a href="https://webread.agentgate.workers.dev">w</a>', { host: 'api.youraitools.co', prefix: '/p' });
assert.match(out, /href="\/p\/health"/); assert.match(out, /href="\/\/cdn\.x\/y"/); assert.match(out, /href="https:\/\/x\.example\/"/);
assert.match(out, /fetch\('\/p\/v1\/x'\)/); assert.match(out, /href="https:\/\/api\.youraitools\.co\/webread"/);
});
test('direct workers.dev request is unchanged: 402 resource (v2 header and v1 body) on workers.dev', async () => {
const { h } = await handler();
const r = await h(req(PAID), ENV);
assert.equal(r.status, 402);
assert.equal(decode(r.headers.get('PAYMENT-REQUIRED')).resource.url, `${WORKERS_DEV}${PAID_PATH}`);
assert.equal((await r.json()).accepts[0].resource, `${WORKERS_DEV}${PAID_PATH}`);
});
test('spoofed X-Forwarded-Host is ignored (workers.dev URLs)', async () => {
const { h } = await handler();
const r = await h(req(PAID, { ...FWD, 'x-forwarded-host': 'evil.example' }), ENV);
assert.equal(decode(r.headers.get('PAYMENT-REQUIRED')).resource.url, `${WORKERS_DEV}${PAID_PATH}`);
});
test('proxied request: 402 resource, openapi servers, .well-known and landing use api.youraitools.co/<prefix>', async () => {
const { h, made } = await handler();
const r = await h(req(PAID, FWD), ENV);
assert.equal(r.status, 402);
const pr = decode(r.headers.get('PAYMENT-REQUIRED'));
assert.equal(pr.resource.url, `${PROXY_BASE}${PAID_PATH}`);
assert.equal(pr.accepts[0].amount, AMOUNT);
assert.equal((await r.json()).accepts[0].resource, `${PROXY_BASE}${PAID_PATH}`);
const oa = await (await h(req('/openapi.json', FWD), ENV)).json();
assert.equal(oa.servers[0].url, PROXY_BASE);
assert.ok(!JSON.stringify(oa).includes('agentgate.workers.dev'), 'no workers.dev URL in proxied openapi');
const wk = await (await h(req('/.well-known/x402', FWD), ENV)).json();
assert.ok(wk.resources.length && wk.resources.every((u) => u.startsWith(`${PROXY_BASE}/`)), JSON.stringify(wk.resources));
const html = await (await h(req('/', { ...FWD, accept: 'text/html' }), ENV)).text();
assert.ok(!new RegExp(`(href|src)="/(?!/)(?!${PREFIX.slice(1)}/)`).test(html), 'every root-relative link carries the prefix');
assert.ok(html.includes(PROXY_BASE));
// workers.dev answers are untouched by a proxied request in the same isolate
const oa2 = await (await h(req('/openapi.json'), ENV)).json();
assert.equal(oa2.servers[0].url, WORKERS_DEV);
assert.deepEqual(made.sort(), [PROXY_BASE, WORKERS_DEV].sort(), 'exactly one app per public base');
});
test('proxied paid call (official @x402/fetch v2 client): the signed resource is the proxied URL, verified and settled', async () => {
const { h, fac } = await handler();
const viaProxy = (input, init) => { const r0 = input instanceof Request ? input : new Request(input, init); const r1 = new Request(r0); for (const [k, v] of Object.entries(FWD)) r1.headers.set(k, v); return h(r1, ENV); };
const pay = wrapFetchWithPaymentFromConfig(viaProxy, { schemes: [{ network: NETWORK, client: new ExactEvmScheme(privateKeyToAccount(generatePrivateKey())) }] });
const r = await pay(`${WORKERS_DEV}${PAID}`);
assert.equal(r.status, 200, await r.clone().text());
assert.ok(r.headers.get('PAYMENT-RESPONSE'));
assert.equal(fac.verified.length, 1); assert.equal(fac.settled.length, 1);
assert.equal(fac.verified[0].payload.resource.url, `${PROXY_BASE}${PAID_PATH}`);
assert.equal(fac.settled[0].payload.resource.url, `${PROXY_BASE}${PAID_PATH}`);
});
test('proxied MCP tools/list works', async () => {
const { h } = await handler();
const r = await h(req('/mcp', { ...FWD, 'content-type': 'application/json', accept: 'application/json, text/event-stream' }, { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {} }) }), ENV);
assert.equal(r.status, 200);
const t = await r.text();
const body = JSON.parse(t.startsWith('{') ? t : t.split('\n').find((l) => l.startsWith('data:')).slice(5));
assert.ok(body.result.tools.length >= 1);
});
agentgate-source: code mirror on Cloudflare Pages. All repos