# Getting started ## Requirements - A C23 compiler (Clang ships with Xcode Command Line Tools on macOS) - CMake ≥ 3.20 - libcurl - OpenSSL (libcrypto) — for SHA-256 hashing - libsecp256k1 — for secp256k1 signing (optional; crypto stubs gracefully if absent) - c-ares 1.28+ — for portable DNS TXT resolution (optional; POSIX resolver fallback) - libzstd — for Jetstream compressed messages (optional) - SQLite3 — **only** required when building the optional persistence module (`WOLFRAM_BUILD_STORE=ON`); the default build does not link it - libsodium — **only** required when `WOLFRAM_BUILD_STORE_CRYPTO=ON` (which also requires `WOLFRAM_BUILD_STORE=ON`); it provides secret-key encryption (`crypto_secretbox_easy`, XSalsa20-Poly1305) and passphrase key derivation (`crypto_pwhash`, Argon2id) for persisted sessions at rest - libmicrohttpd (GNU MHD) — **only** required when `WOLFRAM_BUILD_TEST_HTTPD=ON` for the offline HTTP integration test (`mock_pds`) - libidn2 (GNU, MIT) — **only** required when `WOLFRAM_BUILD_IDN=ON`. It provides IDNA2008 / punycode (ACE, `xn--…`) conversion for internationalized handles On macOS via Homebrew: ```sh brew install cmake curl openssl secp256k1 c-ares zstd # Optional persistence + at-rest encryption: brew install sqlite3 libsodium # Optional offline HTTP integration test (WOLFRAM_BUILD_TEST_HTTPD=ON): brew install libmicrohttpd # Optional internationalized (IDN) handle resolution (WOLFRAM_BUILD_IDN=ON): brew install libidn2 ``` (libcurl also ships with macOS itself, but the Homebrew one is newer and CMake finds it more reliably via `pkg-config`.) Jetstream connections require libcurl 7.86+ built with `ws`/`wss` support; without it, WebSocket attempts return `WF_ERR_INVALID_ARG`. Compressed Jetstream subscriptions additionally require libzstd and the [official Jetstream dictionary](https://github.com/bluesky-social/jetstream/blob/main/pkg/models/zstd_dictionary), supplied through `wf_jetstream_options`. ## Building ```sh cmake -S . -B build cmake --build build ctest --test-dir build ``` The generated Lexicon wrappers (`include/wolfram/atproto_lex.h` and `src/atproto_lex.c`) are **prebuilt and checked into the repository**, so a fresh clone compiles without running `wf_lexgen.cpp`. You only need it to regenerate wrappers from a different/newest Lexicon set or a custom lexicon. If Homebrew's libcurl isn't picked up automatically: ```sh cmake -S . -B build -DCMAKE_PREFIX_PATH="$(brew --prefix curl)" ``` ## Optional persistence and at-rest encryption ```sh # Persistence only (sessions + repo mirror stored in plaintext columns): cmake -S . -B build -DWOLFRAM_BUILD_STORE=ON # Persistence with libsodium at-rest encryption of the session blob: cmake -S . -B build -DWOLFRAM_BUILD_STORE=ON -DWOLFRAM_BUILD_STORE_CRYPTO=ON ``` With `WOLFRAM_BUILD_STORE_CRYPTO=ON`, call `wf_store_set_passphrase` after `wf_store_open` and before saving/loading a session; the key is derived via libsodium's `crypto_pwhash` (Argon2id). ## Generating Lexicon wrappers ```sh cmake --build build --target wf_lexgen_tool ./build/wf_lexgen_tool path/to/lexicons/*.json -o generated/lexicons.h # Add generated JSON codecs and XRPC wrappers: ./build/wf_lexgen_tool path/to/lexicons/*.json -o generated/lexicons.h \ --source-output generated/lexicons.c ``` Generated structs cover records, named object definitions, and XRPC parameters, inputs, and outputs. Optional fields have explicit `has_*` flags; inputs are borrowed and must outlive the struct, while output decoders return owning objects with a `_output_free`. Input encoders resolve referenced objects, arrays, strings, and tokens recursively. ## The CLI The default build produces `./build/wolfram`, the primary command-line client: ```sh ./build/wolfram post https://bsky.social you@example.com yourpassword "Hello!" ./build/wolfram help # every command ./build/wolfram help post # one command ``` [docs/cli.md](cli.md) documents it in full. ## Examples The programs below are separate single-purpose binaries under `examples/`, built only when configured with `-DWOLFRAM_BUILD_EXAMPLES=ON`. They are not produced by the default build: ```sh ./build/describe_repo https://eurosky.social did:plc:ofrbh253gwicbkc5nktqepol ./build/create_post https://bsky.social you@example.com yourpassword "Hello from wolfram!" ./build/upload_image https://bsky.social you@example.com yourpassword ./cat.jpg "a cat" ./build/get_record https://bsky.social did:plc:example app.bsky.feed.post 3jui7kd54zh2y ./build/subscribe_labels https://mod.bsky.app ./build/create_account https://bsky.social alice.bsky.social alice@example.com hunter2 ./build/create_app_password https://bsky.social alice@example.com hunter2 "my-app" ``` End-to-end examples also live in `examples/`: `feed_generator`, `labeler_service`, `ozone_moderation`, `post_image_embed`, and `timeline_moderation`. ## Available modules and guides wolfram's features are split across focused modules, each with a usage guide: - **Agent / high-level API** — [agent.md](agent.md), [typed_api.md](typed_api.md) (feed, thread, graph, list, embed, notification, and the new [unspecced.md](unspecced.md) typed parsers) - **Identity & server management** — DID/handle resolution plus `com.atproto.identity` account ops and `com.atproto.server` account lifecycle. See [identity.md](identity.md). - **Moderation & labeling** — [moderation.md](moderation.md) - **Sync / repo mirror** — [sync.md](sync.md) - **Video upload** — upload a video blob, poll the job status, and embed it in a post. See [video.md](video.md). - **OAuth / DPoP** — [oauth.md](oauth.md) - **Validation** — [validate.md](validate.md); **Notifications** — [notification.md](notification.md); **Agent internals** — [agent_usage.md](agent_usage.md) Build everything with the commands in [Building](#building) above, then pick the guide that matches what you're building.