~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-socialweb-computer-requester

socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons

0
0
main
107 lines · 7.4 KB Raw History
// Live Phase B round: the requester page's WebrtcCubit (req/web/webrtc) in headless Chrome, as an
// app-password session (no OAuth popup in headless), against one bidder over its offering's webrtc
// endpoint: contract (RFP, accept, event over WebRTC), a VM shell over the guest's datachannel, release,
// then every record it wrote checked gone. Never prints the password or tokens.
//   ALICE_ATPROTO_HANDLE=.. ALICE_ATPROTO_PASSWORD=.. node round.mjs <bidder DID> [command]
import { browse } from "./browser.mjs";

const ROOT = process.env.PAGE_ROOT ?? new URL("../web/webrtc/", import.meta.url).pathname;
const [bidder, command = "uname -a; id"] = process.argv.slice(2);
const handle = process.env.ALICE_ATPROTO_HANDLE;
const password = process.env.ALICE_ATPROTO_PASSWORD;
if (!bidder?.startsWith("did:") || !handle || !password) throw new Error("usage: ALICE_ATPROTO_HANDLE ALICE_ATPROTO_PASSWORD node round.mjs <bidder DID> [command]");
const log = (...a) => console.log(new Date().toISOString(), ...a);

const login = await (await fetch("https://bsky.social/xrpc/com.atproto.server.createSession", {
	method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ identifier: handle, password }),
})).json();
if (!login.accessJwt) throw new Error(`createSession failed: ${login.error ?? "?"}`);
const pds = login.didDoc?.service?.find((s) => s.id === "#atproto_pds")?.serviceEndpoint ?? "https://bsky.social";
log("signed in", login.did, "pds", pds);

const index = (await import("node:fs")).readFileSync(`${ROOT}index.html`, "utf8")
	.replace(/<script type="module" src="\.\/[^"]+"><\/script>/g, "")
	.replace("</head>", `<script type="module">
import { secp256k1 } from '@noble/curves/secp256k1.js'
import * as age from 'age-encryption'
import * as devTunnelsSsh from '@microsoft/dev-tunnels-ssh'
import { Buffer } from 'buffer'
import { WebrtcCubit } from './webrtc-cubit.js'
import { createPluginContext } from 'atlibs/plugin-context/index.js'
import { localModule, runModule } from 'atlibs/run-module/index.js'
globalThis.start = async ({ did, handle, pds, jwt }) => {
	const wire = globalThis.wire = { offers: 0, writes: [], https: [] };
	const realFetch = globalThis.fetch;
	wire.reads = [];
	globalThis.fetch = (url, init) => { const u = String(url?.url ?? url); if (/\\/xrpc\\/com\\.publicdomainrelay\\.temp\\.market\\.submit/.test(u)) wire.https.push(u);
		const p = realFetch(url, init); if (/getRecord/.test(u)) p.then((r) => wire.reads.push([r.status, decodeURIComponent(u).replace(/^https:\\/\\/([^/]+).*collection=([^&]+)&rkey=([^&]{0,30}).*$/, '$1 $2 $3')]), (e) => wire.reads.push(['ERR ' + e.message, u.slice(0, 120)])); return p };
	const agent = {
		did,
		com: { atproto: { server: { getSession: async () => ({ success: true, data: { handle } }) } } },
		fetchHandler: async (path, init = {}) => {
			if (/createRecord|deleteRecord/.test(path) && init.body) {
				const b = JSON.parse(init.body);
				wire.writes.push([path.includes('create') ? 'create' : 'delete', b.collection, b.rkey ?? null]);
				if (b.collection === 'computer.socialweb.ingress.offer' && path.includes('create')) wire.offers++;
			}
			return realFetch(pds + path, { ...init, headers: { ...init.headers, authorization: 'Bearer ' + jwt } });
		},
	};
	const ctx = createPluginContext({ id: 'webrtc-live', storage: { managed_records: {} }, settingsSchema: { attestKeys: { type: 'secret' } } });
	const MODULES = new URL('./modules/', location.href);
	globalThis.cubit = new WebrtcCubit({
		client: Promise.resolve({ init: async () => ({ session: {} }), revoke: async () => {} }),
		makeAgent: () => agent, secp256k1, age, makePeer: (config) => new RTCPeerConnection(config), ssh: { ...devTunnelsSsh, Buffer },
		module: localModule(new URL('requester.wasm', MODULES), new URL('requester.json', MODULES)), ctx,
	});
	await globalThis.cubit.init();
	globalThis.dbg = { localModule, runModule, ctx, MODULES };
	return globalThis.cubit.state.session;
};
</script></head>`);

