An easy-to-host PDS on the ATProtocol, iPhone and MacOS. Maintain control of your keys and data, always.
1# ezpds
2
3Last verified: 2026-03-31
4
5## Tech Stack
6- Language: Rust (stable channel via rust-toolchain.toml)
7- Build: Cargo workspace (resolver v2)
8- Database: SQLite via sqlx 0.8 (runtime-tokio + sqlite features)
9- Dev Environment: Nix flake + devenv (direnv integration via .envrc)
10- Task Runner: just
11
12## Commands
13- `nix develop --impure --accept-flake-config` - Enter dev shell (flags required; --impure for devenv CWD detection, --accept-flake-config activates the Cachix binary cache in nixConfig — without it, a cold build takes 20+ minutes)
14- `nix build .#relay --accept-flake-config` - Build relay binary (output at ./result/bin/relay)
15- `nix build .#docker-image --accept-flake-config` - Build Docker image tarball (Linux only; output at `./result`; load with `docker load < result`; `docker-image` is not exposed on macOS — use a remote Linux builder or CI)
16- `just nix-check` / `nix flake check --impure --accept-flake-config` - Validate NixOS module evaluation and flake structure
17- `cargo build` - Build all crates
18- `cargo test` - Run all tests
19- `cargo clippy --workspace -- -D warnings` - Lint (warnings as errors)
20- `cargo fmt --all --check` - Check formatting
21
22## Dev Environment
23- Managed entirely by Nix flake + devenv; do not install tools globally
24- direnv auto-activates via `.envrc` (`use flake . --impure --accept-flake-config`)
25- **Always run `nix develop` from the workspace root**, not from a subdirectory — `CARGO_HOME` and `RUSTUP_HOME` resolve relative to devenv root
26- Rust toolchain managed by **rustup** (not Nix's `rust-default`); pinned in `rust-toolchain.toml` (stable, with rustfmt + clippy + rust-analyzer + iOS targets). On first shell entry, `enterShell` runs `rustup toolchain install` automatically.
27- Shell provides: just, cargo-audit, sqlite (runtime binary + dev headers/library for sqlx's libsqlite3-sys), pkg-config, cargo-tauri, node (22.x), pnpm, rustup
28- `LIBSQLITE3_SYS_USE_PKG_CONFIG=1` is set automatically by devenv (links sqlx against Nix-provided SQLite instead of bundled)
29- `DEVELOPER_DIR` is set to `/Applications/Xcode.app/Contents/Developer` in `enterShell` — Nix's Darwin hooks override it to a stub SDK; the re-export restores real Xcode for iOS tooling (xcrun, simctl, xcodebuild)
30- Binary cache: devenv.cachix.org (activated by `--accept-flake-config`); speeds up cold shell builds significantly
31- nixpkgs pin: `cachix/devenv-nixpkgs/rolling` (devenv's own nixpkgs fork — package versions may differ from upstream nixpkgs.search.dev)
32
33## Project Structure
34- `apps/identity-wallet/` - Tauri v2 mobile app (iOS)
35- `crates/relay/` - Web relay (axum-based)
36- `crates/repo-engine/` - ATProto repo engine
37- `crates/crypto/` - Cryptographic operations (P-256 key generation, did:key derivation, AES-256-GCM encryption, did:plc genesis ops and verification)
38- `crates/common/` - Shared types and utilities
39- `nix/` - Nix packaging and deployment (docker.nix: container image; module.nix: NixOS module)
40- `docs/` - Specs, design plans, implementation plans
41
42## Mobile
43
44- `apps/identity-wallet/` — Tauri v2 iOS app (SvelteKit 2 + Svelte 5 frontend, Rust backend)
45- Developer setup and iOS workstation guide: see [`apps/identity-wallet/CLAUDE.md`](apps/identity-wallet/CLAUDE.md)
46
47## Flake Outputs
48- `packages.<system>.relay` - Relay binary
49- `packages.<system>.docker-image` - Docker image tarball (Linux only)
50- `nixosModules.default` - NixOS module exposing `services.ezpds` options (see `nix/CLAUDE.md`)
51- `devShells.<system>.default` - Development shell via devenv
52
53## Bruno API Collection
54- `bruno/` - Bruno HTTP client collection for all relay endpoints
55- Open in Bruno desktop app; select the `local` environment and set `adminToken` to your relay admin token
56- **Mandatory:** When adding, removing, or changing any route (path, method, request body, response shape, auth), update the corresponding `.bru` file in `bruno/`. New routes get a new `.bru` file with the next `seq` number.
57
58## Relay Architecture
59See [`crates/relay/CLAUDE.md`](crates/relay/CLAUDE.md) for relay-specific module structure,
60hard rules (route isolation, pattern comments, DB ownership), and step-by-step guides for
61adding routes and DB queries.
62
63## Conventions
64- Workspace-level dependency versions in root Cargo.toml; crates use `{ workspace = true }`
65- All crates share version (0.1.0) and edition (2021) via workspace.package
66- publish = false (not intended for crates.io)
67- **No ticket or AC references in source code.** Do not add comments like `// MM-123`, `// AC2.1:`, or `// MM-84.AC3: description` to `.rs` files or CLAUDE.md files. Design plans and test plans in `docs/` are the right home for ticket traceability. Source code comments should describe *why* in terms of the system, not which ticket required it.
68
69## Boundaries
70- Never edit: `flake.lock` by hand (managed by `nix flake update`)
71- Never edit: `devenv.local.nix` is gitignored for local overrides only
72- `flake.nix` `buildDepsOnly` is scoped to relay-related crates (`relay`, `repo-engine`, `crypto`, `common`). Adding a workspace crate with native dependencies not in `commonArgs.buildInputs` (e.g. Tauri's webkit2gtk/Apple frameworks) requires either adding the crate to the scope list or adding its build inputs to `commonArgs`.