/** * Offline integration test for the Router brain path. Spins a `Bun.serve` * mock of the Router's OpenAI-compatible + auth endpoints on an ephemeral port * and drives: * 1. the real headless SIWE → mk- → sk- handshake (router-auth.ts), with the * mock verifying the EIP-191 signature against the SIWE message address; * 2. a real OGComputeBrain wired with a RouterTransport doing infer() against * the mock /chat/completions, asserting the MiniMax-M3 inline * block is stripped from the visible answer; * 3. a tampered signature being rejected at signin. * * This doubles the HTTP TRANSPORT layer (a fake gateway), NOT 0G Compute * itself, so it stays inside the repo rule against mocking 0G Compute in * committed tests. No network egress. */ import { afterAll, beforeAll, describe, expect, it } from 'bun:test' import { recoverMessageAddress } from 'viem' import { privateKeyToAccount } from 'viem/accounts' import type { CompletionTransport } from './completion-transport' import { OGComputeBrain } from './og-compute' import { RouterAuthError, headlessMint } from './router-auth' import { RouterTransport, __clearRouterMintMemo, buildRouterTransport } from './router-transport' const AGENT_PK = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d' as const const CHAIN_ID = 16661 const NO_PREFIX = { systemPrompt: 'test', memoryIndexText: null, identityText: null, personaText: null, skillIndexText: null, toolGuidance: [], appendText: null, envText: null, timestamp: null, } let server: ReturnType let baseUrl: string const seenAuth: { mk?: string; sk?: string } = {} let nonceHits = 0 beforeAll(() => { server = Bun.serve({ port: 0, async fetch(req) { const url = new URL(req.url) const path = url.pathname const auth = req.headers.get('authorization') ?? '' const json = (obj: unknown, status = 200) => new Response(JSON.stringify(obj), { status, headers: { 'content-type': 'application/json' }, }) if (path === '/v1/auth/nonce') { nonceHits += 1 // Alphanumeric, >=8 chars, matches the real Router (hex nonce) and // passes viem's EIP-4361 nonce validation. return json({ nonce: 'a1b2c3d4e5f60718', expires_at: '2099-01-01T00:00:00Z' }) } if (path === '/v1/auth/signin') { const { message, signature } = (await req.json()) as { message: string; signature: string } const claimed = message.match(/^(0x[a-fA-F0-9]{40})$/m)?.[1] let recovered: string | null = null try { recovered = await recoverMessageAddress({ message, signature: signature as `0x${string}`, }) } catch { recovered = null } if (!claimed || !recovered || claimed.toLowerCase() !== recovered.toLowerCase()) { return json( { error: { message: 'signature does not match address', code: 'bad_sig' } }, 401, ) } return json({ token: 'jwt-wallet-token', wallet_address: claimed, expires_at: '2099-01-01T00:00:00Z', }) } if (path === '/v1/management-keys') { if (auth !== 'Bearer jwt-wallet-token') return json({ error: { message: 'bad jwt' } }, 401) const body = (await req.json()) as { name?: string; scopes?: string[] } if (!body.name || !Array.isArray(body.scopes) || body.scopes.length === 0) { return json( { error: { message: 'name + non-empty scopes required', code: 'invalid_scope' } }, 400, ) } seenAuth.mk = 'mk-mock-management' return json({ key: seenAuth.mk, key_id: 'kid-mk', scopes: body.scopes }) } if (path === '/v1/api-keys') { if (auth !== `Bearer ${seenAuth.mk}`) return json({ error: { message: 'bad mk' } }, 401) const body = (await req.json()) as { name?: string } if (!body.name) return json({ error: { message: 'name required', code: 'invalid_name' } }, 400) seenAuth.sk = 'sk-mock-inference' return json({ key: seenAuth.sk, key_id: 'kid-sk' }) } if (path === '/v1/models') { return json({ data: [{ id: 'minimax-m3' }, { id: 'glm-5' }] }) } if (path === '/v1/chat/completions') { if (auth !== `Bearer ${seenAuth.sk}`) { return json( { error: { message: 'insufficient_balance', code: 'insufficient_balance' } }, 402, ) } // MiniMax-M3 shape: chain-of-thought INLINE in content as .... return json({ base_resp: { status_code: 0, status_msg: '' }, choices: [ { finish_reason: 'stop', index: 0, message: { role: 'assistant', name: 'MiniMax AI', content: 'The user said hi.Hello there!', }, }, ], model: 'minimax-m3', object: 'chat.completion', usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }) } return json({ error: { message: `unhandled ${path}` } }, 404) }, }) baseUrl = `http://localhost:${server.port}/v1` }) afterAll(() => { server.stop(true) }) describe('Router headless auth handshake (Bun.serve mock)', () => { it('mints sk- end-to-end via SIWE → signin → mk- → sk-', async () => { const { sk, mk, jwt } = await headlessMint({ baseUrl, privkeyHex: AGENT_PK, chainId: CHAIN_ID }) expect(jwt).toBe('jwt-wallet-token') expect(mk).toBe('mk-mock-management') expect(sk).toBe('sk-mock-inference') }) it('rejects a tampered signature at signin', async () => { // Sign with a DIFFERENT key than the address claimed → recover mismatch. const wrong = privateKeyToAccount( '0x4c0883a69102937d6231471b5dbb6204fe512961708279f2e3e8a5d4b8e5e5e5', ) const { buildAndSignSiwe, signin, requestNonce } = await import('./router-auth') const nonce = await requestNonce(baseUrl) // Build a message that claims `wrong`'s address but sign it with `wrong`, // that is valid. To tamper, sign a message for one address with another // key: build for AGENT_PK's address, sign with `wrong`. const agent = privateKeyToAccount(AGENT_PK) const { createSiweMessage } = await import('viem/siwe') const message = createSiweMessage({ address: agent.address, chainId: CHAIN_ID, domain: 'pc.0g.ai', uri: 'https://pc.0g.ai', version: '1', nonce, statement: 'Sign in to 0G Private Computer', }) const signature = await wrong.signMessage({ message }) await expect(signin(baseUrl, message, signature)).rejects.toBeInstanceOf(RouterAuthError) // buildAndSignSiwe is exercised in the happy path; reference it so the // import is not flagged unused. expect(typeof buildAndSignSiwe).toBe('function') }) }) describe('Router brain infer() over the mock (inline strip)', () => { it('strips the MiniMax-M3 inline block from the visible answer', async () => { const transport = new RouterTransport({ modelId: 'minimax-m3', baseUrl, mintApiKey: async () => (await headlessMint({ baseUrl, privkeyHex: AGENT_PK, chainId: CHAIN_ID })).sk, }) const brain = new OGComputeBrain({ privkeyHex: AGENT_PK, rpcUrl: 'https://does-not-matter', providerAddress: '0x0000000000000000000000000000000000000000', tools: [], prefix: NO_PREFIX, transport, }) const turn = await brain.infer({ event: { id: 't1', source: 'stdin', payload: { label: 'user-message', data: 'hi' }, ts: 1 }, }) expect(turn.content).toBe('Hello there!') expect(turn.toolCalls).toEqual([]) }) it('lists Router models from the public catalog', async () => { const transport = new RouterTransport({ modelId: 'minimax-m3', baseUrl, apiKey: 'sk-x' }) const services = await transport.listServices() expect(services.map(s => s.model).sort()).toEqual(['glm-5', 'minimax-m3']) }) }) describe('brain retries once on 401 via transport.reprovision (auth re-mint)', () => { it('re-mints the sk- and succeeds after an initial 401', async () => { __clearRouterMintMemo() let apiKeyN = 0 const srv = Bun.serve({ port: 0, async fetch(req) { const url = new URL(req.url) const auth = req.headers.get('authorization') ?? '' const j = (o: unknown, s = 200) => new Response(JSON.stringify(o), { status: s, headers: { 'content-type': 'application/json' }, }) if (url.pathname === '/v1/auth/nonce') return j({ nonce: 'a1b2c3d4e5f60718' }) if (url.pathname === '/v1/auth/signin') { const { message, signature } = (await req.json()) as { message: string signature: string } const claimed = message.match(/^(0x[a-fA-F0-9]{40})$/m)?.[1] const rec = await recoverMessageAddress({ message, signature: signature as `0x${string}`, }).catch(() => null) if (!claimed || !rec || claimed.toLowerCase() !== rec.toLowerCase()) return j({ error: { message: 'bad' } }, 401) return j({ token: 'jwt-x' }) } if (url.pathname === '/v1/management-keys') return j({ key: 'mk-x' }) if (url.pathname === '/v1/api-keys') return j({ key: `sk-mint-${++apiKeyN}` }) if (url.pathname === '/v1/chat/completions') { // First minted key (sk-mint-1) is rejected; the re-minted key works. if (auth !== 'Bearer sk-mint-2') return j({ error: { message: 'expired' } }, 401) return j({ choices: [ { finish_reason: 'stop', message: { role: 'assistant', content: 'recovered' } }, ], usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, }) } return j({ error: { message: 'nf' } }, 404) }, }) try { const base = `http://localhost:${srv.port}/v1` const transport = buildRouterTransport({ network: '0g-mainnet', privkeyHex: AGENT_PK, model: 'minimax-m3', router: { baseUrl: base }, }) const brain = new OGComputeBrain({ privkeyHex: AGENT_PK, rpcUrl: 'https://does-not-matter', providerAddress: '', tools: [], prefix: NO_PREFIX, transport, }) const turn = await brain.infer({ event: { id: 'r1', source: 'stdin', payload: { label: 'user-message', data: 'hi' }, ts: 1 }, }) expect(turn.content).toBe('recovered') expect(apiKeyN).toBe(2) // minted twice: initial + reprovision } finally { srv.stop(true) } }) }) describe('Direct path does NOT strip inline (stripInlineThink falsy)', () => { it('returns content verbatim including a literal block', async () => { const srv = Bun.serve({ port: 0, fetch: () => new Response( JSON.stringify({ choices: [ { finish_reason: 'stop', message: { role: 'assistant', content: 'xkept' }, }, ], usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, }), { status: 200, headers: { 'content-type': 'application/json' } }, ), }) try { const base = `http://localhost:${srv.port}/v1` // A minimal Direct-like transport: stripInlineThink is OMITTED (falsy). const directLike: CompletionTransport = { init: async () => {}, baseUrl: base, modelId: 'qwen3.6-plus', authHeaders: async () => ({}), mapHttpError: () => null, getLedger: () => null, getLedgerBalance: async () => null, listServices: async () => [], } const brain = new OGComputeBrain({ privkeyHex: AGENT_PK, rpcUrl: 'https://does-not-matter', providerAddress: '0x0000000000000000000000000000000000000000', tools: [], prefix: NO_PREFIX, transport: directLike, }) const turn = await brain.infer({ event: { id: 'd1', source: 'stdin', payload: { label: 'user-message', data: 'hi' }, ts: 1 }, }) // The literal block is preserved (NOT stripped) for a non-Router transport. expect(turn.content).toBe('xkept') } finally { srv.stop(true) } }) }) describe('buildRouterTransport process mint memo', () => { it('mints the sk- ONCE across the main brain + a delegate sub-brain', async () => { __clearRouterMintMemo() const before = nonceHits // Two transports for the SAME agent privkey + baseUrl (main + delegate). const main = buildRouterTransport({ network: '0g-mainnet', privkeyHex: AGENT_PK, model: 'minimax-m3', router: { baseUrl }, }) const delegate = buildRouterTransport({ network: '0g-mainnet', privkeyHex: AGENT_PK, model: 'minimax-m3', router: { baseUrl }, }) await Promise.all([main.init(), delegate.init()]) // Exactly one SIWE handshake (one nonce fetch) despite two transports. expect(nonceHits - before).toBe(1) expect(await main.authHeaders('x')).toEqual({ Authorization: 'Bearer sk-mock-inference' }) expect(await delegate.authHeaders('x')).toEqual({ Authorization: 'Bearer sk-mock-inference' }) }) })