// Cross-peer attachment fetch: upload to one daemon, read from another.
// Proves OverlayProtocolV2 transports type:'local' chunks (issue #18 verify #3).
//
// Run on tabertron — uses local pear-git-v3 daemon (port 5005, prefix /git).
// Reads back via stortron over its public HTTPS endpoint.
import http from 'http'
import https from 'https'
import b4a from 'b4a'
import crypto from 'hypercore-crypto'
import { signTestEnvelope } from '../lib/auth/envelope.js'
const TABERTRON = 'http://127.0.0.1:5005/git'
const STORTRON = 'https://7rnx.net/git'
function reqRaw (mod, opts, body) {
return new Promise((resolve, reject) => {
const req = mod.request(opts, (res) => {
const parts = []
res.on('data', c => parts.push(c))
res.on('end', () => resolve({
status: res.statusCode,
body: b4a.concat(parts),
setCookie: res.headers['set-cookie']
}))
res.on('error', reject)
})
req.on('error', reject)
if (body) req.write(body)
req.end()
})
}
async function postJson (urlStr, body, headers = {}) {
const u = new URL(urlStr)
const json = JSON.stringify(body || {})
const mod = u.protocol === 'https:' ? https : http
const port = u.port || (u.protocol === 'https:' ? 443 : 80)
const res = await reqRaw(mod, {
method: 'POST', hostname: u.hostname, port, path: u.pathname,
headers: { 'Content-Type': 'application/json', 'Content-Length': b4a.byteLength(json), ...headers }
}, json)
let parsed; try { parsed = JSON.parse(res.body.toString('utf8')) } catch { parsed = res.body.toString('utf8') }
return { status: res.status, body: parsed, setCookie: res.setCookie }
}
async function loginHttp (base) {
const { an } = await import('anproto')
const kp = await an.gen()
const env = await signTestEnvelope({ keypair: kp, method: 'whoami', params: {} })
const res = await postJson(base + '/api/rpc', env)
if (!res.body.ok) throw new Error('login failed: ' + JSON.stringify(res.body))
const setCookie = res.setCookie
return Array.isArray(setCookie) ? setCookie[0].split(';')[0] : setCookie.split(';')[0]
}
async function uploadBytes (base, bytes, cookie) {
const u = new URL(base + '/api/upload')
const mod = u.protocol === 'https:' ? https : http
const port = u.port || (u.protocol === 'https:' ? 443 : 80)
const res = await reqRaw(mod, {
method: 'POST', hostname: u.hostname, port, path: u.pathname,
headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': bytes.length, Cookie: cookie }
}, bytes)
let parsed; try { parsed = JSON.parse(res.body.toString('utf8')) } catch { parsed = res.body.toString('utf8') }
return { status: res.status, body: parsed }
}
async function fetchFile (base, path) {
const u = new URL(base + '/files/raw?path=' + encodeURIComponent(path))
const mod = u.protocol === 'https:' ? https : http
const port = u.port || (u.protocol === 'https:' ? 443 : 80)
return reqRaw(mod, {
method: 'GET', hostname: u.hostname, port, path: u.pathname + u.search
})
}
console.log('[1] login to tabertron (HTTP, local)')
const tabCookie = await loginHttp(TABERTRON)
console.log(' ok')
console.log('[2] upload random 1 MB to tabertron (multi-chunk)')
const data = crypto.randomBytes(1024 * 1024)
const upload = await uploadBytes(TABERTRON, data, tabCookie)
console.log(' status=' + upload.status + ' hash=' + upload.body.hash + ' size=' + upload.body.size)
console.log(' path=' + upload.body.path)
console.log('[3] fetch from tabertron itself (sanity)')
const tabRead = await fetchFile(TABERTRON, upload.body.path)
console.log(' status=' + tabRead.status + ' size=' + tabRead.body.length + ' byte-equal=' + tabRead.body.equals(data))
console.log('[4] fetch from stortron via public HTTPS — first hit may need peer transport time')
const t0 = Date.now()
const storRead = await fetchFile(STORTRON, upload.body.path)
const elapsed = Date.now() - t0
console.log(' status=' + storRead.status + ' size=' + storRead.body.length + ' byte-equal=' + storRead.body.equals(data) + ' elapsed=' + elapsed + 'ms')
if (storRead.status !== 200 || !storRead.body.equals(data)) {
console.error('FAIL: cross-peer attachment fetch broken')
process.exit(1)
}
console.log('PASS: tabertron→stortron attachment cross-peer fetch works')