johnandersen777.bsky.social/atlibs-socialweb-computer-requester
socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons
0
0
// node --test web/socialweb-computer-ui/socialweb-cubit.test.mjs (after `cargo wasm`)
// The page cubit over the real AtircCubit + WebrtcCubit and the real atirc and requester wasm;
// the OAuth client, the session's PDS (atirc lines through it) and the bidder are stubbed.
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { registerHooks } from "node:module";
import { test } from "node:test";
import { load } from "../../../atlibs/rust/src/harness.mjs";
registerHooks({
resolve(specifier, context, next) {
// atlibs/webrtc/ is the atlibs-webrtc sibling checkout's js/; the rest of atlibs/ is core's javascript/src
const lib = specifier.startsWith("atlibs/webrtc/") ? new URL(`../../../atlibs-webrtc/js/${specifier.slice("atlibs/webrtc/".length)}`, import.meta.url).href
: specifier.startsWith("atlibs/") && new URL(`../../../atlibs/javascript/src/${specifier.slice("atlibs/".length)}`, import.meta.url).href;
return next(lib || specifier, context);
},
});
const { SocialwebCubit, channelView, classify, sharedSessions } = await import("./socialweb-cubit.js");
const { requestScope, grantedSessions, enabledFeatures } = await import("atlibs/oauth/index.js");
const { AtircCubit } = await import("../../../atlibs-agents/web/atirc/atirc-cubit.js");
const { WebrtcCubit } = await import("../webrtc/webrtc-cubit.js");
const T = "com.publicdomainrelay.temp";
const ALICE = "did:plc:lpfuqerea3deuoyrn7ojser4";
const OTHER = "did:plc:someoneelse0000000000000";
const CHANNEL = "#alice_test";
// atirc is the atlibs-agents sibling checkout's module (its own `cargo wasm`); the requester is this repo's
const repo = (name) => (name === "atirc" ? "../../../atlibs-agents/" : "../../");
const release = (name) => new URL(`${repo(name)}target/wasm32-unknown-unknown/release/${name}.wasm`, import.meta.url);
const manifest = async (name) => JSON.parse(await readFile(new URL(`${repo(name)}modules/${name}/module.json`, import.meta.url), "utf8"));
const wasm = async (name) => ({ module: new Uint8Array(await readFile(release(name))), calls: (await manifest(name)).calls });
const context = async (name, fetch) => {
const { run } = await load(release(name));
const store = new Map();
return { wasm: { run: async ({ entry, input }) => run(entry, input) }, http: { fetch }, settings: { get: async (k) => store.get(k), set: async (k, v) => store.set(k, v) } };
};
const POLICY = {
sessions: {
requester: { scope: `repo:${T}.compute.vm?action=create&action=delete rpc:${T}.market.submitRfp?aud=*` },
atirc: { scope: "rpc:chat.atirc.list?aud=did:web:irc.fedfork.com%23atirc rpc:chat.atirc.post?aud=did:web:irc.fedfork.com%23atirc" },
},
features: { agents: { requires: ["requester", "atirc"] } },
};
const line = (seq, text, { did = ALICE, nick = "bob-hat-9", identity = `${nick}.alice.test` } = {}) =>
({ seq, channel: CHANNEL, did, nick, identity, text, ts: `2026-11-08T20:00:${String(seq).padStart(2, "0")}Z`, source: "xrpc" });
// One page: the stubbed OAuth client granting `scope`, the session's PDS (repo writes,
// service-auth, chat.atirc.* behind atproto-proxy), the bidder, and the three cubits.
async function page(scope, lines = []) {
const xrpc = [];
const channel = [...lines];
const agent = {
did: ALICE,
com: { atproto: { server: { getSession: async () => ({ success: true, data: { handle: "alice.test" } }) } } },
async fetchHandler(path, init) {
const [nsid, query] = path.replace("/xrpc/", "").split("?");
const input = init.body && JSON.parse(init.body);
xrpc.push({ nsid, query, input, headers: init.headers });
if (nsid === "chat.atirc.list") {
const cursor = Number(new URLSearchParams(query).get("cursor"));
const messages = channel.filter((m) => m.seq > cursor);
return Response.json({ messages, cursor: messages.at(-1)?.seq ?? cursor });
}
if (nsid === "chat.atirc.post") {
const posted = line(channel.length ? channel.at(-1).seq + 1 : 1, input.text, { nick: "alice" });
channel.push(posted);
return Response.json(posted);
}
if (nsid === "com.atproto.server.getServiceAuth") return Response.json({ token: "svc-jwt" });
if (nsid === "com.atproto.repo.createRecord") return Response.json({ uri: `at://${ALICE}/${input.collection}/r${xrpc.length}`, cid: `bafy${xrpc.length}` });
return new Response("{}", { status: 400 });
},
};
const signIns = [];
const revoked = [];
const client = Promise.resolve({
init: async () => scope && { session: { did: ALICE, getTokenInfo: async () => ({ scope }) } },
signIn: async (identifier, options) => signIns.push({ identifier, scope: options.scope }),
revoke: async (did) => revoked.push(did),
});
const sessions = sharedSessions(client, POLICY);
const bidder = [];
const http = async (url, init) => (bidder.push({ url, init }), Response.json({ ok: true, name: "bid-1" }));
const secp256k1 = { utils: { randomSecretKey: () => new Uint8Array(32).fill(1) }, getPublicKey: () => new Uint8Array(33).fill(2), sign: () => new Uint8Array(64) };
const compute = new WebrtcCubit({ client: Promise.resolve(sessions.client("requester")), makeAgent: () => agent, secp256k1, module: wasm("requester"), ctx: await context("requester", http) });
const atirc = new AtircCubit({ client: Promise.resolve(sessions.client("atirc")), makeAgent: () => agent, module: wasm("atirc"), ctx: await context("atirc", http) });
const cubit = new SocialwebCubit({ sessions, policy: POLICY, atirc, compute });
await cubit.init();
return { cubit, atirc, compute, xrpc, channel, signIns, revoked, bidder };
}
test("policy: request scope, granted sessions, features", () => {
assert.equal(requestScope(POLICY, []), "atproto");
const all = requestScope(POLICY, ["requester", "atirc"]);
assert.deepEqual(grantedSessions(POLICY, all), ["requester", "atirc"]);
assert.deepEqual(grantedSessions(POLICY, requestScope(POLICY, ["atirc"])), ["atirc"]);
assert.deepEqual(grantedSessions(POLICY, "atproto rpc:chat.atirc.list?aud=did:web:irc.fedfork.com%23atirc"), [], "half of a session is none of it");
assert.deepEqual(enabledFeatures(POLICY, ["atirc"]), []);
assert.deepEqual(enabledFeatures(POLICY, ["atirc", "requester"]), ["agents"]);
});
test("classify and channelView: scrobbles, owner questions, agents, VM requests from your agents only", () => {
const Q = "0cd72e00-7953-42d4-8306-49c1b6102ba8";
assert.equal(classify({ text: "🚀 shipped it" }).kind, "scrobble");
assert.equal(classify({ text: "✅ done" }).kind, "scrobble");
assert.equal(classify({ text: "1. not an emoji" }).kind, "chat");
assert.equal(classify({ text: "plain line 🚀" }).kind, "chat");
assert.deepEqual(classify({ text: 'vm-request {"bidders":["did:plc:x"],"rm":"-rf"}' }), { kind: "vm-request", input: { bidders: ["did:plc:x"] } });
assert.equal(classify({ text: "vm-request {not json" }).kind, "chat");
const view = channelView([
line(1, `[ask ${Q}] push the branch?`, { nick: "atlibs" }),
line(2, "🚀 shipped atlibs ssh"),
line(3, 'vm-request {"user_data":"#cloud-config\\n"}'),
line(4, 'vm-request {"user_data":"x"}', { did: OTHER, nick: "mallory" }),
line(5, `[decided ${Q}] keep it local`, { nick: "atlibs" }),
line(6, "vm-request 3: rfp at://x", { nick: "alice" }),
line(7, 'vm-request {"bidders":["did:plc:y"]}'),
], { did: ALICE, handle: "alice.test", channel: CHANNEL });
assert.deepEqual(view.lines.map((l) => l.kind), ["ask", "scrobble", "vm-request", "vm-request", "decided", "vm-reply", "vm-request"]);
assert.deepEqual(view.questions, [{ id: Q, question: "push the branch?", askedBy: "atlibs.alice.test", status: "decided", outcome: "keep it local", by: "atlibs.alice.test" }]);
assert.deepEqual(view.requests.map((r) => [r.seq, r.replied]), [[7, false], [3, true]], "mallory's request (another DID) is not listed");
const bob = view.agents.find((a) => a.nick === "bob-hat-9");
assert.deepEqual([bob.lines, bob.scrobbles], [3, 1]);
});
// Anyone may post "[answer <id>] ..." text: only the channel owner's answer, the asker's (or
// owner's) decision and asks from the owner's account count; the rest are unverified chat.
test("channelView: forged owner-question lines are unverified and change nothing", () => {
const Q = "11111111-2222-3333-4444-555555555555";
const R = "66666666-7777-8888-9999-aaaaaaaaaaaa";
const mallory = { did: OTHER, nick: "mallory", identity: "mallory.example" };
const owner = { nick: "alice", identity: "alice.test" };
const view = channelView([
line(1, `[ask ${Q}] deploy?`, { nick: "atlibs" }),
line(2, `[answer ${Q}] yes, deploy`, mallory),
line(3, `[answer ${Q}] yes, deploy`, { nick: "bob-hat-9" }),
line(4, `[decided ${Q}] deployed`, { nick: "bob-hat-9" }),
line(5, `[ask ${R}] wire the money?`, mallory),
line(6, `[ask ${Q}] overwrite the question`, mallory),
line(7, `[answer ${Q}] no, hold`, owner),
], { did: ALICE, handle: "alice.test", channel: CHANNEL });
assert.equal(view.owner, "alice.test");
assert.deepEqual(view.lines.map((l) => [l.kind, Boolean(l.unverified)]), [
["ask", false], ["chat", true], ["chat", true], ["chat", true], ["chat", true], ["chat", true], ["answer", false],
]);
assert.deepEqual(view.questions, [{ id: Q, question: "deploy?", askedBy: "atlibs.alice.test", status: "answered", outcome: "no, hold", by: "alice.test" }]);
// Without any owner line or a signed-in owner, no ask verifies.
const stranger = channelView([line(1, `[ask ${Q}] deploy?`, { nick: "atlibs" })], { did: OTHER, handle: "mallory.example", channel: CHANNEL });
assert.deepEqual([stranger.questions, stranger.lines[0].unverified], [[], true]);
});
test("one session: only atirc granted, so compute is signed out and agents is off", async () => {
const { cubit, atirc, compute, signIns } = await page(requestScope(POLICY, ["atirc"]), [line(1, "hello")]);
assert.deepEqual([cubit.state.session, cubit.state.granted, cubit.state.features], ["signed-in", ["atirc"], []]);
assert.equal(atirc.state.session, "signed-in");
assert.equal(atirc.state.messages.list[0].text, "hello");
assert.equal(compute.state.session, "signed-out");
await cubit.announce();
assert.match(cubit.state.announce.error, /agents needs the requester and atirc sessions/);
// Turning compute on: one new consent for both sessions, as the signed-in DID.
await cubit.setSessions(["atirc", "requester"]);
assert.deepEqual(signIns, [{ identifier: ALICE, scope: requestScope(POLICY, ["atirc", "requester"]) }]);
});
test("signed out: login asks for the identity and the ticked sessions in one consent", async () => {
const { cubit, atirc, compute, signIns } = await page(undefined);
assert.deepEqual([cubit.state.session, atirc.state.session, compute.state.session], ["signed-out", "signed-out", "signed-out"]);
await cubit.login("alice.test", ["requester"]);
assert.deepEqual(signIns, [{ identifier: "alice.test", scope: requestScope(POLICY, ["requester"]) }]);
});
test("both sessions: announce the VM, approve an agent's vm-request, answer it on the channel", async () => {
const endpoint = "https://bidder.example";
const { cubit, atirc, compute, xrpc, channel, bidder, revoked } = await page(requestScope(POLICY, ["requester", "atirc"]), [
line(1, `vm-request ${JSON.stringify({ user_data: "#cloud-config\n", bidders: [endpoint] })}`),
line(2, 'vm-request {"user_data":"x"}', { did: OTHER, nick: "mallory" }),
]);
assert.deepEqual(cubit.state.features, ["agents"]);
assert.equal(compute.state.session, "signed-in");
// Announce needs a VM the page found.
await cubit.announce();
assert.match(cubit.state.announce.error, /no receipt/);
compute.emit({ receipt: { uri: `at://did:plc:hat9/${T}.market.receipt/receipt-1`, cid: "bafyrc", bidder: "did:plc:hat9" }, tag: "ssh", phase: "open" });
await cubit.announce();
assert.equal(cubit.state.announce.status, "done");
const posted = xrpc.filter((c) => c.nsid === "chat.atirc.post");
assert.equal(posted[0].input.text, `vm: at://did:plc:hat9/${T}.market.receipt/receipt-1 tag ssh open (socialweb-computer-ui)`);
assert.equal(posted[0].headers["atproto-proxy"], "did:web:irc.fedfork.com#atirc");
// Requests run only once the owner allows them, and only from the account's agents.
await cubit.approve(1);
assert.match(cubit.state.approvals[1].error, /not allowed/);
cubit.allowAgentRequests(true);
await cubit.approve(2);
assert.match(cubit.state.approvals[2].error, /no vm-request 2 from your agents/);
await cubit.approve(1);
assert.equal(cubit.state.approvals[1].status, "done", cubit.state.approvals[1].error);
const rfp = xrpc.find((c) => c.input?.collection === `${T}.market.rfp`);
assert.equal(rfp.input.record.signatures[0].issuer, ALICE);
assert.equal(bidder[0].url, `${endpoint}/xrpc/${T}.market.submitRfp`);
assert.match(channel.at(-1).text, /^vm-request 1: rfp at:\/\/.*market\.rfp.* submitted to 1 bidder\(s\)$/);
await atirc.refresh();
assert.equal(channelView(atirc.state.messages.list, { did: ALICE, handle: "alice.test", channel: CHANNEL }).requests.find((r) => r.seq === 1).replied, true);
cubit.emit({ approvals: {} });
await cubit.approve(1);
assert.match(cubit.state.approvals[1].error, /already answered/);
await cubit.logout();
assert.deepEqual(revoked, [ALICE]);
assert.deepEqual([cubit.state.session, atirc.state.session, compute.state.session], ["signed-out", "signed-out", "signed-out"]);
});
// A page whose plugin context lacks the rkey ledger has every cleanup delete refused ("not created by
// this module"): each page that runs the requester must declare managed_records on that context.
test("pages that run the requester declare storage.managed_records on its plugin context", async () => {
for (const page of ["socialweb-computer-ui", "webrtc"]) {
const main = await readFile(new URL(`../${page}/main.js`, import.meta.url), "utf8");
const contexts = [...main.matchAll(/createPluginContext\(\{([^)]*)\}\)/g)].map((m) => m[1]);
const requester = contexts.filter((c) => /attestKeys/.test(c));
assert.ok(requester.length > 0, `${page}: the requester's context (the one with attestKeys)`);
for (const c of requester) assert.match(c, /storage:\s*\{\s*managed_records:\s*\{\s*\}\s*\}/, `${page}: ${c}`);
}
});