anki-bin: 24.06.3 -> 24.11 (#360722)
[NixPkgs.git] / nixos / modules / services / development / athens.nix
blobddd4699fea2ada3b68a2bfb4f91191da4011859d
1 { config, lib, pkgs, ... }:
2 let
3   cfg = config.services.athens;
5   athensConfig = lib.flip lib.recursiveUpdate cfg.extraConfig (
6     {
7       GoBinary = "${cfg.goBinary}/bin/go";
8       GoEnv = cfg.goEnv;
9       GoBinaryEnvVars = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.goBinaryEnvVars;
10       GoGetWorkers = cfg.goGetWorkers;
11       GoGetDir = cfg.goGetDir;
12       ProtocolWorkers = cfg.protocolWorkers;
13       LogLevel = cfg.logLevel;
14       CloudRuntime = cfg.cloudRuntime;
15       EnablePprof = cfg.enablePprof;
16       PprofPort = ":${toString cfg.pprofPort}";
17       FilterFile = cfg.filterFile;
18       RobotsFile = cfg.robotsFile;
19       Timeout = cfg.timeout;
20       StorageType = cfg.storageType;
21       TLSCertFile = cfg.tlsCertFile;
22       TLSKeyFile = cfg.tlsKeyFile;
23       Port = ":${toString cfg.port}";
24       UnixSocket = cfg.unixSocket;
25       GlobalEndpoint = cfg.globalEndpoint;
26       BasicAuthUser = cfg.basicAuthUser;
27       BasicAuthPass = cfg.basicAuthPass;
28       ForceSSL = cfg.forceSSL;
29       ValidatorHook = cfg.validatorHook;
30       PathPrefix = cfg.pathPrefix;
31       NETRCPath = cfg.netrcPath;
32       GithubToken = cfg.githubToken;
33       HGRCPath = cfg.hgrcPath;
34       TraceExporter = cfg.traceExporter;
35       StatsExporter = cfg.statsExporter;
36       SumDBs = cfg.sumDBs;
37       NoSumPatterns = cfg.noSumPatterns;
38       DownloadMode = cfg.downloadMode;
39       NetworkMode = cfg.networkMode;
40       DownloadURL = cfg.downloadURL;
41       SingleFlightType = cfg.singleFlightType;
42       IndexType = cfg.indexType;
43       ShutdownTimeout = cfg.shutdownTimeout;
44       SingleFlight = {
45         Etcd = {
46           Endpoints = builtins.concatStringsSep "," cfg.singleFlight.etcd.endpoints;
47         };
48         Redis = {
49           Endpoint = cfg.singleFlight.redis.endpoint;
50           Password = cfg.singleFlight.redis.password;
51           LockConfig = {
52             TTL = cfg.singleFlight.redis.lockConfig.ttl;
53             Timeout = cfg.singleFlight.redis.lockConfig.timeout;
54             MaxRetries = cfg.singleFlight.redis.lockConfig.maxRetries;
55           };
56         };
57         RedisSentinel = {
58           Endpoints = cfg.singleFlight.redisSentinel.endpoints;
59           MasterName = cfg.singleFlight.redisSentinel.masterName;
60           SentinelPassword = cfg.singleFlight.redisSentinel.sentinelPassword;
61           LockConfig = {
62             TTL = cfg.singleFlight.redisSentinel.lockConfig.ttl;
63             Timeout = cfg.singleFlight.redisSentinel.lockConfig.timeout;
64             MaxRetries = cfg.singleFlight.redisSentinel.lockConfig.maxRetries;
65           };
66         };
67       };
68       Storage = {
69         CDN = {
70           Endpoint = cfg.storage.cdn.endpoint;
71         };
72         Disk = {
73           RootPath = cfg.storage.disk.rootPath;
74         };
75         GCP = {
76           ProjectID = cfg.storage.gcp.projectID;
77           Bucket = cfg.storage.gcp.bucket;
78           JSONKey = cfg.storage.gcp.jsonKey;
79         };
80         Minio = {
81           Endpoint = cfg.storage.minio.endpoint;
82           Key = cfg.storage.minio.key;
83           Secret = cfg.storage.minio.secret;
84           EnableSSL = cfg.storage.minio.enableSSL;
85           Bucket = cfg.storage.minio.bucket;
86           region = cfg.storage.minio.region;
87         };
88         Mongo = {
89           URL = cfg.storage.mongo.url;
90           DefaultDBName = cfg.storage.mongo.defaultDBName;
91           CertPath = cfg.storage.mongo.certPath;
92           Insecure = cfg.storage.mongo.insecure;
93         };
94         S3 = {
95           Region = cfg.storage.s3.region;
96           Key = cfg.storage.s3.key;
97           Secret = cfg.storage.s3.secret;
98           Token = cfg.storage.s3.token;
99           Bucket = cfg.storage.s3.bucket;
100           ForcePathStyle = cfg.storage.s3.forcePathStyle;
101           UseDefaultConfiguration = cfg.storage.s3.useDefaultConfiguration;
102           CredentialsEndpoint = cfg.storage.s3.credentialsEndpoint;
103           AwsContainerCredentialsRelativeURI = cfg.storage.s3.awsContainerCredentialsRelativeURI;
104           Endpoint = cfg.storage.s3.endpoint;
105         };
106         AzureBlob = {
107           AccountName = cfg.storage.azureblob.accountName;
108           AccountKey = cfg.storage.azureblob.accountKey;
109           ContainerName = cfg.storage.azureblob.containerName;
110         };
111         External = {
112           URL = cfg.storage.external.url;
113         };
114       };
115       Index = {
116         MySQL = {
117           Protocol = cfg.index.mysql.protocol;
118           Host = cfg.index.mysql.host;
119           Port = cfg.index.mysql.port;
120           User = cfg.index.mysql.user;
121           Password = cfg.index.mysql.password;
122           Database = cfg.index.mysql.database;
123           Params = {
124             parseTime = cfg.index.mysql.params.parseTime;
125             timeout = cfg.index.mysql.params.timeout;
126           };
127         };
128         Postgres = {
129           Host = cfg.index.postgres.host;
130           Port = cfg.index.postgres.port;
131           User = cfg.index.postgres.user;
132           Password = cfg.index.postgres.password;
133           Database = cfg.index.postgres.database;
134           Params = {
135             connect_timeout = cfg.index.postgres.params.connect_timeout;
136             sslmode = cfg.index.postgres.params.sslmode;
137           };
138         };
139       };
140     }
141   );
143   configFile = pkgs.runCommandLocal "config.toml" { } ''
144     ${pkgs.buildPackages.jq}/bin/jq 'del(..|nulls)' \
145       < ${pkgs.writeText "config.json" (builtins.toJSON athensConfig)} | \
146     ${pkgs.buildPackages.remarshal}/bin/remarshal -if json -of toml \
147       > $out
148   '';
151   meta = {
152     maintainers = pkgs.athens.meta.maintainers;
153     doc = ./athens.md;
154   };
156   options.services.athens = {
157     enable = lib.mkEnableOption "Go module datastore and proxy";
159     package = lib.mkOption {
160       default = pkgs.athens;
161       defaultText = lib.literalExpression "pkgs.athens";
162       example = "pkgs.athens";
163       description = "Which athens derivation to use";
164       type = lib.types.package;
165     };
167     goBinary = lib.mkOption {
168       type = lib.types.package;
169       default = pkgs.go;
170       defaultText = lib.literalExpression "pkgs.go";
171       example = "pkgs.go_1_23";
172       description = ''
173         The Go package used by Athens at runtime.
175         Athens primarily runs two Go commands:
176         1. `go mod download -json <module>@<version>`
177         2. `go list -m -json <module>@latest`
178       '';
179     };
181     goEnv = lib.mkOption {
182       type = lib.types.enum [ "development" "production" ];
183       description = "Specifies the type of environment to run. One of 'development' or 'production'.";
184       default = "development";
185       example = "production";
186     };
188     goBinaryEnvVars = lib.mkOption {
189       type = lib.types.attrs;
190       description = "Environment variables to pass to the Go binary.";
191       example = ''
192         { "GOPROXY" = "direct", "GODEBUG" = "true" }
193       '';
194       default = { };
195     };
197     goGetWorkers = lib.mkOption {
198       type = lib.types.int;
199       description = "Number of workers concurrently downloading modules.";
200       default = 10;
201       example = 32;
202     };
204     goGetDir = lib.mkOption {
205       type = lib.types.nullOr lib.types.path;
206       description = ''
207         Temporary directory that Athens will use to
208         fetch modules from VCS prior to persisting
209         them to a storage backend.
211         If the value is empty, Athens will use the
212         default OS temp directory.
213       '';
214       default = null;
215       example = "/tmp/athens";
216     };
218     protocolWorkers = lib.mkOption {
219       type = lib.types.int;
220       description = "Number of workers concurrently serving protocol paths.";
221       default = 30;
222     };
224     logLevel = lib.mkOption {
225       type = lib.types.nullOr (lib.types.enum [ "panic" "fatal" "error" "warning" "info" "debug" "trace" ]);
226       description = ''
227         Log level for Athens.
228         Supports all logrus log levels (https://github.com/Sirupsen/logrus#level-logging)".
229       '';
230       default = "warning";
231       example = "debug";
232     };
234     cloudRuntime = lib.mkOption {
235       type = lib.types.enum [ "GCP" "none" ];
236       description = ''
237         Specifies the Cloud Provider on which the Proxy/registry is running.
238       '';
239       default = "none";
240       example = "GCP";
241     };
243     enablePprof = lib.mkOption {
244       type = lib.types.bool;
245       description = "Enable pprof endpoints.";
246       default = false;
247     };
249     pprofPort = lib.mkOption {
250       type = lib.types.port;
251       description = "Port number for pprof endpoints.";
252       default = 3301;
253       example = 443;
254     };
256     filterFile = lib.mkOption {
257       type = lib.types.nullOr lib.types.path;
258       description = ''Filename for the include exclude filter.'';
259       default = null;
260       example = lib.literalExpression ''
261         pkgs.writeText "filterFile" '''
262           - github.com/azure
263           + github.com/azure/azure-sdk-for-go
264           D golang.org/x/tools
265         '''
266       '';
267     };
269     robotsFile = lib.mkOption {
270       type = lib.types.nullOr lib.types.path;
271       description = ''Provides /robots.txt for net crawlers.'';
272       default = null;
273       example = lib.literalExpression ''pkgs.writeText "robots.txt" "# my custom robots.txt ..."'';
274     };
276     timeout = lib.mkOption {
277       type = lib.types.int;
278       description = "Timeout for external network calls in seconds.";
279       default = 300;
280       example = 3;
281     };
283     storageType = lib.mkOption {
284       type = lib.types.enum [ "memory" "disk" "mongo" "gcp" "minio" "s3" "azureblob" "external" ];
285       description = "Specifies the type of storage backend to use.";
286       default = "disk";
287     };
289     tlsCertFile = lib.mkOption {
290       type = lib.types.nullOr lib.types.path;
291       description = "Path to the TLS certificate file.";
292       default = null;
293       example = "/etc/ssl/certs/athens.crt";
294     };
296     tlsKeyFile = lib.mkOption {
297       type = lib.types.nullOr lib.types.path;
298       description = "Path to the TLS key file.";
299       default = null;
300       example = "/etc/ssl/certs/athens.key";
301     };
303     port = lib.mkOption {
304       type = lib.types.port;
305       default = 3000;
306       description = ''
307         Port number Athens listens on.
308       '';
309       example = 443;
310     };
312     unixSocket = lib.mkOption {
313       type = lib.types.nullOr lib.types.path;
314       description = ''
315         Path to the unix socket file.
316         If set, Athens will listen on the unix socket instead of TCP socket.
317       '';
318       default = null;
319       example = "/run/athens.sock";
320     };
322     globalEndpoint = lib.mkOption {
323       type = lib.types.str;
324       description = ''
325         Endpoint for a package registry in case of a proxy cache miss.
326       '';
327       default = "";
328       example = "http://upstream-athens.example.com:3000";
329     };
331     basicAuthUser = lib.mkOption {
332       type = lib.types.nullOr lib.types.str;
333       description = ''
334         Username for basic auth.
335       '';
336       default = null;
337       example = "user";
338     };
340     basicAuthPass = lib.mkOption {
341       type = lib.types.nullOr lib.types.str;
342       description = ''
343         Password for basic auth. Warning: this is stored in plain text in the config file.
344       '';
345       default = null;
346       example = "swordfish";
347     };
349     forceSSL = lib.mkOption {
350       type = lib.types.bool;
351       description = ''
352         Force SSL redirects for incoming requests.
353       '';
354       default = false;
355     };
357     validatorHook = lib.mkOption {
358       type = lib.types.nullOr lib.types.str;
359       description = ''
360         Endpoint to validate modules against.
362         Not used if empty.
363       '';
364       default = null;
365       example = "https://validation.example.com";
366     };
368     pathPrefix = lib.mkOption {
369       type = lib.types.nullOr lib.types.str;
370       description = ''
371         Sets basepath for all routes.
372       '';
373       default = null;
374       example = "/athens";
375     };
377     netrcPath = lib.mkOption {
378       type = lib.types.nullOr lib.types.path;
379       description = ''
380         Path to the .netrc file.
381       '';
382       default = null;
383       example = "/home/user/.netrc";
384     };
386     githubToken = lib.mkOption {
387       type = lib.types.nullOr lib.types.str;
388       description = ''
389         Creates .netrc file with the given token to be used for GitHub.
390         Warning: this is stored in plain text in the config file.
391       '';
392       default = null;
393       example = "ghp_1234567890";
394     };
396     hgrcPath = lib.mkOption {
397       type = lib.types.nullOr lib.types.path;
398       description = ''
399         Path to the .hgrc file.
400       '';
401       default = null;
402       example = "/home/user/.hgrc";
403     };
405     traceExporter = lib.mkOption {
406       type = lib.types.nullOr (lib.types.enum [ "jaeger" "datadog" ]);
407       description = ''
408         Trace exporter to use.
409       '';
410       default = null;
411     };
413     traceExporterURL = lib.mkOption {
414       type = lib.types.nullOr lib.types.str;
415       description = ''
416         URL endpoint that traces will be sent to.
417       '';
418       default = null;
419       example = "http://localhost:14268";
420     };
422     statsExporter = lib.mkOption {
423       type = lib.types.nullOr (lib.types.enum [ "prometheus" ]);
424       description = "Stats exporter to use.";
425       default = null;
426     };
428     sumDBs = lib.mkOption {
429       type = lib.types.listOf lib.types.str;
430       description = ''
431         List of fully qualified URLs that Athens will proxy
432         that the go command can use a checksum verifier.
433       '';
434       default = [ "https://sum.golang.org" ];
435     };
437     noSumPatterns = lib.mkOption {
438       type = lib.types.listOf lib.types.str;
439       description = ''
440         List of patterns that Athens sum db proxy will return a 403 for.
441       '';
442       default = [ ];
443       example = [ "github.com/mycompany/*" ];
444     };
446     downloadMode = lib.mkOption {
447       type = lib.types.oneOf [ (lib.types.enum [ "sync" "async" "redirect" "async_redirect" "none" ]) (lib.types.strMatching "^file:.*$|^custom:.*$") ];
448       description = ''
449         Defines how Athens behaves when a module@version
450         is not found in storage. There are 7 options:
451         1. "sync": download the module synchronously and
452         return the results to the client.
453         2. "async": return 404, but asynchronously store the module
454         in the storage backend.
455         3. "redirect": return a 301 redirect status to the client
456         with the base URL as the DownloadRedirectURL from below.
457         4. "async_redirect": same as option number 3 but it will
458         asynchronously store the module to the backend.
459         5. "none": return 404 if a module is not found and do nothing.
460         6. "file:<path>": will point to an HCL file that specifies
461         any of the 5 options above based on different import paths.
462         7. "custom:<base64-encoded-hcl>" is the same as option 6
463         but the file is fully encoded in the option. This is
464         useful for using an environment variable in serverless
465         deployments.
466       '';
467       default = "async_redirect";
468     };
470     networkMode = lib.mkOption {
471       type = lib.types.enum [ "strict" "offline" "fallback" ];
472       description = ''
473         Configures how Athens will return the results
474         of the /list endpoint as it can be assembled from both its own
475         storage and the upstream VCS.
477         Note, that for better error messaging, this would also affect how other
478         endpoints behave.
480         Modes:
481         1. strict: merge VCS versions with storage versions, but fail if either of them fails.
482         2. offline: only get storage versions, never reach out to VCS.
483         3. fallback: only return storage versions, if VCS fails. Note this means that you may
484         see inconsistent results since fallback mode does a best effort of giving you what's
485         available at the time of requesting versions.
486       '';
487       default = "strict";
488     };
490     downloadURL = lib.mkOption {
491       type = lib.types.str;
492       description = "URL used if DownloadMode is set to redirect.";
493       default = "https://proxy.golang.org";
494     };
496     singleFlightType = lib.mkOption {
497       type = lib.types.enum [ "memory" "etcd" "redis" "redis-sentinel" "gcp" "azureblob" ];
498       description = ''
499         Determines what mechanism Athens uses to manage concurrency flowing into the Athens backend.
500       '';
501       default = "memory";
502     };
504     indexType = lib.mkOption {
505       type = lib.types.enum [ "none" "memory" "mysql" "postgres" ];
506       description = ''
507         Type of index backend Athens will use.
508       '';
509       default = "none";
510     };
512     shutdownTimeout = lib.mkOption {
513       type = lib.types.int;
514       description = ''
515         Number of seconds to wait for the server to shutdown gracefully.
516       '';
517       default = 60;
518       example = 1;
519     };
521     singleFlight = {
522       etcd = {
523         endpoints = lib.mkOption {
524           type = lib.types.listOf lib.types.str;
525           description = "URLs that determine all distributed etcd servers.";
526           default = [ ];
527           example = [ "localhost:2379" ];
528         };
529       };
530       redis = {
531         endpoint = lib.mkOption {
532           type = lib.types.str;
533           description = "URL of the redis server.";
534           default = "";
535           example = "localhost:6379";
536         };
537         password = lib.mkOption {
538           type = lib.types.str;
539           description = "Password for the redis server. Warning: this is stored in plain text in the config file.";
540           default = "";
541           example = "swordfish";
542         };
544         lockConfig = {
545           ttl = lib.mkOption {
546             type = lib.types.int;
547             description = "TTL for the lock in seconds.";
548             default = 900;
549             example = 1;
550           };
551           timeout = lib.mkOption {
552             type = lib.types.int;
553             description = "Timeout for the lock in seconds.";
554             default = 15;
555             example = 1;
556           };
557           maxRetries = lib.mkOption {
558             type = lib.types.int;
559             description = "Maximum number of retries for the lock.";
560             default = 10;
561             example = 1;
562           };
563         };
564       };
566       redisSentinel = {
567         endpoints = lib.mkOption {
568           type = lib.types.listOf lib.types.str;
569           description = "URLs that determine all distributed redis servers.";
570           default = [ ];
571           example = [ "localhost:26379" ];
572         };
573         masterName = lib.mkOption {
574           type = lib.types.str;
575           description = "Name of the sentinel master server.";
576           default = "";
577           example = "redis-1";
578         };
579         sentinelPassword = lib.mkOption {
580           type = lib.types.str;
581           description = "Password for the sentinel server. Warning: this is stored in plain text in the config file.";
582           default = "";
583           example = "swordfish";
584         };
586         lockConfig = {
587           ttl = lib.mkOption {
588             type = lib.types.int;
589             description = "TTL for the lock in seconds.";
590             default = 900;
591             example = 1;
592           };
593           timeout = lib.mkOption {
594             type = lib.types.int;
595             description = "Timeout for the lock in seconds.";
596             default = 15;
597             example = 1;
598           };
599           maxRetries = lib.mkOption {
600             type = lib.types.int;
601             description = "Maximum number of retries for the lock.";
602             default = 10;
603             example = 1;
604           };
605         };
606       };
607     };
609     storage = {
610       cdn = {
611         endpoint = lib.mkOption {
612           type = lib.types.nullOr lib.types.str;
613           description = "hostname of the CDN server.";
614           example = "cdn.example.com";
615           default = null;
616         };
617       };
619       disk = {
620         rootPath = lib.mkOption {
621           type = lib.types.nullOr lib.types.path;
622           description = "Athens disk root folder.";
623           default = "/var/lib/athens";
624         };
625       };
627       gcp = {
628         projectID = lib.mkOption {
629           type = lib.types.nullOr lib.types.str;
630           description = "GCP project ID.";
631           example = "my-project";
632           default = null;
633         };
634         bucket = lib.mkOption {
635           type = lib.types.nullOr lib.types.str;
636           description = "GCP backend storage bucket.";
637           example = "my-bucket";
638           default = null;
639         };
640         jsonKey = lib.mkOption {
641           type = lib.types.nullOr lib.types.str;
642           description = "Base64 encoded GCP service account key. Warning: this is stored in plain text in the config file.";
643           default = null;
644         };
645       };
647       minio = {
648         endpoint = lib.mkOption {
649           type = lib.types.nullOr lib.types.str;
650           description = "Endpoint of the minio storage backend.";
651           example = "minio.example.com:9001";
652           default = null;
653         };
654         key = lib.mkOption {
655           type = lib.types.nullOr lib.types.str;
656           description = "Access key id for the minio storage backend.";
657           example = "minio";
658           default = null;
659         };
660         secret = lib.mkOption {
661           type = lib.types.nullOr lib.types.str;
662           description = "Secret key for the minio storage backend. Warning: this is stored in plain text in the config file.";
663           example = "minio123";
664           default = null;
665         };
666         enableSSL = lib.mkOption {
667           type = lib.types.bool;
668           description = "Enable SSL for the minio storage backend.";
669           default = false;
670         };
671         bucket = lib.mkOption {
672           type = lib.types.nullOr lib.types.str;
673           description = "Bucket name for the minio storage backend.";
674           example = "gomods";
675           default = null;
676         };
677         region = lib.mkOption {
678           type = lib.types.nullOr lib.types.str;
679           description = "Region for the minio storage backend.";
680           example = "us-east-1";
681           default = null;
682         };
683       };
685       mongo = {
686         url = lib.mkOption {
687           type = lib.types.nullOr lib.types.str;
688           description = "URL of the mongo database.";
689           example = "mongodb://localhost:27017";
690           default = null;
691         };
692         defaultDBName = lib.mkOption {
693           type = lib.types.nullOr lib.types.str;
694           description = "Name of the mongo database.";
695           example = "athens";
696           default = null;
697         };
698         certPath = lib.mkOption {
699           type = lib.types.nullOr lib.types.path;
700           description = "Path to the certificate file for the mongo database.";
701           example = "/etc/ssl/mongo.pem";
702           default = null;
703         };
704         insecure = lib.mkOption {
705           type = lib.types.bool;
706           description = "Allow insecure connections to the mongo database.";
707           default = false;
708         };
709       };
711       s3 = {
712         region = lib.mkOption {
713           type = lib.types.nullOr lib.types.str;
714           description = "Region of the S3 storage backend.";
715           example = "eu-west-3";
716           default = null;
717         };
718         key = lib.mkOption {
719           type = lib.types.nullOr lib.types.str;
720           description = "Access key id for the S3 storage backend.";
721           example = "minio";
722           default = null;
723         };
724         secret = lib.mkOption {
725           type = lib.types.str;
726           description = "Secret key for the S3 storage backend. Warning: this is stored in plain text in the config file.";
727           default = "";
728         };
729         token = lib.mkOption {
730           type = lib.types.nullOr lib.types.str;
731           description = "Token for the S3 storage backend. Warning: this is stored in plain text in the config file.";
732           default = null;
733         };
734         bucket = lib.mkOption {
735           type = lib.types.nullOr lib.types.str;
736           description = "Bucket name for the S3 storage backend.";
737           example = "gomods";
738           default = null;
739         };
740         forcePathStyle = lib.mkOption {
741           type = lib.types.bool;
742           description = "Force path style for the S3 storage backend.";
743           default = false;
744         };
745         useDefaultConfiguration = lib.mkOption {
746           type = lib.types.bool;
747           description = "Use default configuration for the S3 storage backend.";
748           default = false;
749         };
750         credentialsEndpoint = lib.mkOption {
751           type = lib.types.str;
752           description = "Credentials endpoint for the S3 storage backend.";
753           default = "";
754         };
755         awsContainerCredentialsRelativeURI = lib.mkOption {
756           type = lib.types.nullOr lib.types.str;
757           description = "Container relative url (used by fargate).";
758           default = null;
759         };
760         endpoint = lib.mkOption {
761           type = lib.types.nullOr lib.types.str;
762           description = "Endpoint for the S3 storage backend.";
763           default = null;
764         };
765       };
767       azureblob = {
768         accountName = lib.mkOption {
769           type = lib.types.nullOr lib.types.str;
770           description = "Account name for the Azure Blob storage backend.";
771           default = null;
772         };
773         accountKey = lib.mkOption {
774           type = lib.types.nullOr lib.types.str;
775           description = "Account key for the Azure Blob storage backend. Warning: this is stored in plain text in the config file.";
776           default = null;
777         };
778         containerName = lib.mkOption {
779           type = lib.types.nullOr lib.types.str;
780           description = "Container name for the Azure Blob storage backend.";
781           default = null;
782         };
783       };
785       external = {
786         url = lib.mkOption {
787           type = lib.types.nullOr lib.types.str;
788           description = "URL of the backend storage layer.";
789           example = "https://athens.example.com";
790           default = null;
791         };
792       };
793     };
795     index = {
796       mysql = {
797         protocol = lib.mkOption {
798           type = lib.types.str;
799           description = "Protocol for the MySQL database.";
800           default = "tcp";
801         };
802         host = lib.mkOption {
803           type = lib.types.str;
804           description = "Host for the MySQL database.";
805           default = "localhost";
806         };
807         port = lib.mkOption {
808           type = lib.types.int;
809           description = "Port for the MySQL database.";
810           default = 3306;
811         };
812         user = lib.mkOption {
813           type = lib.types.str;
814           description = "User for the MySQL database.";
815           default = "root";
816         };
817         password = lib.mkOption {
818           type = lib.types.nullOr lib.types.str;
819           description = "Password for the MySQL database. Warning: this is stored in plain text in the config file.";
820           default = null;
821         };
822         database = lib.mkOption {
823           type = lib.types.str;
824           description = "Database name for the MySQL database.";
825           default = "athens";
826         };
827         params = {
828           parseTime = lib.mkOption {
829             type = lib.types.nullOr lib.types.str;
830             description = "Parse time for the MySQL database.";
831             default = "true";
832           };
833           timeout = lib.mkOption {
834             type = lib.types.nullOr lib.types.str;
835             description = "Timeout for the MySQL database.";
836             default = "30s";
837           };
838         };
839       };
841       postgres = {
842         host = lib.mkOption {
843           type = lib.types.str;
844           description = "Host for the Postgres database.";
845           default = "localhost";
846         };
847         port = lib.mkOption {
848           type = lib.types.int;
849           description = "Port for the Postgres database.";
850           default = 5432;
851         };
852         user = lib.mkOption {
853           type = lib.types.str;
854           description = "User for the Postgres database.";
855           default = "postgres";
856         };
857         password = lib.mkOption {
858           type = lib.types.nullOr lib.types.str;
859           description = "Password for the Postgres database. Warning: this is stored in plain text in the config file.";
860           default = null;
861         };
862         database = lib.mkOption {
863           type = lib.types.str;
864           description = "Database name for the Postgres database.";
865           default = "athens";
866         };
867         params = {
868           connect_timeout = lib.mkOption {
869             type = lib.types.nullOr lib.types.str;
870             description = "Connect timeout for the Postgres database.";
871             default = "30s";
872           };
873           sslmode = lib.mkOption {
874             type = lib.types.nullOr lib.types.str;
875             description = "SSL mode for the Postgres database.";
876             default = "disable";
877           };
878         };
879       };
880     };
882     extraConfig = lib.mkOption {
883       type = lib.types.attrs;
884       description = ''
885         Extra configuration options for the athens config file.
886       '';
887       default = { };
888     };
889   };
891   config = lib.mkIf cfg.enable {
892     systemd.services.athens = {
893       description = "Athens Go module proxy";
894       documentation = [ "https://docs.gomods.io" ];
896       wantedBy = [ "multi-user.target" ];
897       after = [ "network-online.target" ];
898       wants = [ "network-online.target" ];
900       serviceConfig = {
901         Restart = "on-abnormal";
902         Nice = 5;
903         ExecStart = ''${cfg.package}/bin/athens -config_file=${configFile}'';
905         KillMode = "mixed";
906         KillSignal = "SIGINT";
907         TimeoutStopSec = cfg.shutdownTimeout;
909         LimitNOFILE = 1048576;
910         LimitNPROC = 512;
912         DynamicUser = true;
913         PrivateTmp = true;
914         PrivateDevices = true;
915         ProtectHome = "read-only";
916         ProtectSystem = "full";
918         ReadWritePaths = lib.mkIf (cfg.storage.disk.rootPath != null && (! lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath)) [ cfg.storage.disk.rootPath ];
919         StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath) [ (lib.removePrefix "/var/lib/" cfg.storage.disk.rootPath) ];
921         CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
922         AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
923         NoNewPrivileges = true;
924       };
925     };
927     networking.firewall = {
928       allowedTCPPorts = lib.optionals (cfg.unixSocket == null) [ cfg.port ]
929         ++ lib.optionals cfg.enablePprof [ cfg.pprofPort ];
930     };
931   };