An easy-to-host PDS on the ATProtocol, MacOS. Grandma-approved.

feat(MM-65): add nix build output for relay binary using crane #3

merged opened by malpercio.dev targeting main from malpercio/mm-65-nix-flake-relay-build-output

Summary#

  • Adds nix build .#relay (and nix build via default) to flake.nix
  • Uses crane's two-phase build (buildDepsOnlybuildPackage) for cached dependency derivations
  • Passes sqlite + pkg-config so rusqlite links correctly; LIBSQLITE3_SYS_USE_PKG_CONFIG=1 mirrors the devenv setup
  • Pins crane via flake.lock

Test plan#

  • nix build .#relay --accept-flake-config completes without error
  • ./result/bin/relay runs without crashing (prints relay starting)
  • nix build --accept-flake-config (default) also resolves to the relay binary
  • Dev shell still works: nix develop --impure --accept-flake-config

Closes MM-65

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:web:malpercio.dev/sh.tangled.repo.pull/3mgjgjj65hz22
+1064 -4
Diff #1
+1 -1
.envrc
··· 1 - use flake 1 + use flake . --impure --accept-flake-config
+1 -1
CLAUDE.md
··· 17 17 18 18 ## Dev Environment 19 19 - Managed entirely by Nix flake + devenv; do not install tools globally 20 - - direnv auto-activates via `.envrc` (`use flake`) 20 + - direnv auto-activates via `.envrc` (`use flake . --impure --accept-flake-config`) 21 21 - Rust toolchain pinned in `rust-toolchain.toml` (stable, with rustfmt + clippy + rust-analyzer) 22 22 - Shell provides: just, cargo-audit, sqlite (runtime binary + dev headers/library for rusqlite), pkg-config 23 23 - `LIBSQLITE3_SYS_USE_PKG_CONFIG=1` is set automatically by devenv
+1 -1
docs/design-plans/2026-03-07-MM-64.md
··· 100 100 101 101 **Components:** 102 102 - `flake.nix` — three-input minimal flake with Cachix `nixConfig` and `devenv.lib.mkShell` for `devShells.default` 103 - - `devenv.nix` — `languages.rust.toolchainFile = ./rust-toolchain.toml`, `packages = [pkgs.just pkgs.cargo-audit pkgs.sqlite]` 103 + - `devenv.nix` — `languages.rust.toolchainFile = ./rust-toolchain.toml`, `packages = [pkgs.just pkgs.cargo-audit pkgs.sqlite pkgs.pkg-config]`, `env.LIBSQLITE3_SYS_USE_PKG_CONFIG = "1"` (pkg-config required explicitly; languages.rust does not include it automatically) 104 104 - `rust-toolchain.toml` — `channel = "stable"`, `components = ["rustfmt" "clippy" "rust-analyzer"]`, `targets = ["aarch64-apple-darwin" "x86_64-unknown-linux-gnu"]` 105 105 - `.envrc` — single line: `use flake` 106 106 - `.gitignore` — append `.devenv/`, `.direnv/`, `devenv.local.nix`
+229
docs/implementation-plans/2026-03-07-MM-64/phase_01.md
··· 1 + # MM-64 Implementation Plan — Phase 1 2 + 3 + **Goal:** Produce all static configuration files for the Nix flake + devenv dev shell. 4 + 5 + **Architecture:** Minimal Nix flake (flake.nix) wires devenv as the sole output. A separate devenv.nix module configures the Rust toolchain and packages. rust-toolchain.toml is the single source of truth for the toolchain version, readable by both devenv (via rust-overlay) and rustup. 6 + 7 + **Tech Stack:** Nix flakes, devenv (cachix/devenv), nix-systems, rust-overlay (used internally by devenv) 8 + 9 + **Scope:** 2 phases total (Phase 1 of 2) 10 + 11 + **Codebase verified:** 2026-03-07 12 + 13 + --- 14 + 15 + ## Acceptance Criteria Coverage 16 + 17 + **Verifies: None** — This is an infrastructure phase that creates configuration files. All acceptance criteria (MM-64.AC1–MM-64.AC6) are verified operationally in Phase 2 by running `nix develop`. 18 + 19 + --- 20 + 21 + <!-- START_SUBCOMPONENT_A (tasks 1-5) --> 22 + 23 + <!-- START_TASK_1 --> 24 + ### Task 1: Create `flake.nix` 25 + 26 + **Files:** 27 + - Create: `flake.nix` 28 + 29 + **Step 1: Create the file with the following exact contents** 30 + 31 + ```nix 32 + { 33 + description = "ezpds development shell"; 34 + 35 + nixConfig = { 36 + extra-substituters = "https://devenv.cachix.org"; 37 + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; 38 + }; 39 + 40 + inputs = { 41 + nixpkgs.url = "github:cachix/devenv-nixpkgs/rolling"; 42 + devenv.url = "github:cachix/devenv"; 43 + systems.url = "github:nix-systems/default"; 44 + }; 45 + 46 + outputs = { self, nixpkgs, devenv, systems, ... } @ inputs: 47 + let 48 + forEachSystem = f: nixpkgs.lib.genAttrs (import systems) f; 49 + in { 50 + devShells = forEachSystem (system: 51 + let 52 + pkgs = nixpkgs.legacyPackages.${system}; 53 + in { 54 + default = devenv.lib.mkShell { 55 + inherit inputs pkgs; 56 + modules = [ ./devenv.nix ]; 57 + }; 58 + } 59 + ); 60 + }; 61 + } 62 + ``` 63 + 64 + **Step 2: Verify file exists** 65 + 66 + Run: `ls -la flake.nix` 67 + Expected: file listed 68 + 69 + **Step 3: Verify scope boundaries (no packages or nixosModules outputs)** 70 + 71 + Run: 72 + ```bash 73 + grep -E "^\s*packages\s*=" flake.nix && echo "FAIL: packages output found" || echo "OK: no packages output" 74 + grep "nixosModules" flake.nix && echo "FAIL: nixosModules found" || echo "OK: no nixosModules" 75 + ``` 76 + Expected: both lines print "OK" 77 + <!-- END_TASK_1 --> 78 + 79 + <!-- START_TASK_2 --> 80 + ### Task 2: Create `devenv.nix` 81 + 82 + **Files:** 83 + - Create: `devenv.nix` 84 + 85 + **Step 1: Create the file with the following exact contents** 86 + 87 + ```nix 88 + { pkgs, lib, config, ... }: 89 + { 90 + languages.rust = { 91 + enable = true; 92 + toolchainFile = ./rust-toolchain.toml; 93 + }; 94 + 95 + packages = [ 96 + pkgs.just 97 + pkgs.cargo-audit 98 + pkgs.sqlite 99 + pkgs.pkg-config 100 + ]; 101 + 102 + env.LIBSQLITE3_SYS_USE_PKG_CONFIG = "1"; 103 + } 104 + ``` 105 + 106 + Notes: 107 + - `languages.rust.toolchainFile` requires `enable = true` to be set alongside it — devenv does not auto-detect rust-toolchain.toml. 108 + - `pkgs.pkg-config` must be explicit — `languages.rust` does not include it automatically. 109 + - `pkgs.sqlite` includes both the runtime binary and dev headers (the `.dev` output is propagated by nixpkgs). 110 + - `LIBSQLITE3_SYS_USE_PKG_CONFIG = "1"` tells `libsqlite3-sys` (used by `rusqlite`) to find sqlite via pkg-config instead of attempting to bundle it. 111 + 112 + **Step 2: Verify file exists** 113 + 114 + Run: `ls -la devenv.nix` 115 + Expected: file listed 116 + <!-- END_TASK_2 --> 117 + 118 + <!-- START_TASK_3 --> 119 + ### Task 3: Create `rust-toolchain.toml` 120 + 121 + **Files:** 122 + - Create: `rust-toolchain.toml` 123 + 124 + **Step 1: Create the file with the following exact contents** 125 + 126 + ```toml 127 + [toolchain] 128 + channel = "stable" 129 + components = ["rustfmt", "clippy", "rust-analyzer"] 130 + targets = ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu"] 131 + ``` 132 + 133 + Notes: 134 + - `channel = "stable"` satisfies MM-64.AC2.1 (stable release, not nightly or beta). 135 + - The host platform target is always included automatically by rustup/rust-overlay; the listed targets enable cross-compilation from any platform to the others. 136 + - devenv reads this file via `rust-overlay`'s `fromRustupToolchainFile`. rustup reads it natively on machines without Nix. 137 + - `rust-analyzer` as a component satisfies MM-64.AC2.3. 138 + - `clippy` as a component satisfies MM-64.AC2.4. 139 + - `rustfmt` as a component satisfies MM-64.AC2.5. 140 + 141 + **Step 2: Verify file exists** 142 + 143 + Run: `ls -la rust-toolchain.toml` 144 + Expected: file listed 145 + <!-- END_TASK_3 --> 146 + 147 + <!-- START_TASK_4 --> 148 + ### Task 4: Create `.envrc` 149 + 150 + **Files:** 151 + - Create: `.envrc` 152 + 153 + **Step 1: Create the file with the following exact contents (single line)** 154 + 155 + ``` 156 + use flake 157 + ``` 158 + 159 + Notes: 160 + - `use flake` is the direnv hook that activates the Nix devShell automatically on `cd`. 161 + - This satisfies MM-64.AC4.1 (`.envrc` contains `use flake` and is tracked by git — it will be tracked once committed in Task 6). 162 + 163 + **Step 2: Verify file contents** 164 + 165 + Run: `cat .envrc` 166 + Expected: `use flake` 167 + <!-- END_TASK_4 --> 168 + 169 + <!-- START_TASK_5 --> 170 + ### Task 5: Update `.gitignore` 171 + 172 + **Files:** 173 + - Modify: `.gitignore` (append to end) 174 + 175 + **Step 1: Append the following block to the end of `.gitignore`** 176 + 177 + The current `.gitignore` ends after the `# Environment files` section. Append: 178 + 179 + ``` 180 + # Nix / devenv 181 + .devenv/ 182 + .direnv/ 183 + devenv.local.nix 184 + ``` 185 + 186 + **Step 2: Verify entries are present** 187 + 188 + Run: 189 + ```bash 190 + grep -E "\.devenv/|\.direnv/|devenv\.local\.nix" .gitignore 191 + ``` 192 + Expected: all three lines printed 193 + <!-- END_TASK_5 --> 194 + 195 + <!-- END_SUBCOMPONENT_A --> 196 + 197 + <!-- START_TASK_6 --> 198 + ### Task 6: Commit all configuration files 199 + 200 + **Step 1: Stage exactly the five files** 201 + 202 + ```bash 203 + git add flake.nix devenv.nix rust-toolchain.toml .envrc .gitignore 204 + ``` 205 + 206 + **Step 2: Verify staging — confirm only expected files are staged** 207 + 208 + Run: `git status` 209 + Expected: 210 + - `flake.nix` — new file 211 + - `devenv.nix` — new file 212 + - `rust-toolchain.toml` — new file 213 + - `.envrc` — new file 214 + - `.gitignore` — modified 215 + 216 + **Step 3: Commit** 217 + 218 + ```bash 219 + git commit -m "$(cat <<'EOF' 220 + chore(MM-64): add Nix flake + devenv dev shell configuration 221 + 222 + Adds flake.nix, devenv.nix, rust-toolchain.toml, .envrc. Phase 2 will 223 + run nix develop to generate flake.lock and verify the shell. 224 + EOF 225 + )" 226 + ``` 227 + 228 + Expected: commit message displayed, branch `MM-64` advanced by one commit. 229 + <!-- END_TASK_6 -->
+209
docs/implementation-plans/2026-03-07-MM-64/phase_02.md
··· 1 + # MM-64 Implementation Plan — Phase 2 2 + 3 + **Goal:** Run `nix develop` to resolve and pin all flake inputs, verify all required tools are present in the dev shell, then commit `flake.lock`. 4 + 5 + **Architecture:** Operational verification phase. The Nix flake inputs declared in Phase 1's `flake.nix` are resolved by Nix and pinned in `flake.lock`. The resulting devShell is verified by running each required tool inside it non-interactively via `nix develop --command`. 6 + 7 + **Tech Stack:** Nix 2.29.0 (installed, flakes + nix-command enabled in /etc/nix/nix.conf via nix-darwin), devenv Cachix substituter 8 + 9 + **Scope:** 2 phases total (Phase 2 of 2) 10 + 11 + **Codebase verified:** 2026-03-07 12 + 13 + --- 14 + 15 + ## Acceptance Criteria Coverage 16 + 17 + This phase implements and verifies: 18 + 19 + ### MM-64.AC1: Dev shell activates 20 + - **MM-64.AC1.1 Success:** `nix develop` completes without error on macOS (aarch64-darwin or x86_64-darwin) 21 + - **MM-64.AC1.2 Success:** `nix develop` completes without error on Linux (x86_64-linux) *(human verification — see notes below)* 22 + 23 + ### MM-64.AC2: Required tools are present in the shell 24 + - **MM-64.AC2.1 Success:** `rustc --version` outputs a stable Rust release (not nightly, not beta) 25 + - **MM-64.AC2.2 Success:** `cargo --version` is available 26 + - **MM-64.AC2.3 Success:** `rust-analyzer --version` is available 27 + - **MM-64.AC2.4 Success:** `cargo clippy --version` is available 28 + - **MM-64.AC2.5 Success:** `rustfmt --version` is available 29 + - **MM-64.AC2.6 Success:** `just --version` is available 30 + - **MM-64.AC2.7 Success:** `cargo audit --version` is available 31 + - **MM-64.AC2.8 Success:** `sqlite3 --version` is available and SQLite dev headers are accessible via `pkg-config` 32 + 33 + ### MM-64.AC3: Rust version is governed by rust-toolchain.toml 34 + - **MM-64.AC3.1 Success:** The Rust version inside the shell matches the `stable` channel as specified in `rust-toolchain.toml` 35 + - **MM-64.AC3.2 Success:** `rustup` on the same machine reads `rust-toolchain.toml` and resolves to the same Rust version without Nix *(human verification — see notes below)* 36 + 37 + ### MM-64.AC4: direnv integration is committed 38 + - **MM-64.AC4.1 Success:** `.envrc` contains `use flake` and is tracked by git (`git ls-files .envrc` returns it) 39 + - **MM-64.AC4.2 Success:** `direnv allow` in repo root activates the shell automatically on `cd` *(human verification — see notes below)* 40 + 41 + ### MM-64.AC5: flake.lock is committed 42 + - **MM-64.AC5.1 Success:** `flake.lock` exists at repo root and is tracked by git 43 + 44 + ### MM-64.AC6: Scope boundaries 45 + - **MM-64.AC6.1 Negative:** `flake.nix` defines no `packages` output (no build derivations) 46 + - **MM-64.AC6.2 Negative:** `flake.nix` defines no `nixosModules` output 47 + 48 + --- 49 + 50 + **Human verification required (cannot be automated on the local macOS machine):** 51 + - **MM-64.AC1.2:** Have a contributor or CI runner on `x86_64-linux` run `nix develop` and confirm it completes without error. 52 + - **MM-64.AC3.2:** On a machine with rustup installed (but without entering the Nix shell), `cd` to repo root and run `rustup show` — it should read `rust-toolchain.toml` and resolve to the stable channel matching the Nix shell's rustc version. 53 + - **MM-64.AC4.2:** On a machine with direnv installed and hooked into the shell (e.g., `eval "$(direnv hook zsh)"` in `.zshrc`), run `direnv allow` once in repo root, then `cd` out and back in — the devenv shell should activate automatically. 54 + 55 + --- 56 + 57 + <!-- START_TASK_1 --> 58 + ### Task 1: Run `nix develop` to generate `flake.lock` 59 + 60 + **Verifies:** MM-64.AC1.1 (partial — flake.lock generated; all tools verified in Task 2) 61 + 62 + **Prerequisite:** Phase 1 must be fully complete — `flake.nix`, `devenv.nix`, `rust-toolchain.toml`, `.envrc`, and `.gitignore` must all exist at repo root and be committed. 63 + 64 + **Files:** 65 + - Generates: `flake.lock` (auto-generated by Nix, not hand-written) 66 + 67 + **Step 1: Run `nix develop` from repo root** 68 + 69 + ```bash 70 + nix develop 71 + ``` 72 + 73 + **What to expect on first run:** 74 + - Nix resolves the three flake inputs (`devenv-nixpkgs/rolling`, `cachix/devenv`, `nix-systems/default`) and writes `flake.lock` before activating the shell. 75 + - Nix may prompt: *"do you want to allow configuration setting 'extra-substituters' to be set to 'https://devenv.cachix.org'?"* — answer **y** (yes, allow for this session or all sessions). 76 + - Nix may also prompt about the trusted public key `devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=` — answer **y**. 77 + - If the Cachix cache is warm, the shell typically activates in under a minute. A cold first run (cache miss) may take 10–20 minutes as devenv's dependencies are built from source. 78 + - A devenv welcome banner should appear when the shell is ready. 79 + - The `nix-command` and `flakes` experimental features are already enabled in `/etc/nix/nix.conf` — no extra flags are needed. 80 + 81 + **Step 2: Verify `flake.lock` was generated** 82 + 83 + While still inside the devenv shell (or in a second terminal): 84 + ```bash 85 + ls -la flake.lock 86 + ``` 87 + Expected: `flake.lock` exists at repo root with a nonzero file size. 88 + 89 + **Step 3: Exit the shell** 90 + 91 + ```bash 92 + exit 93 + ``` 94 + <!-- END_TASK_1 --> 95 + 96 + <!-- START_TASK_2 --> 97 + ### Task 2: Verify all required tools are present in the shell 98 + 99 + **Verifies:** MM-64.AC1.1, MM-64.AC2.1, MM-64.AC2.2, MM-64.AC2.3, MM-64.AC2.4, MM-64.AC2.5, MM-64.AC2.6, MM-64.AC2.7, MM-64.AC2.8, MM-64.AC3.1, MM-64.AC6.1, MM-64.AC6.2 100 + 101 + **Step 1: Run all tool version checks inside a non-interactive devenv shell** 102 + 103 + ```bash 104 + nix develop --command bash -c ' 105 + set -e 106 + echo "=== rustc ===" && rustc --version 107 + echo "=== cargo ===" && cargo --version 108 + echo "=== rust-analyzer ===" && rust-analyzer --version 109 + echo "=== clippy ===" && cargo clippy --version 110 + echo "=== rustfmt ===" && rustfmt --version 111 + echo "=== just ===" && just --version 112 + echo "=== cargo-audit ===" && cargo audit --version 113 + echo "=== sqlite3 ===" && sqlite3 --version 114 + echo "=== pkg-config sqlite3 ===" && pkg-config --libs sqlite3 115 + echo "=== All tools verified ===" 116 + ' 117 + ``` 118 + 119 + **Expected output (exact versions will vary — see per-tool criteria below):** 120 + 121 + ``` 122 + === rustc === 123 + rustc 1.XX.0 (... stable) 124 + === cargo === 125 + cargo 1.XX.0 (...) 126 + === rust-analyzer === 127 + rust-analyzer 1.XX.0-stable (...) 128 + === clippy === 129 + clippy 1.XX.0 (...) 130 + === rustfmt === 131 + rustfmt 1.XX.0-stable (...) 132 + === just === 133 + just 1.XX.X 134 + === cargo-audit === 135 + cargo-audit X.X.X 136 + === sqlite3 === 137 + 3.XX.X XXXX-XX-XX XX:XX:XX 138 + === pkg-config sqlite3 === 139 + -L/nix/store/.../lib -lsqlite3 140 + === All tools verified === 141 + ``` 142 + 143 + **Per-tool verification criteria:** 144 + - `rustc --version` output must contain `stable` (not `nightly` or `beta`) → **MM-64.AC2.1** 145 + - `cargo --version` must succeed → **MM-64.AC2.2** 146 + - `rust-analyzer --version` must succeed → **MM-64.AC2.3** 147 + - `cargo clippy --version` must succeed → **MM-64.AC2.4** 148 + - `rustfmt --version` must succeed → **MM-64.AC2.5** 149 + - `just --version` must succeed → **MM-64.AC2.6** 150 + - `cargo audit --version` must succeed → **MM-64.AC2.7** 151 + - `sqlite3 --version` must succeed AND `pkg-config --libs sqlite3` must print linker flags (e.g., `-L/nix/store/.../lib -lsqlite3`) → **MM-64.AC2.8** 152 + - The Rust channel in `rustc --version` must match `channel = "stable"` in `rust-toolchain.toml` → **MM-64.AC3.1** 153 + - The entire command must exit with code 0 → **MM-64.AC1.1** 154 + 155 + **Step 2: Verify scope boundaries in `flake.nix`** 156 + 157 + ```bash 158 + grep -E "^\s*packages\s*=" flake.nix && echo "FAIL: packages output found" || echo "OK: no packages output" 159 + grep "nixosModules" flake.nix && echo "FAIL: nixosModules found" || echo "OK: no nixosModules" 160 + ``` 161 + Expected: both lines print "OK" → **MM-64.AC6.1**, **MM-64.AC6.2** 162 + <!-- END_TASK_2 --> 163 + 164 + <!-- START_TASK_3 --> 165 + ### Task 3: Commit `flake.lock` 166 + 167 + **Verifies:** MM-64.AC4.1, MM-64.AC5.1 168 + 169 + **Step 1: Verify `.envrc` is tracked by git (committed in Phase 1)** 170 + 171 + ```bash 172 + git ls-files .envrc 173 + ``` 174 + Expected: `.envrc` printed → **MM-64.AC4.1** 175 + 176 + **Step 2: Stage `flake.lock`** 177 + 178 + ```bash 179 + git add flake.lock 180 + ``` 181 + 182 + **Step 3: Verify only `flake.lock` is staged** 183 + 184 + ```bash 185 + git status 186 + ``` 187 + Expected: `flake.lock` listed as "new file" under "Changes to be committed". No other unexpected files staged. 188 + 189 + **Step 4: Commit** 190 + 191 + ```bash 192 + git commit -m "$(cat <<'EOF' 193 + chore(MM-64): commit generated flake.lock 194 + 195 + Pins all Nix flake inputs to exact revisions. Generated by running 196 + nix develop for the first time with cachix/devenv + devenv-nixpkgs. 197 + EOF 198 + )" 199 + ``` 200 + 201 + Expected: commit message displayed, branch `MM-64` advanced by one commit. 202 + 203 + **Step 5: Verify `flake.lock` is now tracked by git** 204 + 205 + ```bash 206 + git ls-files flake.lock 207 + ``` 208 + Expected: `flake.lock` printed → **MM-64.AC5.1** 209 + <!-- END_TASK_3 -->
+386
docs/implementation-plans/2026-03-07-MM-64/test-requirements.md
··· 1 + # MM-64 Test Requirements 2 + 3 + **Ticket:** MM-64 — Nix Flake + devenv Development Shell 4 + **Phases:** 2 (Phase 1: configuration files, Phase 2: operational verification) 5 + **Test approach:** All verification is operational (command execution and output inspection). There are no unit tests — this is infrastructure-only. 6 + 7 + --- 8 + 9 + ## Automated Operational Tests 10 + 11 + These tests run inside a non-interactive Nix devShell via `nix develop --command`. Each test maps to a specific acceptance criterion and can be executed by a script or CI job on the target platform. 12 + 13 + ### Test 1: Dev shell activates on macOS 14 + 15 + **Criterion:** MM-64.AC1.1 16 + **Test type:** Operational — shell activation 17 + **What to run:** 18 + 19 + ```bash 20 + nix develop --command bash -c 'echo "shell activated"' 21 + ``` 22 + 23 + **Expected output:** The command exits with code 0 and prints `shell activated`. Any non-zero exit code or Nix evaluation error is a failure. 24 + 25 + **Rationale:** Phase 2, Task 1 generates `flake.lock` by running `nix develop`. Phase 2, Task 2 runs all tool checks inside the shell via `nix develop --command`. This test isolates the activation check from the tool checks. Only covers `aarch64-darwin` or `x86_64-darwin` depending on the machine running the test — Linux coverage requires separate verification (see Human Verification section). 26 + 27 + --- 28 + 29 + ### Test 2: rustc is stable 30 + 31 + **Criterion:** MM-64.AC2.1, MM-64.AC3.1 32 + **Test type:** Operational — version string assertion 33 + **What to run:** 34 + 35 + ```bash 36 + nix develop --command bash -c ' 37 + VERSION=$(rustc --version) 38 + echo "$VERSION" 39 + echo "$VERSION" | grep -qv "nightly" || { echo "FAIL: nightly detected"; exit 1; } 40 + echo "$VERSION" | grep -qv "beta" || { echo "FAIL: beta detected"; exit 1; } 41 + echo "OK: rustc is stable" 42 + ' 43 + ``` 44 + 45 + **Expected output:** A version string like `rustc 1.XX.0 (hash YYYY-MM-DD)` with no `nightly` or `beta` substring, followed by `OK: rustc is stable`. Exit code 0. 46 + 47 + **Rationale:** `rust-toolchain.toml` sets `channel = "stable"`. `devenv.nix` reads this file via `languages.rust.toolchainFile`. This test confirms the toolchain channel propagated correctly through devenv/rust-overlay. Satisfies both AC2.1 (stable release) and AC3.1 (version matches rust-toolchain.toml's stable channel). 48 + 49 + --- 50 + 51 + ### Test 3: cargo is available 52 + 53 + **Criterion:** MM-64.AC2.2 54 + **Test type:** Operational — command presence 55 + **What to run:** 56 + 57 + ```bash 58 + nix develop --command bash -c 'cargo --version' 59 + ``` 60 + 61 + **Expected output:** `cargo 1.XX.0 (hash YYYY-MM-DD)`. Exit code 0. 62 + 63 + **Rationale:** `cargo` is part of the Rust toolchain activated by `languages.rust.enable = true` in `devenv.nix`. 64 + 65 + --- 66 + 67 + ### Test 4: rust-analyzer is available 68 + 69 + **Criterion:** MM-64.AC2.3 70 + **Test type:** Operational — command presence 71 + **What to run:** 72 + 73 + ```bash 74 + nix develop --command bash -c 'rust-analyzer --version' 75 + ``` 76 + 77 + **Expected output:** A version string (e.g., `rust-analyzer 1.XX.0-stable (hash YYYY-MM-DD)`). Exit code 0. 78 + 79 + **Rationale:** `rust-toolchain.toml` lists `rust-analyzer` in `components`. devenv reads this file and includes the component via rust-overlay. 80 + 81 + --- 82 + 83 + ### Test 5: clippy is available 84 + 85 + **Criterion:** MM-64.AC2.4 86 + **Test type:** Operational — command presence 87 + **What to run:** 88 + 89 + ```bash 90 + nix develop --command bash -c 'cargo clippy --version' 91 + ``` 92 + 93 + **Expected output:** `clippy 0.1.XX (hash YYYY-MM-DD)`. Exit code 0. 94 + 95 + **Rationale:** `rust-toolchain.toml` lists `clippy` in `components`. 96 + 97 + --- 98 + 99 + ### Test 6: rustfmt is available 100 + 101 + **Criterion:** MM-64.AC2.5 102 + **Test type:** Operational — command presence 103 + **What to run:** 104 + 105 + ```bash 106 + nix develop --command bash -c 'rustfmt --version' 107 + ``` 108 + 109 + **Expected output:** `rustfmt 1.X.XX-stable (hash YYYY-MM-DD)`. Exit code 0. 110 + 111 + **Rationale:** `rust-toolchain.toml` lists `rustfmt` in `components`. 112 + 113 + --- 114 + 115 + ### Test 7: just is available 116 + 117 + **Criterion:** MM-64.AC2.6 118 + **Test type:** Operational — command presence 119 + **What to run:** 120 + 121 + ```bash 122 + nix develop --command bash -c 'just --version' 123 + ``` 124 + 125 + **Expected output:** `just 1.XX.X`. Exit code 0. 126 + 127 + **Rationale:** `devenv.nix` includes `pkgs.just` in the `packages` list. 128 + 129 + --- 130 + 131 + ### Test 8: cargo-audit is available 132 + 133 + **Criterion:** MM-64.AC2.7 134 + **Test type:** Operational — command presence 135 + **What to run:** 136 + 137 + ```bash 138 + nix develop --command bash -c 'cargo audit --version' 139 + ``` 140 + 141 + **Expected output:** `cargo-audit X.X.X`. Exit code 0. 142 + 143 + **Rationale:** `devenv.nix` includes `pkgs.cargo-audit` in the `packages` list. 144 + 145 + --- 146 + 147 + ### Test 9: sqlite3 and dev headers are available 148 + 149 + **Criterion:** MM-64.AC2.8 150 + **Test type:** Operational — command presence and pkg-config resolution 151 + **What to run:** 152 + 153 + ```bash 154 + nix develop --command bash -c ' 155 + set -e 156 + sqlite3 --version 157 + pkg-config --libs sqlite3 158 + echo "OK: sqlite3 runtime and dev headers available" 159 + ' 160 + ``` 161 + 162 + **Expected output:** A sqlite3 version string (e.g., `3.XX.X YYYY-MM-DD HH:MM:SS`), followed by linker flags (e.g., `-L/nix/store/.../lib -lsqlite3`), followed by `OK: sqlite3 runtime and dev headers available`. Exit code 0. 163 + 164 + **Rationale:** `devenv.nix` includes `pkgs.sqlite` and `pkgs.pkg-config` in the `packages` list. The `LIBSQLITE3_SYS_USE_PKG_CONFIG = "1"` env var tells `libsqlite3-sys` to use pkg-config. This test confirms both the runtime binary and dev headers are resolvable. 165 + 166 + --- 167 + 168 + ### Test 10: .envrc is tracked by git and contains `use flake` 169 + 170 + **Criterion:** MM-64.AC4.1 171 + **Test type:** Operational — git and file content check 172 + **What to run:** 173 + 174 + ```bash 175 + git ls-files .envrc | grep -q ".envrc" || { echo "FAIL: .envrc not tracked by git"; exit 1; } 176 + grep -q "use flake" .envrc || { echo "FAIL: .envrc does not contain 'use flake'"; exit 1; } 177 + echo "OK: .envrc tracked and contains 'use flake'" 178 + ``` 179 + 180 + **Expected output:** `OK: .envrc tracked and contains 'use flake'`. Exit code 0. 181 + 182 + **Rationale:** Phase 1, Task 4 creates `.envrc` with `use flake`. Phase 1, Task 6 commits it. This test does not require entering the Nix shell — it verifies git state and file content directly. 183 + 184 + --- 185 + 186 + ### Test 11: flake.lock is tracked by git 187 + 188 + **Criterion:** MM-64.AC5.1 189 + **Test type:** Operational — git check 190 + **What to run:** 191 + 192 + ```bash 193 + git ls-files flake.lock | grep -q "flake.lock" || { echo "FAIL: flake.lock not tracked by git"; exit 1; } 194 + echo "OK: flake.lock tracked by git" 195 + ``` 196 + 197 + **Expected output:** `OK: flake.lock tracked by git`. Exit code 0. 198 + 199 + **Rationale:** Phase 2, Task 1 generates `flake.lock` via `nix develop`. Phase 2, Task 3 commits it. This test confirms the file made it into git. 200 + 201 + --- 202 + 203 + ### Test 12: flake.nix defines no packages output 204 + 205 + **Criterion:** MM-64.AC6.1 206 + **Test type:** Operational — static file analysis 207 + **What to run:** 208 + 209 + ```bash 210 + grep -E '^\s*packages\s*=' flake.nix && { echo "FAIL: packages output found in flake.nix"; exit 1; } || echo "OK: no packages output" 211 + ``` 212 + 213 + **Expected output:** `OK: no packages output`. Exit code 0. 214 + 215 + **Rationale:** The design explicitly scopes MM-64 to devShells only. Phase 1, Task 1 creates `flake.nix` with only a `devShells` output. This grep confirms no `packages` output was accidentally introduced. 216 + 217 + --- 218 + 219 + ### Test 13: flake.nix defines no nixosModules output 220 + 221 + **Criterion:** MM-64.AC6.2 222 + **Test type:** Operational — static file analysis 223 + **What to run:** 224 + 225 + ```bash 226 + grep 'nixosModules' flake.nix && { echo "FAIL: nixosModules found in flake.nix"; exit 1; } || echo "OK: no nixosModules" 227 + ``` 228 + 229 + **Expected output:** `OK: no nixosModules`. Exit code 0. 230 + 231 + **Rationale:** Same scope boundary as AC6.1. NixOS modules are explicitly out of scope for MM-64. This grep confirms the absence. 232 + 233 + --- 234 + 235 + ### Combined Automated Test Script 236 + 237 + All automated tests above can be run sequentially as a single validation script. The script should be executed from the repo root after both Phase 1 and Phase 2 are complete. 238 + 239 + ```bash 240 + #!/usr/bin/env bash 241 + set -euo pipefail 242 + 243 + PASS=0 244 + FAIL=0 245 + 246 + run_test() { 247 + local name="$1" 248 + shift 249 + echo "--- $name ---" 250 + if "$@"; then 251 + echo "PASS: $name" 252 + ((PASS++)) 253 + else 254 + echo "FAIL: $name" 255 + ((FAIL++)) 256 + fi 257 + echo 258 + } 259 + 260 + # Tests that do not require the Nix shell 261 + run_test "AC4.1: .envrc tracked with use flake" bash -c ' 262 + git ls-files .envrc | grep -q ".envrc" && grep -q "use flake" .envrc 263 + ' 264 + 265 + run_test "AC5.1: flake.lock tracked by git" bash -c ' 266 + git ls-files flake.lock | grep -q "flake.lock" 267 + ' 268 + 269 + run_test "AC6.1: no packages output" bash -c ' 270 + ! grep -qE "^\s*packages\s*=" flake.nix 271 + ' 272 + 273 + run_test "AC6.2: no nixosModules output" bash -c ' 274 + ! grep -q "nixosModules" flake.nix 275 + ' 276 + 277 + # Tests that require the Nix shell 278 + run_test "AC1.1: dev shell activates" \ 279 + nix develop --command bash -c 'echo "shell activated"' 280 + 281 + run_test "AC2.1+AC3.1: rustc is stable" \ 282 + nix develop --command bash -c ' 283 + V=$(rustc --version) 284 + echo "$V" 285 + echo "$V" | grep -qv nightly && echo "$V" | grep -qv beta 286 + ' 287 + 288 + run_test "AC2.2: cargo available" \ 289 + nix develop --command bash -c 'cargo --version' 290 + 291 + run_test "AC2.3: rust-analyzer available" \ 292 + nix develop --command bash -c 'rust-analyzer --version' 293 + 294 + run_test "AC2.4: clippy available" \ 295 + nix develop --command bash -c 'cargo clippy --version' 296 + 297 + run_test "AC2.5: rustfmt available" \ 298 + nix develop --command bash -c 'rustfmt --version' 299 + 300 + run_test "AC2.6: just available" \ 301 + nix develop --command bash -c 'just --version' 302 + 303 + run_test "AC2.7: cargo-audit available" \ 304 + nix develop --command bash -c 'cargo audit --version' 305 + 306 + run_test "AC2.8: sqlite3 + pkg-config" \ 307 + nix develop --command bash -c 'sqlite3 --version && pkg-config --libs sqlite3' 308 + 309 + echo "=========================" 310 + echo "PASS: $PASS FAIL: $FAIL" 311 + [ "$FAIL" -eq 0 ] || exit 1 312 + ``` 313 + 314 + --- 315 + 316 + ## Human Verification 317 + 318 + The following acceptance criteria cannot be fully automated on the local development machine. Each includes justification and a verification approach. 319 + 320 + ### HV-1: Dev shell activates on Linux (x86_64-linux) 321 + 322 + **Criterion:** MM-64.AC1.2 323 + **Why it cannot be automated locally:** The local development machine is macOS (darwin). Verifying `nix develop` on `x86_64-linux` requires either a Linux machine or a Linux CI runner. The flake declares support for all systems returned by `nix-systems/default` (including `x86_64-linux`), but the Nix evaluation and package availability can only be confirmed by actually running on that platform. 324 + 325 + **Verification approach:** 326 + 1. A contributor with access to a `x86_64-linux` machine clones the repo and runs: 327 + ```bash 328 + nix develop --command bash -c 'rustc --version && echo "Linux shell OK"' 329 + ``` 330 + 2. Alternatively, a CI pipeline (e.g., GitHub Actions with `ubuntu-latest` and Nix installed) runs the combined automated test script above. 331 + 3. Pass condition: exit code 0 and `Linux shell OK` printed. 332 + 333 + --- 334 + 335 + ### HV-2: rustup reads rust-toolchain.toml without Nix 336 + 337 + **Criterion:** MM-64.AC3.2 338 + **Why it cannot be automated locally:** This test must run *outside* the Nix shell on a machine with `rustup` installed. The automated test harness runs inside `nix develop --command`, which sets up the Nix-managed toolchain and shadows rustup. Testing rustup's behavior requires a deliberate exit from the Nix environment, which conflicts with the automated test runner's context. 339 + 340 + **Verification approach:** 341 + 1. Ensure rustup is installed on the machine (outside Nix). 342 + 2. From the repo root, without entering the Nix shell, run: 343 + ```bash 344 + rustup show 345 + ``` 346 + 3. Pass condition: output includes a line referencing `stable` and showing the same major.minor Rust version as `rustc --version` inside the Nix shell. The `rust-toolchain.toml` file should appear in the output as the active override source. 347 + 348 + --- 349 + 350 + ### HV-3: direnv activates the shell automatically on `cd` 351 + 352 + **Criterion:** MM-64.AC4.2 353 + **Why it cannot be automated locally:** direnv activation is a shell-level integration that requires an interactive shell session with direnv hooked in (e.g., `eval "$(direnv hook zsh)"` in `.zshrc`). Non-interactive `bash -c` invocations do not load the direnv hook, so the automatic activation on `cd` cannot be observed programmatically. 354 + 355 + **Verification approach:** 356 + 1. Ensure direnv is installed and hooked into the shell (`eval "$(direnv hook zsh)"` or equivalent in shell RC file). 357 + 2. From the repo root, run: 358 + ```bash 359 + direnv allow 360 + ``` 361 + 3. `cd` out of the repo directory, then `cd` back in. 362 + 4. Pass condition: direnv prints a loading/activation message, and `which rustc` points to a path inside `/nix/store/` (confirming the devShell is active). 363 + 364 + --- 365 + 366 + ## Acceptance Criteria Traceability Matrix 367 + 368 + | Criterion | Description | Test Type | Test ID | 369 + |-----------|-------------|-----------|---------| 370 + | MM-64.AC1.1 | `nix develop` succeeds on macOS | Automated | Test 1 | 371 + | MM-64.AC1.2 | `nix develop` succeeds on Linux | Human | HV-1 | 372 + | MM-64.AC2.1 | `rustc` is stable | Automated | Test 2 | 373 + | MM-64.AC2.2 | `cargo` available | Automated | Test 3 | 374 + | MM-64.AC2.3 | `rust-analyzer` available | Automated | Test 4 | 375 + | MM-64.AC2.4 | `clippy` available | Automated | Test 5 | 376 + | MM-64.AC2.5 | `rustfmt` available | Automated | Test 6 | 377 + | MM-64.AC2.6 | `just` available | Automated | Test 7 | 378 + | MM-64.AC2.7 | `cargo-audit` available | Automated | Test 8 | 379 + | MM-64.AC2.8 | `sqlite3` + pkg-config headers | Automated | Test 9 | 380 + | MM-64.AC3.1 | Rust version matches rust-toolchain.toml stable | Automated | Test 2 | 381 + | MM-64.AC3.2 | rustup reads rust-toolchain.toml without Nix | Human | HV-2 | 382 + | MM-64.AC4.1 | `.envrc` tracked with `use flake` | Automated | Test 10 | 383 + | MM-64.AC4.2 | `direnv allow` activates shell on `cd` | Human | HV-3 | 384 + | MM-64.AC5.1 | `flake.lock` tracked by git | Automated | Test 11 | 385 + | MM-64.AC6.1 | No `packages` output in flake.nix | Automated | Test 12 | 386 + | MM-64.AC6.2 | No `nixosModules` output in flake.nix | Automated | Test 13 |
+188
docs/v01-issue-plan.md
··· 1 + # v0.1 Linear Issue Plan 2 + 3 + Proposed breakdown for the ezpds v0.1 milestone. 4 + Each issue is scoped to be completable in hours by a human or within an agent's context window. 5 + 6 + Issues are organized into waves for incremental deployability. Each wave produces a running system you can test. 7 + 8 + --- 9 + 10 + ## Wave 0 — Project Scaffolding 11 + 12 + Checkpoint: `nix develop` drops into a shell with Rust toolchain, `cargo build` succeeds, `just check` runs, CI is green on an empty workspace, `nix build` produces a relay binary. 13 + 14 + ### Dependency graph 15 + 16 + ``` 17 + MM-63 (Cargo workspace) ──┬──> MM-64 (devenv) ──> MM-68 (CI) ⚠️ human-preferred 18 + ├──> MM-65 (Nix build) ──┬──> MM-66 (Docker) 19 + │ └──> MM-135 (NixOS module) 20 + ├──> MM-67 (Justfile) ↑ 21 + ├──> MM-69 (Config) ──────────┘ 22 + └──> MM-70 (Error types) 23 + 24 + All ──> MM-136 (Checkpoint validation) 25 + ``` 26 + 27 + ### Issues 28 + 29 + 1. **MM-63** — Cargo workspace setup (root Cargo.toml, crate directories: relay, repo-engine, crypto, common) 30 + 2. **MM-64** — Nix flake + devenv — dev shell with Rust toolchain, SQLite, just, cargo-audit, clippy; `nix develop` is the single entry point for contributors. *Blocked by: MM-63* 31 + 3. **MM-65** — Nix flake — relay build output (`nix build .#relay`). *Blocked by: MM-63* 32 + 4. **MM-135** — NixOS module for relay deployment (systemd service, relay.toml config options, user/group). *Blocked by: MM-65, MM-69* 33 + 5. **MM-66** — Docker image derived from Nix build (`nix build .#docker-image`) — single-layer OCI image, minimal base. *Blocked by: MM-65* 34 + 6. **MM-67** — Justfile with recipes: check, build, test, fmt, clippy, run-relay, nix-build, docker-build. *Blocked by: MM-63* 35 + 7. **MM-68** — Tangled CI pipeline (.tangled/workflows/ — build, test, clippy, fmt, cargo-audit; Nix-native via Nixery). ⚠️ Human-preferred — spindle YAML format is niche. *Blocked by: MM-64* 36 + 8. **MM-69** — Configuration system (relay.toml parsing, env var overrides, crate: common). *Blocked by: MM-63* 37 + 9. **MM-70** — Error types and shared API envelope (crate: common). *Blocked by: MM-63* 38 + 10. **MM-136** — Wave 0 checkpoint validation — run through all checkpoint criteria on a clean checkout. *Blocked by: all above* 39 + 40 + --- 41 + 42 + ## Wave 1 — Running Server 43 + 44 + Checkpoint: `curl localhost:port/_health` returns OK. `describeServer` returns valid ATProto server metadata. 45 + 46 + 9. Axum HTTP server skeleton + XRPC routing (crate: relay) 47 + 10. SQLite schema v1 (accounts, devices, sessions, repos, blobs, oauth tables) 48 + 11. XRPC: _health endpoint 49 + 12. XRPC: com.atproto.server.describeServer 50 + 51 + --- 52 + 53 + ## Wave 2 — Auth 54 + 55 + Checkpoint: Bluesky app can complete OAuth flow and receive tokens. createSession/getSession work. 56 + 57 + 13. atproto-oauth-axum integration + server metadata endpoint (/.well-known/oauth-authorization-server) 58 + 14. OAuth: Authorization endpoint + minimal server-rendered consent UI 59 + 15. OAuth: Token endpoint (DPoP, PKCE, refresh rotation) 60 + 16. OAuth: PAR endpoint 61 + 17. OAuth: JWKS endpoint 62 + 18. XRPC: com.atproto.server.createSession 63 + 19. XRPC: com.atproto.server.getSession 64 + 20. XRPC: com.atproto.server.refreshSession 65 + 66 + --- 67 + 68 + ## Wave 3 — Account Creation + Identity 69 + 70 + Checkpoint: Mobile app can create account, DID is resolvable, handle works. User can log into Bluesky. 71 + 72 + 21. Provisioning: POST /v1/accounts (web dashboard account creation) 73 + 22. Provisioning: POST /v1/accounts/mobile (combined mobile account creation + device binding) 74 + 23. Provisioning: POST /v1/accounts/sessions (login) 75 + 24. Provisioning: POST /v1/accounts/claim-codes 76 + 25. Provisioning: POST /v1/devices (device registration via claim code) 77 + 26. Provisioning: GET /v1/devices/:id/relay (relay endpoint discovery) 78 + 27. DID creation — did:plc via PLC directory proxy (crate: crypto) 79 + 28. Provisioning: POST /v1/dids (DID ceremony endpoint) 80 + 29. Provisioning: GET /v1/dids/:did (DID document retrieval) 81 + 30. Relay signing key generation — POST /v1/relay/keys (crate: crypto) 82 + 31. Shamir 2-of-3 share generation at onboarding (crate: crypto) 83 + 32. Provisioning: POST /v1/handles (handle creation + subdomain DNS) 84 + 33. Provisioning: GET /v1/handles/:handle/status (DNS propagation polling) 85 + 34. Provisioning: DELETE /v1/handles/:handle 86 + 35. XRPC: com.atproto.identity.resolveHandle 87 + 88 + --- 89 + 90 + ## Wave 4 — Repo CRUD + Blobs 91 + 92 + Checkpoint: User can create a post via Bluesky. Records stored in repo, blobs uploadable. 93 + 94 + 36. Repo engine — MST construction + CAR storage (crate: repo-engine) 95 + 37. Repo engine — commit construction + signing integration (crate: repo-engine) 96 + 38. XRPC: com.atproto.repo.createRecord 97 + 39. XRPC: com.atproto.repo.putRecord 98 + 40. XRPC: com.atproto.repo.deleteRecord 99 + 41. XRPC: com.atproto.repo.applyWrites 100 + 42. XRPC: com.atproto.repo.getRecord 101 + 43. XRPC: com.atproto.repo.listRecords 102 + 44. XRPC: com.atproto.repo.describeRepo 103 + 45. Blob storage backend — local FS, CID generation, MIME sniffing (crate: relay) 104 + 46. XRPC: com.atproto.repo.uploadBlob 105 + 47. XRPC: com.atproto.sync.getBlob 106 + 48. XRPC: com.atproto.sync.listBlobs 107 + 49. Blob garbage collection — 6-hour temp cleanup, dereferenced check 108 + 109 + --- 110 + 111 + ## Wave 5 — Federation 112 + 113 + Checkpoint: Relay emits firehose. BGS can subscribe. Posts appear in Bluesky AppView. The relay is a federating PDS. 114 + 115 + 50. Firehose emitter — subscribeRepos WebSocket event stream (crate: relay) 116 + 51. XRPC: com.atproto.sync.subscribeRepos (WebSocket handler) 117 + 52. XRPC: com.atproto.sync.getRepo (full CAR export) 118 + 53. XRPC: com.atproto.sync.getRecord (single record as CAR) 119 + 54. XRPC: com.atproto.sync.listRepos 120 + 55. XRPC: com.atproto.sync.getRepoStatus 121 + 56. requestCrawl trigger on new content 122 + 57. Iroh tunnel integration — relay endpoint for device connections (crate: relay) 123 + 124 + --- 125 + 126 + ## Wave 6 — App Proxy + Remaining Endpoints 127 + 128 + Checkpoint: Full v0.1 XRPC surface. User preferences persist locally. Chat endpoints proxied. 129 + 130 + 58. XRPC: app.bsky.* catch-all proxy to appview (bsky.network) 131 + 59. XRPC: app.bsky.actor.getPreferences (local storage) 132 + 60. XRPC: app.bsky.actor.putPreferences (local storage) 133 + 61. XRPC: chat.bsky.convo.getLog (proxy) 134 + 62. XRPC: chat.bsky.convo.listConvos (proxy) 135 + 63. XRPC: com.atproto.server.activateAccount 136 + 64. XRPC: com.atproto.server.deactivateAccount 137 + 65. Provisioning: GET /v1/accounts/:id/usage 138 + 66. Provisioning: GET /v1/accounts/:id/storage (blob metrics) 139 + 140 + --- 141 + 142 + ## Wave 7 — Transfer, Testing, Hardening 143 + 144 + Checkpoint: Planned device swap works. Interop tests pass. CI catches regressions. 145 + 146 + 67. Provisioning: POST /v1/transfer/initiate 147 + 68. Provisioning: POST /v1/transfer/accept 148 + 69. Provisioning: POST /v1/transfer/complete 149 + 70. L1 interop test vectors (atproto-interop-tests + interop-test-files) 150 + 71. cargo-audit in CI (already in Wave 0 CI, this adds policy + dep review) 151 + 72. Tauri IPC lockdown — minimal allowlist definition 152 + 153 + --- 154 + 155 + ## Notes 156 + 157 + - Some issues span both an XRPC endpoint and a backend concern (e.g., uploadBlob is XRPC handler in Wave 4, blob storage backend is a separate issue). The XRPC issue implements the handler; the backend issue implements the storage layer it calls. 158 + - subscribeRepos has two issues: the firehose emission pipeline (Wave 5) and the WebSocket handler that serves it. 159 + - DID creation spans the crypto crate (PLC directory integration) and the provisioning API endpoint (POST /v1/dids). 160 + - Wave 0's CI pipeline includes cargo-audit; Wave 7's cargo-audit issue adds dep review policy and Cargo.lock diff checks. 161 + - Total: 74 issues across 8 waves (72 original + MM-135 NixOS module split + MM-136 checkpoint validation). 162 + 163 + ## Crate Structure 164 + 165 + ``` 166 + ezpds/ 167 + ├── Justfile 168 + ├── Cargo.toml (workspace root) 169 + ├── flake.nix (devenv, build outputs, NixOS module, Docker image) 170 + ├── flake.lock 171 + ├── devenv.nix (dev shell configuration) 172 + ├── crates/ 173 + │ ├── relay/ (Axum server, XRPC, OAuth, provisioning API, blob storage) 174 + │ ├── repo-engine/ (MST, CAR, commit construction — atrium-repo integration) 175 + │ ├── crypto/ (signing, Shamir, DID operations — rsky-crypto integration) 176 + │ ├── common/ (shared types, error envelope, config parsing) 177 + │ └── app-desktop/ (Tauri shell — v0.2, not in scope for v0.1) 178 + ├── nix/ 179 + │ ├── module.nix (NixOS module for relay deployment) 180 + │ └── docker.nix (Docker image derivation from Nix build) 181 + ├── .tangled/workflows/ (CI pipelines — Nix-native via Nixery) 182 + ├── docs/ (specs, this plan) 183 + └── tests/ (integration tests) 184 + ``` 185 + 186 + ## License 187 + 188 + TBD — decision deferred to pre-v1.0. Options under consideration: AGPL-3.0, Apache-2.0/MIT dual, or BSL-style.
+16
flake.lock
··· 33 33 "type": "github" 34 34 } 35 35 }, 36 + "crane": { 37 + "locked": { 38 + "lastModified": 1772560058, 39 + "narHash": "sha256-NuVKdMBJldwUXgghYpzIWJdfeB7ccsu1CC7B+NfSoZ8=", 40 + "owner": "ipetkov", 41 + "repo": "crane", 42 + "rev": "db590d9286ed5ce22017541e36132eab4e8b3045", 43 + "type": "github" 44 + }, 45 + "original": { 46 + "owner": "ipetkov", 47 + "repo": "crane", 48 + "type": "github" 49 + } 50 + }, 36 51 "devenv": { 37 52 "inputs": { 38 53 "cachix": "cachix", ··· 298 313 }, 299 314 "root": { 300 315 "inputs": { 316 + "crane": "crane", 301 317 "devenv": "devenv", 302 318 "nixpkgs": "nixpkgs_2", 303 319 "rust-overlay": "rust-overlay",
+33 -1
flake.nix
··· 13 13 systems.url = "github:nix-systems/default"; 14 14 rust-overlay.url = "github:oxalica/rust-overlay"; 15 15 rust-overlay.inputs = { nixpkgs.follows = "nixpkgs"; }; 16 + crane.url = "github:ipetkov/crane"; 16 17 }; 17 18 18 - outputs = { self, nixpkgs, devenv, systems, rust-overlay, ... } @ inputs: 19 + outputs = { self, nixpkgs, devenv, systems, rust-overlay, crane, ... } @ inputs: 19 20 let 20 21 forEachSystem = f: nixpkgs.lib.genAttrs (import systems) f; 21 22 in { 23 + packages = forEachSystem (system: 24 + let 25 + pkgs = import nixpkgs { 26 + inherit system; 27 + overlays = [ (import rust-overlay) ]; 28 + }; 29 + rustToolchain = pkgs.rust-bin.stable.latest.default; 30 + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; 31 + 32 + commonArgs = { 33 + src = craneLib.cleanCargoSource ./.; 34 + pname = "relay"; 35 + strictDeps = true; 36 + nativeBuildInputs = [ pkgs.pkg-config ]; 37 + buildInputs = [ pkgs.sqlite ]; 38 + LIBSQLITE3_SYS_USE_PKG_CONFIG = "1"; 39 + }; 40 + 41 + # Build deps separately so they're cached when only source changes. 42 + cargoArtifacts = craneLib.buildDepsOnly commonArgs; 43 + 44 + relay = craneLib.buildPackage (commonArgs // { 45 + inherit cargoArtifacts; 46 + cargoExtraArgs = "--package relay"; 47 + }); 48 + in { 49 + inherit relay; 50 + default = relay; 51 + } 52 + ); 53 + 22 54 devShells = forEachSystem (system: 23 55 let 24 56 pkgs = nixpkgs.legacyPackages.${system};

History

3 rounds 0 comments
sign up or login to add to the discussion
3 commits
expand
fix(envrc): pass --impure to fix direnv shell activation; add docs
feat(MM-65): add nix build output for relay binary using crane
fix(MM-65): address PR review — toolchain parity and CLAUDE.md
expand 0 comments
pull request successfully merged
2 commits
expand
fix(envrc): pass --impure to fix direnv shell activation; add docs
feat(MM-65): add nix build output for relay binary using crane
expand 0 comments
1 commit
expand
fix(envrc): pass --impure to fix direnv shell activation; add docs
expand 0 comments