~ 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

```mermaid
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.

```js run
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: [{}, {}] } } })));
```

```output
{"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

| where | acts as | chosen by |
|---|---|---|
| a page | the signed-in user's OAuth session | `BrowserOAuthClient` ([Sign in from a page](browser/oauth.md)) |
| 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](server/sessions.md)) |
| the CLI | an OAuth session `atlibs login` saved | `--as <handle>` |
| tests, an operator account | an app password | `--app-password <handle>` with `--password-file` or `--password-env` |
| none of the above | nobody: public reads only | |

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

## Where calls go

| call | goes 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](modules/calls.md).

## Records

| collection | what |
|---|---|
| `com.fedfork.exec.wasm.module` | a module: its wasm blob, entries, allowed calls, session, hooks |
| `com.fedfork.exec.oauth.clientMetadata` | an OAuth client: the scope a module's session gets |
| `com.fedfork.exec.source.file`, `.source.manifest` | the source a module was built from, so anyone can rebuild it |
| `com.fedfork.exec.composition` | modules 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](modules/publish.md).

Previous: [Docs](README.md) | Next: [Run modules in a page](browser/run-modules.md)