This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

configs.services.git: tranquil PDS on neoprism with id.lassul.us login

pds.lassul.us with handles <name>.pds.lassul.us (wildcard cert via
DNS-01). Login through the zitadel project pds (access = declarative,
only lass@lassul.us), via Tranquil's generic OIDC SSO. Accounts are
created only through the SSO signup, without invite codes; nginx
returns 403 for createAccount, createPasskeyAccount and
createDelegatedAccount (this also blocks migrating accounts in).
SMTP username and SSO_OIDC_ENABLED are set via the environment
because the nixpkgs module validates the config file at build time
without the secrets they require. Mail goes out as send-only
pds@lassul.us, a mailbox on neoprism's mailserver defined next to the
PDS.

+157
+3
machines/neoprism/config.nix
··· 106 106 # "Your own provider" logins for zitadel (byoidc.lassul.us) 107 107 ../../configs/byoidc.nix 108 108 109 + # ATProto PDS (pds.lassul.us) 110 + ../../configs/services/git 111 + 109 112 # radicle public seed (radicle.lassul.us) 110 113 ../../configs/radicle.lassul.us.nix 111 114 # radicle identity + node for this machine (/var/lib/rad)
+5
configs/services/git/default.nix
··· 1 + { 2 + imports = [ 3 + ./pds.nix 4 + ]; 5 + }
+149
configs/services/git/pds.nix
··· 1 + { 2 + self, 3 + config, 4 + pkgs, 5 + ... 6 + }: 7 + let 8 + domain = "pds.lassul.us"; 9 + cfg = config.services.tranquil-pds; 10 + package = self.packages.${pkgs.stdenv.hostPlatform.system}.tranquil-pds; 11 + zitadelEnv = config.lass.zitadel.projects.pds.apps.web.envFile.path; 12 + in 13 + { 14 + # ATProto PDS (Tranquil). Accounts get handles <name>.pds.lassul.us and 15 + # log in via id.lassul.us. Accounts are created only by the SSO signup 16 + # (no invite code), nginx rejects all other account-creation routes. 17 + clan.core.vars.generators.tranquil-pds = { 18 + files."secrets.env" = { }; 19 + runtimeInputs = [ pkgs.openssl ]; 20 + script = '' 21 + for var in JWT_SECRET DPOP_SECRET MASTER_KEY; do 22 + echo "$var=$(openssl rand -hex 32)" 23 + done > "$out/secrets.env" 24 + ''; 25 + }; 26 + 27 + # send-only mailbox on this host's mailserver (configs/mailserver.nix) for 28 + # email verification and password resets 29 + mailserver.loginAccounts."pds@lassul.us" = { 30 + hashedPasswordFile = config.clan.core.vars.generators.mailserver-pds.files."password-hash".path; 31 + sendOnly = true; 32 + }; 33 + 34 + # smarthost.env is an environmentFile of tranquil-pds 35 + clan.core.vars.generators.mailserver-pds = { 36 + files."smarthost.env" = { }; 37 + files."password-hash" = { }; 38 + runtimeInputs = with pkgs; [ 39 + coreutils 40 + mkpasswd 41 + pwgen 42 + ]; 43 + script = '' 44 + password=$(pwgen -s 32 1) 45 + echo "MAIL_SMARTHOST_PASSWORD=$password" > "$out/smarthost.env" 46 + mkpasswd -sm bcrypt <<< "$password" > "$out/password-hash" 47 + ''; 48 + }; 49 + 50 + services.tranquil-pds = { 51 + enable = true; 52 + inherit package; 53 + database.createLocally = true; 54 + environmentFiles = [ 55 + config.clan.core.vars.generators.tranquil-pds.files."secrets.env".path 56 + config.clan.core.vars.generators.mailserver-pds.files."smarthost.env".path 57 + zitadelEnv 58 + ]; 59 + settings = { 60 + server = { 61 + hostname = domain; 62 + invite_code_required = false; 63 + # certs come from nginx, not caddy 64 + enable_caddy_on_demand_tls = false; 65 + }; 66 + frontend.dir = package.frontend; 67 + # owner's PLC recovery key (public part) 68 + secrets.plc_rotation_key = "did:key:zDnaepKZiYEbndTH9NNqoZUa7mvbhdiF3M9g2MCjtnxhijtUS"; 69 + email = { 70 + from_address = "pds@lassul.us"; 71 + from_name = "lassul.us PDS"; 72 + smarthost = { 73 + host = "mail.lassul.us"; 74 + port = 465; 75 + tls = "implicit"; 76 + }; 77 + }; 78 + sso.oidc = { 79 + issuer = "https://id.lassul.us"; 80 + display_name = "lassul.us ID"; 81 + }; 82 + }; 83 + }; 84 + 85 + systemd.services.tranquil-pds = { 86 + wants = [ "zitadel-reconcile.service" ]; 87 + after = [ "zitadel-reconcile.service" ]; 88 + # not in settings: the config file is validated at build time, without 89 + # the secrets from environmentFiles that these require 90 + environment = { 91 + MAIL_SMARTHOST_USERNAME = "pds@lassul.us"; 92 + SSO_OIDC_ENABLED = "true"; 93 + }; 94 + }; 95 + 96 + # only the users listed here get a project grant and can log in 97 + lass.zitadel.projects.pds = { 98 + access = "declarative"; 99 + users = [ "lass@lassul.us" ]; 100 + apps.web = { 101 + redirectUris = [ "https://${domain}/oauth/sso/callback" ]; 102 + authMethod = "post"; 103 + envFile = { 104 + clientIdVariable = "SSO_OIDC_CLIENT_ID"; 105 + clientSecretVariable = "SSO_OIDC_CLIENT_SECRET"; 106 + }; 107 + }; 108 + }; 109 + 110 + # repos, accounts and keys in postgres, blobs on disk 111 + lass.backup.state.tranquil-pds = { 112 + folders = [ cfg.dataDir ]; 113 + preBackup = '' 114 + mkdir -p /var/backup/postgresql 115 + ${pkgs.sudo}/bin/sudo -u postgres ${config.services.postgresql.package}/bin/pg_dump \ 116 + tranquil-pds | ${pkgs.gzip}/bin/gzip > /var/backup/postgresql/tranquil-pds.sql.gz 117 + ''; 118 + }; 119 + 120 + # handles are served /.well-known/atproto-did on <handle>.pds.lassul.us; 121 + # DNS-01 against our own knot, see _acme-challenge.pds.lassul.us in 122 + # configs/dns/knot.nix 123 + security.acme.certs.${domain} = { 124 + extraDomainNames = [ "*.${domain}" ]; 125 + dnsProvider = "rfc2136"; 126 + environmentFile = config.clan.core.vars.generators.knot-acme-review.files."lego.env".path; 127 + group = config.services.nginx.group; 128 + }; 129 + 130 + services.nginx.virtualHosts.${domain} = { 131 + serverAliases = [ "*.${domain}" ]; 132 + useACMEHost = domain; 133 + forceSSL = true; 134 + locations."/" = { 135 + proxyPass = "http://${cfg.settings.server.host}:${toString cfg.settings.server.port}"; 136 + recommendedProxySettings = true; 137 + # com.atproto.sync.subscribeRepos 138 + proxyWebsockets = true; 139 + extraConfig = '' 140 + proxy_read_timeout 1d; 141 + client_max_body_size 10G; 142 + ''; 143 + }; 144 + # password, passkey, delegated and migrated (service-auth) accounts 145 + locations."= /xrpc/com.atproto.server.createAccount".return = "403"; 146 + locations."= /xrpc/_account.createPasskeyAccount".return = "403"; 147 + locations."= /xrpc/_delegation.createDelegatedAccount".return = "403"; 148 + }; 149 + }