Add jirorian Nostr image upload service
Adds the jirorian NixOS module (systemd service, sops secrets, Caddy fragment), wires it into the flake as a Codeberg input, fixes the Caddy glob import path, and encrypts all five R2/Nostr secrets into bucur.yaml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3ae266293f
commit
e8dcc49320
6 changed files with 88 additions and 51 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -7,4 +7,5 @@
|
|||
/result
|
||||
/result/
|
||||
/*.qcow2
|
||||
/server.md
|
||||
/server.md
|
||||
r2-upload-api.md
|
||||
47
deploy.md
47
deploy.md
|
|
@ -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).
|
||||
|
|
@ -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
|
||||
];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,6 +72,6 @@ relay.otherwhere.app {
|
|||
reverse_proxy localhost:8880
|
||||
}
|
||||
|
||||
import Caddyfile.d/*.caddyfile
|
||||
import /etc/caddy/Caddyfile.d/*.caddyfile
|
||||
'';
|
||||
}
|
||||
|
|
|
|||
74
modules/services/jirorian.nix
Normal file
74
modules/services/jirorian.nix
Normal file
|
|
@ -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}
|
||||
}
|
||||
'';
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue