diff --git a/.gitignore b/.gitignore index a699ab3..666ca4a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ /result /result/ /*.qcow2 -/server.md \ No newline at end of file +/server.md +r2-upload-api.md \ No newline at end of file diff --git a/deploy.md b/deploy.md deleted file mode 100644 index fcee793..0000000 --- a/deploy.md +++ /dev/null @@ -1,47 +0,0 @@ -Yes — but you need **NixOS**, not just “minimal Nix.” Here’s the pragmatic path based on your flake: - -**What’s missing right now** -- There is **no hardware config** in the repo. You must generate one on the server and include it. -- `sops-nix` expects the **age key file** `secrets/age/keys.txt` to exist **in your local checkout at build time** (it’s ignored, so you must place it manually). - -**Recommended install flow (fresh NixOS install)** -1. Boot the NixOS minimal ISO on the server and partition/mount as usual. -2. Generate hardware config: - ```bash - nixos-generate-config --root /mnt - ``` -3. Clone this repo into `/mnt/etc/nixos` (or `/mnt/etc/nixos/bucur`). -4. Move the generated hardware file into the repo: - ```bash - mv /mnt/etc/nixos/hardware-configuration.nix /mnt/etc/nixos/hosts/bucur-hardware.nix - ``` -5. Add it to the flake modules list and ignore it: - - Add `./hosts/bucur-hardware.nix` to `modules` in `flake.nix`. - - Add `/hosts/bucur-hardware.nix` to `.gitignore`. -6. Place your age key in the repo (local‑only): - ```bash - install -d -m 700 /mnt/etc/nixos/secrets/age - install -m 600 /path/to/keys.txt /mnt/etc/nixos/secrets/age/keys.txt - ``` -7. Install using the flake: - ```bash - nixos-install --flake /mnt/etc/nixos#bucur - ``` -8. Reboot, then SSH in as `tobias` using the key in `modules/users/tobias.nix`. - -**Alternative (remote rebuild on existing NixOS)** -- If the server is already running NixOS and you can SSH: - ```bash - nixos-rebuild switch --flake /path/to/repo#bucur - ``` - -**Important gotchas** -- `services.openssh` disables passwords, so you must have the correct SSH key in `modules/users/tobias.nix`. -- `sops-nix` will fail unless `secrets/age/keys.txt` exists in the checkout used to build. -- `server.md` is ignored and no longer part of the repo/history. - -If you want, I can: -1. Add a `hosts/bucur-hardware.nix` placeholder + `.gitignore` entry now. -2. Give you a copy‑paste install script tailored to your disk layout. - -Tell me if the server is **fresh** or already running NixOS, and how you want to deploy (local install vs remote rebuild). \ No newline at end of file diff --git a/flake.nix b/flake.nix index 04106d6..ba0ef6c 100644 --- a/flake.nix +++ b/flake.nix @@ -29,6 +29,9 @@ zonetoast.url = "git+ssh://git@codeberg.org/randogoth/zonetoast.git"; zonetoast.flake = false; + + jirorian.url = "git+ssh://git@codeberg.org/randogoth/jirorian.git"; + jirorian.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { self, nixpkgs, sops-nix, ... }@inputs: @@ -52,6 +55,7 @@ ./modules/services/podman.nix ./modules/services/static-sites ./modules/services/webhook-deploy.nix + ./modules/services/jirorian.nix ./modules/users/tobias.nix ]; }; diff --git a/modules/services/caddy.nix b/modules/services/caddy.nix index 89c6542..0a3a080 100644 --- a/modules/services/caddy.nix +++ b/modules/services/caddy.nix @@ -72,6 +72,6 @@ relay.otherwhere.app { reverse_proxy localhost:8880 } -import Caddyfile.d/*.caddyfile +import /etc/caddy/Caddyfile.d/*.caddyfile ''; } diff --git a/modules/services/jirorian.nix b/modules/services/jirorian.nix new file mode 100644 index 0000000..dffd603 --- /dev/null +++ b/modules/services/jirorian.nix @@ -0,0 +1,74 @@ +{ pkgs, inputs, config, ... }: +let + # Public domain for this service — set before deploying. + domain = "upload.otherwhere.app"; + + # Non-secret runtime configuration. + port = 8390; + relayUrl = "wss://relay.otherwhere.app/"; + cdnUrl = "https://cdn.otherwhere.app"; + bucketName = "media"; + + pkg = inputs.jirorian.packages.${pkgs.system}.default; + + # Writes an EnvironmentFile from sops-managed secret files at service start. + makeEnv = pkgs.writeShellScript "jirorian-make-env" '' + set -euo pipefail + install -m 0600 /dev/null /run/jirorian/env + { + echo "R2_ACCESS_KEY_ID=$(cat ${config.sops.secrets.jirorian_r2_access_key_id.path})" + echo "R2_SECRET_ACCESS_KEY=$(cat ${config.sops.secrets.jirorian_r2_secret_access_key.path})" + echo "R2_ENDPOINT=$(cat ${config.sops.secrets.jirorian_r2_endpoint.path})" + echo "SERVICE_NOSTR_PRIVKEY=$(cat ${config.sops.secrets.jirorian_nostr_privkey.path})" + echo "JIRORIAN_APP_SECRETS=$(cat ${config.sops.secrets.jirorian_app_secrets.path})" + } >> /run/jirorian/env + ''; +in +{ + # Secrets — add corresponding keys to secrets/bucur.yaml via sops. + sops.secrets.jirorian_r2_access_key_id = { owner = "jirorian"; }; + sops.secrets.jirorian_r2_secret_access_key = { owner = "jirorian"; }; + sops.secrets.jirorian_r2_endpoint = { owner = "jirorian"; }; + sops.secrets.jirorian_nostr_privkey = { owner = "jirorian"; }; + sops.secrets.jirorian_app_secrets = { owner = "jirorian"; }; + + users.users.jirorian = { + isSystemUser = true; + group = "jirorian"; + home = "/var/lib/jirorian"; + }; + users.groups.jirorian = {}; + + systemd.services.jirorian = { + description = "Jirorian — Nostr-native R2 image upload API"; + after = [ "network.target" "sops-nix.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + User = "jirorian"; + Group = "jirorian"; + StateDirectory = "jirorian"; + RuntimeDirectory = "jirorian"; + ExecStartPre = "+${makeEnv}"; + ExecStart = "${pkg}/bin/jirorian"; + EnvironmentFile = "/run/jirorian/env"; + Environment = [ + "JIRORIAN_BASE_URL=https://${domain}" + "JIRORIAN_PORT=${toString port}" + "JIRORIAN_DB=/var/lib/jirorian/jirorian.db" + "R2_BUCKET_NAME=${bucketName}" + "R2_CDN_URL=${cdnUrl}" + "SERVICE_RELAY_URL=${relayUrl}" + ]; + Restart = "on-failure"; + RestartSec = 5; + }; + }; + + # Caddy reverse proxy fragment — picked up via the glob import in caddy.nix. + environment.etc."caddy/Caddyfile.d/jirorian.caddyfile".text = '' + ${domain} { + reverse_proxy localhost:${toString port} + } + ''; +} diff --git a/secrets/bucur.yaml b/secrets/bucur.yaml index 4621a88..bf79c3b 100644 --- a/secrets/bucur.yaml +++ b/secrets/bucur.yaml @@ -1,6 +1,11 @@ mtproto_secret: ENC[AES256_GCM,data:MyzyUCH8cLOG+GMtmV03daJ9sqwOw/ozN2yi/14SMlY=,iv:LLf2LIvX+jNjRdMHclHpsmTZa7wmVM3gD6RbqeCGmik=,tag:i36lVESyKJafsn14kSzh3g==,type:str] codeberg_id_ed25519: ENC[AES256_GCM,data:AZbFWfc80HVskenEVLYL9MOYrGhW7Cwzdqv8gS4+1JGfk+nC801SmMgElUdISo+3m/O54b8RbJtdVz2u99gL8WG6wgbK7xqf40q4HNvV5cFXp7L/ozUkT41uXjWsDIdnMwumOqL/vjXowmAay30kMOiD5M4DL0t6EmuOo1Rcdffe/HwCVd2Pz7ZpU+BSTQV/pDTK1X3taUCjCY3Ui0XrcSCY8G2tuX5pFQ6t+vLrdqy3UGhofYHQ+nZuoyEKQHCcvEhYvEgDPJtyy+gk96vZCDYyXb9am/5k0I+MoIsArpavp4P0r5dNDcTnuCHH+fVPTG4tOsG/Ztf1rFBCsqPiM0azNElI8FERSKLYipuDDD8GI46p43OalHw4pxmAHXNOs70PfIH25iFJ5AUb32v2A71LScJMNP/TFSSezuq6g5eTd/JrtpQnWwToH1eVrwcN/8YX3xflLZ2JO1q4fjCXrkYHyw004dV/HAq3u1XQDmfMviZiH80+hiVIgZLfMWv5IyzfuomqSnfOj6oJ6D+j,iv:37HiX60OnO/zZFs1v4VWQLxcaoC1byZ3N2ueYFh4BVQ=,tag:5+z2YlnA8+MEcQTTrYhPJA==,type:str] webhook_secret: ENC[AES256_GCM,data:iLDVsnRCifIXdZzuTTDRT/XHQoW4O/C2Wc33cxn3QoiZNgVXJef50In8+mKPy6SXV05iJqe/0YdOXF3IsHbelA==,iv:Ve/jQx6xPeS3O5h67cideBpGEhJSCVgtDfIypQSmIMc=,tag:Lcp+buNI23SSXVW9Vsn1RQ==,type:str] +jirorian_nostr_privkey: ENC[AES256_GCM,data:fn19816F7BPzdUpRwI8uv6wHFaAh1ipQ7hsHb6iXecz1rQufPW1M1QHW2ee7Zc073XgutQK+tSlpbYlwPSwwVQ==,iv:Ap8ykEyV/7QkAf4FEBPvdWNjIIhQOOyM/pO0r5bEMBg=,tag:O0BqmzqF3MP2xfqX46FDug==,type:str] +jirorian_r2_access_key_id: ENC[AES256_GCM,data:e20DrP0pvsKiA8Ir5i0Na74TTGwRc8P7xc1928OU9eg=,iv:F5yLO2Vl0kP18ntU98HsOq/PBXOXLV/qTq6s9p+1tOE=,tag:dVZzJfKqaw5j5GADOJhWpg==,type:str] +jirorian_r2_secret_access_key: ENC[AES256_GCM,data:ix+y6sdDa+Ze40ty1hQufoi6NCmWL0OrAD2Sk+ljs+cMCTDMdNyLh8YaNfQkUK1I73+csxcyojTMJEA2XSG52A==,iv:sGSPVRq0dMlXX8NnOgirgplprtNgktaxwY6p7ZmIzKw=,tag:E8PAT9t9d1LRoaJw+OTisg==,type:str] +jirorian_r2_endpoint: ENC[AES256_GCM,data:9mokSsGMS2dU72sMEpQNbsjysalOlMnHdd/VS2ebaG7ah80e8p7zG9IJxsXx/6jHy9XPPO+1ItsyhyNhyovfGs0=,iv:inCCSHuHI4X/B0jFilT3j/c4CNntOsti4pdsj1Z7xhE=,tag:OJWu/xmT1Y/XxbWG2mqCrA==,type:str] +jirorian_app_secrets: ENC[AES256_GCM,data:soUEk9GBrpN03+5rqMd2s9qT6sFGAswZUWjKjAHz0k058AJaNbOVlTW56CUjMG25/noIQzhwgqO7IVOzP1+UcdSOqB/67y7Q2rWG0ua8Nxg+qALV5e4Ub6rKGpgYHUfnwYW2wYi7UyxZQe1/mFaG6zCbahp956q/GHYUbXAPfXJcsFjZb0bkenUbrpxkKGdAadPfjwbdSD8ZXadGdwA=,iv:DXdxNIphpTkfcWLfWQrIG2GPsFp1WDLMp16M1Dbv4T8=,tag:ZuBf0XEPFKYCqvFe5FVvow==,type:str] sops: age: - recipient: age1647c5f46njylggj33h26t6twhq57n5qeaza04l845uy0fksv0ucsq82c3d @@ -12,7 +17,7 @@ sops: U0x1ZmtzLytSL1Y3cVo1ZVJ2Ymtob1EKTpgjSymQx14wLSuUhh5Txq++3DrLS4Bs x8vVtQgcXZgv1q0OMGup6DT4bkY6UiNbn9bAt4rHCaRPeb15x4ZKNw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-05-14T10:35:18Z" - mac: ENC[AES256_GCM,data:aTYOA9JxUYYhlqN9Cgocxl50qiSvZ5wCVCgNf15yUNmxXhuaQW8FV1TNXBrJ++k6AQ7+Yerh6cDEu3wjXg38oNdVF60pU4xlMFsQZy337yUXIi0IwStuy7hwshDj6prnROg2a0s77MpSJQLPHF2yQpFv1pHW3aTC3HAiCXcKrL0=,iv:PYOYeHCa4IHE9Kx87ECyMNRtfgmaZ4QUg4rsvjiSgSI=,tag:dGBhf8RW/PRGbA6XziaSAQ==,type:str] + lastmodified: "2026-06-08T07:05:00Z" + mac: ENC[AES256_GCM,data:BomXN9yuhCDbxcWamyScXDDtqUDvG8JuQlkrQzCm6iyvAkXR6QrFbzAP2REoQgEEMKGeAUc0SBZHu3/qgyA9FTHn65Z1zce7sbMu2tRkmb3KKeD/us6gCVRxyEgRB4WLXDaVNAt2aWUC35ek3COt8qQhKEFc05V5WRd6qpaGa6Y=,iv:65lpzgwG+7aNefHc1m3cGKKauE8TMYzAXiTBzUNZJ9I=,tag:cjDB+eO/34YfLd8hJmL/yw==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0