const out = await browse(async ({ open, evaluate, on }) => {
	on("Runtime.consoleAPICalled", (p) => log("page:", p.args.map((a) => a.value ?? a.description).join(" ").slice(0, 400)));
	await open("/");
	await new Promise((r) => setTimeout(r, 3000));
	log("session", await evaluate(`start(${JSON.stringify({ did: login.did, handle: login.handle, pds, jwt: login.accessJwt })})`));
	if (process.env.POLICY) {
		const input = { selfDid: login.did, perspective: "requester", mode: "scope", counterpartyDid: bidder, subjectDid: bidder, rootRequesterDid: login.did };
		log("policy", JSON.stringify(await evaluate(`(async () => { const m = await dbg.localModule(new URL('policy-requester-tangled-vouch.wasm', dbg.MODULES), new URL('policy-requester-tangled-vouch.json', dbg.MODULES)); const r = await dbg.runModule(dbg.ctx, m, 'evaluate', ${JSON.stringify(input)}, cubit.call); return { result: r.result, error: r.error, log: r.log?.slice?.(-5) } })()`)).slice(0, 1500));
		return { seconds: 0, ran: { error: "policy only" }, wire: await evaluate("wire") };
	}
	const t0 = Date.now();
	const input = { bidders: [bidder], webrtc_ssh: true, iroh: false, via: "webrtc", tag: "ssh", command, poll_ms: 3000 };
	// this profile's fresh attestation key: bound to the account for the round, its binding deleted after
	const bound = await evaluate(`cubit.run("bind_key", {}).then((r) => ({ ok: r }), (e) => ({ error: e.message }))`);
	log("bind_key", JSON.stringify(bound).slice(0, 300));
	const ran = await evaluate(`cubit.sshKey().then((ssh_key) => cubit.run("contract", { ...${JSON.stringify(input)}, ssh_key })).then((r) => ({ ok: r }), (e) => ({ error: e.message }))`);
	const wire = await evaluate("wire");
	const binding = JSON.stringify(bound.ok ?? {}).match(/at:\/\/[^"]*badgeBlueKeys\/[^"]+/)?.[0];
	if (binding) {
		const [, , , collection, rkey] = binding.split("/");
		log("unbind", binding.split("/").pop(), await evaluate(`cubit.call("com.atproto.repo.deleteRecord", { input: { repo: cubit.state.did, collection: "${collection}", rkey: "${rkey}" } }).then(() => "deleted", (e) => e.message)`));
	}
	return { seconds: Math.round((Date.now() - t0) / 1000), ran, wire };
}, { root: ROOT, index });

const r = out.ran.ok ?? {};
log("contract", out.ran.error ? `ERROR ${out.ran.error}` : "ok", `${out.seconds}s`);
log("winner", JSON.stringify(r.winner ?? null)?.slice(0, 200));
log("receipt", r.receipt?.uri);
log("shell", JSON.stringify(r.shell ?? null)?.slice(0, 400));
log("release", JSON.stringify(r.release ?? null)?.slice(0, 300));
log("errors", JSON.stringify(r.errors ?? []));
log("offers written (computer.socialweb.ingress.offer)", out.wire.offers, "https submit* calls", out.wire.https.length);
log("writes", JSON.stringify(out.wire.writes));
if (process.env.READS) for (const r of out.wire.reads) log("read", JSON.stringify(r).slice(0, 220));
// every record the run created is gone
const gone = [];
for (const uri of r.deleted ?? []) {
	const [repo, collection, rkey] = uri.replace("at://", "").split("/");
	const res = await fetch(`${pds}/xrpc/com.atproto.repo.getRecord?${new URLSearchParams({ repo, collection, rkey })}`);
	gone.push([collection.split(".").slice(-2).join("."), res.status]);
}
log("deleted, now", JSON.stringify(gone));
const offersLeft = await (await fetch(`${pds}/xrpc/com.atproto.repo.listRecords?${new URLSearchParams({ repo: login.did, collection: "computer.socialweb.ingress.offer", limit: "10" })}`)).json();
log("ingress.offer records left (verification read only)", offersLeft.records?.length ?? offersLeft.error);