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

johnandersen777.bsky.social/atlibs

0
0
main
93 lines · 4.7 KB Raw History

How it works

A module is pure WebAssembly: it gets JSON and returns the calls it wants made. The host makes every call with the session it was given, and hands each result back as the next event.

The pieces

flowchart LR
  PDS[(publisher's PDS)] -- wasm.module record + blob --> Host
  Host -- event JSON --> Module[module.wasm]
  Module -- calls, state, result --> Host
  Host -- XRPC with the user's session --> UserPDS[(user's PDS, services)]
  • Module: a .wasm with no imports, and its module.json (entries, allowed calls, session).
  • Host: runs modules and makes their calls. Three hosts share one protocol: the browser (javascript/src), Node (javascript/src/server) and Rust (rust/host, behind the atlibs CLI).
  • Session: who the calls act as. A module names its session record, and a signed-in run may only make the calls that record's scope allows.
  • Records: modules, their OAuth client records, source and compositions are atproto records, so anyone can publish them and anyone can fetch and check them.

The step protocol

The host calls an entry once per event, with the action's input, did, now, the state the entry returned last and the event. The entry answers with calls to make and new state, or a result.

import { load } from "./rust/src/harness.mjs";

const { run } = await load(new URL("./target/wasm32-unknown-unknown/release/hello.wasm", `file://${process.cwd()}/`));
const input = { did: "did:plc:alice", now: "2026-11-09T00:00:00.000Z", collection: "xyz.statusphere.status" };
console.log(JSON.stringify(run("count", { ...input, state: null, event: { type: "start" } })));
console.log(JSON.stringify(run("count", { ...input, state: null, event: { type: "result", id: "list", nsid: "com.atproto.repo.listRecords", ok: true, output: { records: [{}, {}] } } })));
{"calls":[{"id":"list","nsid":"com.atproto.repo.listRecords","params":{"collection":"xyz.statusphere.status","limit":"100","repo":"did:plc:alice"},"service":"did:plc:alice"}]}
{"done":true,"result":{"collection":"xyz.statusphere.status","records":2}}

Events in:

  • start: the first event.
  • result {id}: a call finished; ok and output, or ok: false and error.
  • message {id}, closed {id}: a subscription's messages, and its end.
  • timer {id}: a timer the module set.
  • input {data}: data the host feeds into a running module.
  • callback {id, cap}: another module invoked a capability this run passed it.

Output fields: calls, send (write up a subscription), cancel, timers, state, result, done. The run ends when the module says done, or when nothing is pending.

Who a run acts as

whereacts aschosen by
a pagethe signed-in user's OAuth sessionBrowserOAuthClient (Sign in from a page)
a server (atlibs serve, Node)each request's signed-in user, with their own OAuth session/oauth/login, then the atlibs_session cookie or bearer (Server sessions)
the CLIan OAuth session atlibs login saved--as <handle>
tests, an operator accountan app password--app-password <handle> with --password-file or --password-env
none of the abovenobody: public reads only

Nothing is read from the environment: ATPROTO_* variables mean nothing to atlibs.

Where calls go

callgoes to
{ nsid, params } / { nsid, input }the session's own PDS
{ nsid, params, proxy: "did:web:svc#id" }the PDS, which proxies it (atproto-proxy); signed out, a query goes to that service directly, no token
{ nsid, input, service: "https://svc" }that service, with a service-auth token the host mints
{ nsid, params, service: "did:plc:..." }a public read of that account's PDS, no token (did:web:svc#id: of that service)
{ nsid, ..., subscribe: true }a WebSocket the host holds; messages arrive as events
com.fedfork.exec.*the host itself: attest, did:key, and transports (iroh, WebRTC)

Details: Calls.

Records

collectionwhat
com.fedfork.exec.wasm.modulea module: its wasm blob, entries, allowed calls, session, hooks
com.fedfork.exec.oauth.clientMetadataan OAuth client: the scope a module's session gets
com.fedfork.exec.source.file, .source.manifestthe source a module was built from, so anyone can rebuild it
com.fedfork.exec.compositionmodules wired together

A module record names its session record and its source manifest by URI and CID. A pin (the wasm's sha256 plus the record's grant fields) is what a lockfile checks. See Publish a module.

Previous: Docs | Next: Run modules in a page