1{ lib, config, ... }:
2let
3 pdsLocalhost = "http://localhost:${toString config.services.bluesky-pds.settings.PDS_PORT}";
4in
5{
6 services.nginx.virtualHosts.${config.services.bluesky-pds.settings.PDS_HOSTNAME} = {
7 useACMEHost = "gaze.systems";
8 forceSSL = true;
9 locations = {
10 # we need to proxy /xrpc for pds to work
11 # silly but i want root domain >:3
12 "/xrpc" = {
13 proxyPass = pdsLocalhost;
14 proxyWebsockets = true;
15 # pass ws headers so we can actually proxy the ws
16 extraConfig = ''
17 proxy_set_header id $request_id;
18 client_max_body_size 100M;
19 '';
20 # higher prio just to make sure
21 priority = 100;
22 };
23 "/xrpc/app.bsky.unspecced.getAgeAssuranceState".extraConfig = ''
24 default_type application/json;
25 add_header access-control-allow-headers "authorization,dpop,atproto-accept-labelers,atproto-proxy" always;
26 add_header access-control-allow-origin "*" always;
27 return 200 '{"lastInitiatedAt":"2025-07-14T14:22:43.912Z","status":"assured"}';
28 '';
29 }
30 # others
31 // (lib.genAttrs
32 [
33 "/account"
34 "/@atproto"
35 "/oauth"
36 "=/.well-known/oauth-protected-resource"
37 "=/.well-known/oauth-authorization-server"
38 ]
39 (_: {
40 proxyPass = pdsLocalhost;
41 # higher prio just to make sure
42 priority = 100;
43 })
44 );
45 };
46 # setup pds stuff
47 services.bluesky-pds = {
48 enable = true;
49 settings = {
50 PDS_HOSTNAME = "gaze.systems";
51 PDS_PORT = 1334;
52
53 PDS_SERVICE_NAME = ''"gazing at the sky"'';
54 PDS_LOGO_URL = "https://gaze.systems/icons/gaze_site.webp";
55
56 PDS_RATE_LIMITS_ENABLED = "true";
57 PDS_INVITE_REQUIRED = "true";
58
59 PDS_DID_PLC_URL = "https://plc.directory";
60 PDS_BSKY_APP_VIEW_URL = "https://api.bsky.app";
61 PDS_BSKY_APP_VIEW_DID = "did:web:api.bsky.app";
62 PDS_REPORT_SERVICE_URL = "https://mod.bsky.app";
63 PDS_REPORT_SERVICE_DID = "did:plc:ar7c4by46qjdydhdevvrndac";
64 PDS_CRAWLERS = "https://bsky.network";
65 };
66 environmentFiles = [ config.age.secrets.pdsConfig.path ];
67 };
68
69 # services.fluent-bit.settings = {
70 # parsers = [
71 # {
72 # name = "pds_json";
73 # format = "json";
74 # time_key = "time";
75 # time_strict = false;
76 # }
77 # ];
78 # pipeline = {
79 # inputs = [
80 # {
81 # name = "systemd";
82 # tag = "logs.pds";
83 # systemd_filter = "_SYSTEMD_UNIT=bluesky-pds.service";
84 # }
85 # ];
86 # filters = [
87 # {
88 # name = "parser";
89 # match = "logs.pds";
90 # key_name = "MESSAGE";
91 # parser = "pds_json";
92 # }
93 # {
94 # name = "modify";
95 # match = "logs.pds";
96 # Rename = [ "msg _msg" ];
97 # }
98 # ];
99 # };
100 # };
101
102 # services.vmalert.instances."".rules.groups = [
103 # {
104 # name = "pds-logs";
105 # type = "vlogs";
106 # interval = "1m";
107 # rules = [
108 # {
109 # record = "pds_request_count";
110 # expr = "name:pds | stats (res.statusCode) count() as total_requests";
111 # }
112 # {
113 # record = "pds_response_latency";
114 # expr = "name:pds | stats avg(responseTime) avg, quantile(0.5, responseTime) p50, quantile(0.9, responseTime) p90, quantile(0.99, responseTime) p99";
115 # }
116 # ];
117 # }
118 # ];
119
120 # virtualisation = {
121 # podman = {
122 # enable = true;
123 # dockerCompat = true;
124 # defaultNetwork.settings.dns_enabled = true;
125 # };
126 # oci-containers.containers = {
127 # pds = {
128 # image = "ghcr.io/bluesky-social/pds:0.4";
129 # autoStart = true;
130 # environmentFiles = [ ./pds.env config.age.secrets.pdsConfig.path ];
131 # ports = [ "1334:1334" ];
132 # volumes = [
133 # "/var/lib/pds:/pds"
134 # ];
135 # extraOptions = [
136 # #"--network=host"
137 # "--label=io.containers.autoupdate=registry"
138 # ];
139 # };
140 # };
141 # };
142 # # This is the podman auto-update systemd timer.
143 # # If I start to rely on podman auto-update more, I should move this out of the PDS definition.
144 # systemd.timers."podman-auto-update" = {
145 # enable = true;
146 # timerConfig = {
147 # OnCalendar = "*-*-* 4:00:00";
148 # Persistent = true;
149 # };
150 # wantedBy = [ "timers.target" ];
151 # };
152}