1{ 2 description = "lmjtfy — let me Jev that for you: a Rust Cloudflare Worker that puts typed questions to Jev (TypeSafe AI) and shows the call"; 3 4 inputs = { 5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 7 # The Rust toolchain comes from fenix, as in ~/jevsnes: one stable 8 # toolchain with the wasm32-unknown-unknown rust-std the Worker builds for. 9 fenix = { 10 url = "github:nix-community/fenix"; 11 inputs.nixpkgs.follows = "nixpkgs"; 12 }; 13 14 # The estate's package set and facts, for the Cloudflare credentials: 15 # wrangler runs through a wrapper that reads the API token from 1Password 16 # per run, as ~/jevstrudel's does. Nothing is logged in and nothing is on disk. 17 nix-pkgs.url = "git+ssh://git@github.com/deizel/nix-pkgs"; 18 nix-pkgs.inputs.nixpkgs.follows = "nixpkgs"; 19 nix-facts.url = "git+ssh://git@github.com/deizel/nix-facts"; 20 }; 21 22 outputs = 23 { 24 nixpkgs, 25 fenix, 26 nix-pkgs, 27 nix-facts, 28 ... 29 }: 30 let 31 system = "x86_64-linux"; 32 pkgs = nixpkgs.legacyPackages.${system}; 33 rust = fenix.packages.${system}; 34 35 rustToolchain = rust.combine [ 36 rust.stable.rustc 37 rust.stable.cargo 38 rust.stable.clippy 39 rust.stable.rustfmt 40 rust.stable.rust-src 41 rust.stable.rust-analyzer 42 rust.targets.wasm32-unknown-unknown.stable.rust-std 43 ]; 44 45 cloudflare = { 46 itemRef = nix-facts.facts.onePassword.cloudflareMasterKey; 47 accountId = nix-facts.facts.cloudflare.accounts.deizel; 48 }; 49 # `lmjtfy-wrangler <dir> [wrangler args]`: wrangler with the account's 50 # token in its environment. Workers AI has no local emulation, so even 51 # `dev` needs it once the Worker calls a model. 52 wrangler = nix-pkgs.lib.infra.mkWranglerDeploy { 53 inherit pkgs; 54 inherit (cloudflare) itemRef accountId; 55 name = "lmjtfy-wrangler"; 56 }; 57 58 # `lmjtfy-eval [args]`: tools/eval with the account, and where in 59 # 1Password its token is, in its own environment only. The reference is 60 # an `op://` URL, and `op-env-run` resolves every such value it finds 61 # in the environment it is run from, with an account that cannot read 62 # this one. So it must not be a devshell variable (2026-10-02: it was, 63 # and the dev server's op-env-run refused to start). 64 evalTool = pkgs.writeShellApplication { 65 name = "lmjtfy-eval"; 66 text = '' 67 export CLOUDFLARE_ACCOUNT_ID=${cloudflare.accountId} 68 export LMJTFY_OP_ITEM_REF=${pkgs.lib.escapeShellArg cloudflare.itemRef} 69 exec cargo run -q -p eval -- "$@" 70 ''; 71 }; 72 73 # `lmjtfy-secret <which>`: sets one of the deployed Worker's secrets. 74 # op-env-run -- lmjtfy-secret jev LMJTFY_TYPESAFE_API_KEY, from the environment 75 # lmjtfy-secret account CLOUDFLARE_ACCOUNT_ID, from the estate's facts 76 # … | lmjtfy-secret analytics CLOUDFLARE_ANALYTICS_TOKEN, from stdin: 77 # nix run ~/config#infra-core -- output -raw lmjtfy-analytics-token-value | tail -n 1 | lmjtfy-secret analytics 78 # op-env-run -- lmjtfy-secret github LMJTFY_GITHUB_TOKEN, from the environment: the clone 79 # proxy's read-only fine-grained token (made on GitHub; the API cannot mint one) 80 # 81 # Not `printf value | lmjtfy-wrangler … secret put`: that wrapper runs 82 # op.exe to fetch the Cloudflare token before it runs wrangler, op.exe 83 # reads the stdin it is given, and the value is gone by the time 84 # wrangler looks. That uploaded an empty secret on 2026-10-02 and the 85 # live site said Jev was offline. Here op gets no stdin. 86 secretTool = pkgs.writeShellApplication { 87 name = "lmjtfy-secret"; 88 runtimeInputs = [ pkgs.wrangler ]; 89 text = '' 90 case "''${1:-}" in 91 jev) 92 name=LMJTFY_TYPESAFE_API_KEY 93 value=''${LMJTFY_TYPESAFE_API_KEY:-} 94 ;; 95 account) 96 name=CLOUDFLARE_ACCOUNT_ID 97 value=${cloudflare.accountId} 98 ;; 99 analytics) 100 name=CLOUDFLARE_ANALYTICS_TOKEN 101 value=$(cat) 102 ;; 103 github) 104 name=LMJTFY_GITHUB_TOKEN 105 value=''${LMJTFY_GITHUB_TOKEN:-} 106 ;; 107 *) 108 echo "usage: lmjtfy-secret jev | account | analytics | github" >&2 109 exit 2 110 ;; 111 esac 112 if [ -z "$value" ]; then 113 echo "lmjtfy-secret: no value for $name" >&2 114 exit 1 115 fi 116 op=op 117 if command -v op.exe >/dev/null; then op=op.exe; fi 118 CLOUDFLARE_API_TOKEN=$("$op" read ${pkgs.lib.escapeShellArg cloudflare.itemRef}/Token </dev/null | tr -d '\r') 119 if [ -z "$CLOUDFLARE_API_TOKEN" ]; then 120 echo "lmjtfy-secret: no Cloudflare token from 1Password" >&2 121 exit 1 122 fi 123 export CLOUDFLARE_API_TOKEN 124 export CLOUDFLARE_ACCOUNT_ID=${cloudflare.accountId} 125 cd "$(git rev-parse --show-toplevel)/apps/lmjtfy" 126 printf %s "$value" | wrangler secret put "$name" 127 ''; 128 }; 129 130 # Everything the Worker builds and tests with, from public inputs only. 131 # Nix fetches a locked input only when an output uses it (measured on 132 # Nix 2.34, 2026-10-02), so anyone who clones can enter this shell 133 # without access to nix-pkgs or nix-facts. 134 public = [ 135 rustToolchain 136 137 # The Worker build: worker-build runs cargo for wasm32, then 138 # wasm-bindgen, then writes build/index.js for wrangler. 139 pkgs.worker-build 140 pkgs.wasm-bindgen-cli 141 pkgs.binaryen 142 pkgs.esbuild 143 pkgs.wrangler 144 145 pkgs.curl 146 pkgs.jq 147 ]; 148 149 # worker-build otherwise downloads its own wasm-bindgen, wasm-opt and 150 # esbuild into a cache. These are nix's. Cargo.toml pins the 151 # wasm-bindgen crate to this CLI's exact version, because the two 152 # must match. 153 buildTools = { 154 WASM_BINDGEN_BIN = "${pkgs.wasm-bindgen-cli}/bin/wasm-bindgen"; 155 WASM_OPT_BIN = "${pkgs.binaryen}/bin/wasm-opt"; 156 ESBUILD_BIN = "${pkgs.esbuild}/bin/esbuild"; 157 }; 158 in 159 { 160 packages.${system}.wrangler = wrangler; 161 162 devShells.${system} = { 163 # `nix develop`: build and test. 164 default = pkgs.mkShell ({ packages = public; } // buildTools); 165 166 # `nix develop .#owner`: the same, and the owner's tools, which read 167 # the Cloudflare account and the 1Password item from the private 168 # nix-facts: the credentialed wrangler, the secrets, the eval, and the 169 # pitchfork that supervises the dev server (from nix-pkgs, which 170 # carries a newer release than nixpkgs). 171 owner = pkgs.mkShell ( 172 { 173 packages = public ++ [ 174 wrangler 175 secretTool 176 evalTool 177 nix-pkgs.packages.${system}.pitchfork 178 ]; 179 } 180 // buildTools 181 ); 182 }; 183 }; 184}