# 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
```bash run=install
cargo install --locked --git https://knotless.dev/johnandersen777.bsky.social/atlibs --rev 88b2ba8 atlibs
```
Into `~/.cargo/bin` (`--root
`: `/bin`). From a clone: `cargo install --locked --path rust/cli`,
or `cargo host` (into `rust/host/target//release/`).
## Refs
Every command takes modules by **ref**, resolved the same way everywhere:
| ref | example |
|---|---|
| a published module | `bobvmbuilder.bsky.social/echo`, `did:plc:.../echo`, `at://did:plc:.../com.fedfork.exec.wasm.module/echo` |
| pinned | `bobvmbuilder.bsky.social/echo@<32+ hex of its pin>` or `@` |
| a name in `atlibs.lock.json` | `echo` (after `atlibs add`) |
| a module directory | `examples/rust/hello` (its `module.json`, the `cargo wasm` build above it, `records/.json`) |
| a bare example name | `hello`, for `examples/rust/hello` |
| a composition | `examples/rust/compose/composition.json`, or a published one |
| a wasm and its spec | `module.wasm module.json` |
| a staged module | `staged/stage.json compose` |
Output and `atlibs.lock.json`'s `ref` show `/`; 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.
```text help
A [ is /[@pin|@cid] (or /), an at:// URI, a name in atlibs.lock.json, a module directory or a name under examples/rust, a composition or clientMetadata .json, , or .
```
## run
```text help
run ][ [] ['' | - | @file] [--handler = ...] [--allow-exec] [--params '' | @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.
```bash run
atlibs run hello 2>&1 | tail -1
echo '{"n":3}' | atlibs run examples/rust/compose lookup - | jq -c .result
```
```output
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:][#`), an `https://` URL (`http://` to localhost only), or a command
(`exec:`, with `--allow-exec`; JSON in on stdin, out on stdout, an empty environment).
```bash run
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
```
```output
{"ok":true,"output":{"approved":true,"n":1}}
```
## serve
```text help
serve ][ [--port 8787] [--host 127.0.0.1] [--origin URL | ATLIBS_ORIGIN] [--oauth-path oauth/] [--handler = ...] [--allow-exec]
```
`GET /` lists the entries (a form page in a browser), `POST /run/` runs one
(`/run//` for a composition), `GET /oauth//client-metadata.json` serves the
modules' OAuth clients. Sign-in and the request guard: [Server sessions](server/sessions.md). With
`--origin` and a session record that declares `app`, it also serves the app's own identity
([App identity](server/app-identity.md)).
```bash run
atlibs serve examples/rust/compose/composition.json --handler confirm=wasm:examples/rust/compose#lookup --port 8793 &
```
```bash run
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
```
```output
12
```
## add
```text help
add /[@pin|@cid] | ... [--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/.wasm` + `.json`. A module's
hook defaults and a composition's modules come too.
```bash run=network
atlibs add bobvmbuilder.bsky.social/echo
jq -c '.modules.echo | {ref, session, wasm}' atlibs.lock.json
```
```output
{"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
```text help
stage [--target spa|server] [--out dist] [--origin https://site/] [--oauth-path oauth/] [--name [--record ]] [--dir .] [][ ...]
```
`--target spa` writes a static site's files ([Sign in from a page](browser/oauth.md));
`--target server` writes modules and a `stage.json` with each session's record, for a server to mount
([Node](server/node.md), [Rust](server/rust.md)). 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](server/app-identity.md)).
```bash run
atlibs stage --target server --out staged examples/rust/compose examples/rust/statusphere | jq -c '{modules: (.modules | keys), sessions: (.sessions | keys)}'
```
```output
{"modules":["compose","statusphere"],"sessions":["statusphere"]}
```
## oauth
```text help
oauth --origin https://site/ [--oauth-path oauth/] [--name ] [--dir .] [][ ...]
```
Prints the client metadata documents an origin serves, by session.
```bash run
atlibs oauth --origin https://you.example/ examples/rust/statusphere | jq -r .statusphere.client_id
```
```output
https://you.example/oauth/statusphere/client-metadata.json
```
## login, logout
```text help
login [--scope "atproto transition:generic"] [--no-open]
logout
```
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.
```bash run=account
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 ` | the OAuth session `atlibs login` saved |
| `--app-password ` with `--password-file ` or `--password-env ` `[--pds ]` | an app password (tests, operator accounts); saved, so later commands may give `--app-password ` alone |
| neither | signed out: public reads only |
## new
```text help
new
```
```bash run
atlibs new my-module > /dev/null && find my-module -type f | LC_ALL=C sort
```
```output
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
```text help
publish [ ...] [--dir modules] [--dry-run] [--check-live] [--no-build] [--allow-dirty] [--out records/wasm]
publish module ... [--no-build] [--out records/wasm] [--allow-dirty]
publish records [ ...]
publish tile [--out records/tiles]
publish composition [--rkey ]
publish delete ...
```
`atlibs publish` builds every module, compares each with what it last published (a cache under
`$XDG_CACHE_HOME/atlibs//`, 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](modules/publish.md).
```bash run=account
atlibs publish --dry-run --as you.bsky.social
```
## verify
```text help
verify / |
```
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:` 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).
```bash run=container
atlibs verify bobvmbuilder.bsky.social/echo
```
## compose
```text help
compose bindings [--dir .] [ ...]
compose check
```
`compose bindings` writes `atlibs.modules.js` and `.d.ts`, the modules a `compose.ts` imports
([Compositions](compositions.md#write-one)): those `atlibs.lock.json` vendors (pinned; `atlibs add` writes
them too), `modules/` 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.
```bash run
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 $?"
```
```output
["ui","lookup","even"]
composition: instance lookup: input n must be of type integer
exit 1
```
## Environment
Every variable: [reference/env.md](reference/env.md).
Previous: [MCP servers](server/mcp.md) | Next: [Write a module](modules/write.md)
]