~ 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

```bash run=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:

| 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 `@<record CID>` |
| 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/<session>.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 `<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.

```text help
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

```text help
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.

```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:<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).

```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 <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](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 <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.

```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 <client> [--record <file>]] [--dir .] [<ref> ...]
```

`--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 <client>] [--dir .] [<ref> ...]
```

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

```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 <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 |
| neither | signed out: public reads only |

## new

```text help
new <name>
```

```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 [<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](modules/publish.md).

```bash run=account
atlibs publish --dry-run --as you.bsky.social
```

## verify

```text help
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).

```bash run=container
atlibs verify bobvmbuilder.bsky.social/echo
```

## compose

```text help
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](compositions.md#write-one)): 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.

```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)