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

johnandersen777.bsky.social/atlibs

0
0
main
266 lines · 9.9 KB Raw History

CLI

atlibs is one Rust binary: run and serve modules, fetch published ones, stage them for a site or a server, sign in, and publish. atlibs help lists everything; a bad invocation prints that command's usage. Results go to stdout, progress and errors to stderr; a reader that stops early (atlibs help | head -1) ends the command quietly, exit 0.

Install

cargo install --locked --git https://knotless.dev/johnandersen777.bsky.social/atlibs --rev 88b2ba8 atlibs

Into ~/.cargo/bin (--root <dir>: <dir>/bin). From a clone: cargo install --locked --path rust/cli, or cargo host (into rust/host/target/<host triple>/release/).

Refs

Every command takes modules by ref, resolved the same way everywhere:

refexample
a published modulebobvmbuilder.bsky.social/echo, did:plc:.../echo, at://did:plc:.../com.fedfork.exec.wasm.module/echo
pinnedbobvmbuilder.bsky.social/echo@<32+ hex of its pin> or @<record CID>
a name in atlibs.lock.jsonecho (after atlibs add)
a module directoryexamples/rust/hello (its module.json, the cargo wasm build above it, records/<session>.json)
a bare example namehello, for examples/rust/hello
a compositionexamples/rust/compose/composition.json, or a published one
a wasm and its specmodule.wasm module.json
a staged modulestaged/stage.json compose

Output and atlibs.lock.json's ref show <handle>/<lib>; what identifies a module is its uri (by DID), cid and pin, so a handle that moves breaks nothing. A published composition names its modules by at:// URI.

A <ref> is <handle>/<lib>[@pin|@cid] (or <did>/<lib>), an at:// URI, a name in atlibs.lock.json, a module directory or a name under examples/rust, a composition or clientMetadata .json, <module.wasm> <module.json>, or <stage.json|atlibs.lock.json> <name>.

run

run <ref> [<instance>] <entry> ['<json>' | - | @file] [--handler <name>=<spec> ...] [--allow-exec] [--params '<json>' | @file]

Prints { entry, result, error?, events, calls }; exits 1 when the run failed. - reads the input from stdin and @file from a file, which keeps secrets out of ps and out of error messages.

atlibs run hello 2>&1 | tail -1
echo '{"n":3}' | atlibs run examples/rust/compose lookup - | jq -c .result
hello entries: count
{"ack":null,"caller":null,"doubled":6}

A composition runs one instance's entry. Its declared handlers come from --handler: another module's entry (wasm:<ref>#<entry>), an https:// URL (http:// to localhost only), or a command (exec:<command>, with --allow-exec; JSON in on stdin, out on stdout, an empty environment).

atlibs run examples/rust/compose/composition.json ui caller '{"nsid":"com.example.compose.confirm","input":{"n":1}}' \
  --handler 'confirm=exec:jq -c "{approved: true, n: .input.n}"' --allow-exec | jq -c .result.q
{"ok":true,"output":{"approved":true,"n":1}}

serve

serve <ref> [--port 8787] [--host 127.0.0.1] [--origin URL | ATLIBS_ORIGIN] [--oauth-path oauth/] [--handler <name>=<spec> ...] [--allow-exec]

GET / lists the entries (a form page in a browser), POST /run/<entry> runs one (/run/<instance>/<entry> for a composition), GET /oauth/<session>/client-metadata.json serves the modules' OAuth clients. Sign-in and the request guard: Server sessions. With --origin and a session record that declares app, it also serves the app's own identity (App identity).

atlibs serve examples/rust/compose/composition.json --handler confirm=wasm:examples/rust/compose#lookup --port 8793 &
curl -s -X POST localhost:8793/run/ui/caller -H 'content-type: application/json' -d '{"nsid":"com.example.compose.lookup","input":{"n":6}}' | jq .result.q.output.doubled
12

add

add <handle>/<lib>[@pin|@cid] | <at://...> ... [--dir .]

Resolves the handle to a DID (checked both ways), fetches the module record (or a composition), checks the blob against its CID, and writes atlibs.lock.json and modules/<lib>.wasm + .json. A module's hook defaults and a composition's modules come too.

atlibs add bobvmbuilder.bsky.social/echo
jq -c '.modules.echo | {ref, session, wasm}' atlibs.lock.json
{"ref":"bobvmbuilder.bsky.social/echo","session":"echo","wasm":"modules/echo.wasm"}

Every read of a vendored module checks its wasm and spec against the lockfile's pin: the sha256 of the wasm's sha256 and the record's grant fields (calls, anyRecords, hooks, implements, anyHooks, didkey). A republished record whose calls changed no longer matches. add again to update.

stage

stage [--target spa|server] [--out dist] [--origin https://site/] [--oauth-path oauth/] [--name <client> [--record <file>]] [--dir .] [<ref> ...]

--target spa writes a static site's files (Sign in from a page); --target server writes modules and a stage.json with each session's record, for a server to mount (Node, Rust). No ref: everything in atlibs.lock.json. A spa with --origin and a session record that declares app gets the app identity's static files too (App identity).

atlibs stage --target server --out staged examples/rust/compose examples/rust/statusphere | jq -c '{modules: (.modules | keys), sessions: (.sessions | keys)}'
{"modules":["compose","statusphere"],"sessions":["statusphere"]}

oauth

oauth --origin https://site/ [--oauth-path oauth/] [--name <client>] [--dir .] [<ref> ...]

Prints the client metadata documents an origin serves, by session.

atlibs oauth --origin https://you.example/ examples/rust/statusphere | jq -r .statusphere.client_id
https://you.example/oauth/statusphere/client-metadata.json

login, logout

login <handle|did> [--scope "atproto transition:generic"] [--no-open]
logout <handle|did>

Signs in with OAuth through a loopback redirect in your browser (ATLIBS_BROWSER opens it; the URL is printed either way). The session is saved per DID in the state dir and refreshed as it expires.

atlibs login you.bsky.social
atlibs run examples/rust/statusphere clear_status --as you.bsky.social
atlibs logout you.bsky.social
flag (every command)acts as
--as <handle|did>the OAuth session atlibs login saved
--app-password <handle> with --password-file <path|-> or --password-env <NAME> [--pds <url>]an app password (tests, operator accounts); saved, so later commands may give --app-password <handle> alone
neithersigned out: public reads only

new

new <name>
atlibs new my-module > /dev/null && find my-module -type f | LC_ALL=C sort
my-module/.cargo/config.toml
my-module/.gitignore
my-module/Cargo.toml
my-module/modules/my-module/Cargo.toml
my-module/modules/my-module/module.json
my-module/modules/my-module/src/lib.rs

publish

publish [<name> ...] [--dir modules] [--dry-run] [--check-live] [--no-build] [--allow-dirty] [--out records/wasm]
publish module <dir|name> ... [--no-build] [--out records/wasm] [--allow-dirty]
publish records [<file> ...]
publish tile <dir> <name> [--out records/tiles]
publish composition <file> [--rkey <name>]
publish delete <at://did/collection/rkey | collection/rkey> ...

atlibs publish builds every module, compares each with what it last published (a cache under $XDG_CACHE_HOME/atlibs/<did>/, or the live records with --check-live), publishes what changed and prints a table: published, unchanged, would-publish (--dry-run), skipped:dirty (uncommitted sources), drift (changed elsewhere), failed. The subcommands publish one kind of thing each. Details: Publish a module.

atlibs publish --dry-run --as you.bsky.social

verify

verify <handle>/<lib> | <at://.../com.fedfork.exec.wasm.module/...>

Rebuilds a published module from its source records in a container and compares the sha256 with the manifest and the blob: atlibs publish built it in that same rust:<version> container. The engine is CONTAINER_ENGINE, else the first of podman, docker and Apple's container (macOS) that answers info (container system status): a docker CLI whose daemon is stopped is skipped. A build that exits 0 without writing the manifest's artifact is reported as the engine's fault (a docker look-alike whose run returns before the command ends).

atlibs verify bobvmbuilder.bsky.social/echo

compose

compose bindings [--dir .] [<module dir> ...]
compose check <composition.json>

compose bindings writes atlibs.modules.js and .d.ts, the modules a compose.ts imports (Compositions): those atlibs.lock.json vendors (pinned; atlibs add writes them too), modules/<name> directories and the ones named.

compose check runs the hosts' load checks without the wasm: modules from atlibs.lock.json or stage.json beside it (pins too), module directories (module.json) or published refs. Prints the composition as it would run, hook defaults added; exits 1 with the reason it would not load.

atlibs compose check examples/rust/compose/composition.json | jq -c '[.instances[].name]'
jq '.instances[1].input.n = "four"' examples/rust/compose/composition.json > bad.json
atlibs compose check bad.json 2>&1 || echo "exit $?"
["ui","lookup","even"]
composition: instance lookup: input n must be of type integer
exit 1

Environment

Every variable: reference/env.md.

Previous: MCP servers | Next: Write a module