···11-let
22- domain = "maze.lassul.us";
33- port = 8772;
44-in
55-{ self, config, ... }:
66-{
77- # mazegame: Windows 95 "Maze" screensaver you can play in a browser tab. The
88- # exit is the NixOS snowflake. /watch is a spectator camera that follows a
99- # random player and cuts to the next one after 2s without movement.
1010- imports = [ self.inputs.mazegame.nixosModules.default ];
1111-1212- services.mazegame = {
1313- enable = true;
1414- host = "127.0.0.1";
1515- port = port;
1616- };
1717-1818- # One address may hold a household's worth of tabs and join at a human
1919- # pace. Without this a single host opened ~2500 sockets at once and churned
2020- # thousands more, and the thread-per-socket server ran out of tasks for
2121- # everyone else.
2222- services.nginx.appendHttpConfig = ''
2323- limit_conn_zone $binary_remote_addr zone=mazegame_conns:10m;
2424- limit_req_zone $binary_remote_addr zone=mazegame_joins:10m rate=2r/s;
2525- '';
2626-2727- services.nginx.virtualHosts.${domain} = {
2828- enableACME = true;
2929- forceSSL = true;
3030- # nginx hands out the client straight from the store — the very package
3131- # the service runs, so the page never lags the server. The game is a
3232- # single event loop: every .js it serves during a join burst is a tick it
3333- # is not spending on the world, and a crowd arriving costs six files each.
3434- root = config.services.mazegame.package.static;
3535- locations."/" = {
3636- index = "index.html";
3737- tryFiles = "$uri $uri/ =404";
3838- # Versions ride in the store path but the URLs never change, so let the
3939- # browser revalidate against the ETag rather than cache a stale client.
4040- # add_header does not inherit into the exact-match locations below.
4141- extraConfig = ''add_header Cache-Control "no-cache";'';
4242- };
4343- locations."= /play" = {
4444- tryFiles = "/index.html =404";
4545- extraConfig = ''add_header Cache-Control "no-cache";'';
4646- };
4747- locations."= /watch" = {
4848- tryFiles = "/watch.html =404";
4949- extraConfig = ''add_header Cache-Control "no-cache";'';
5050- };
5151-5252- # Player and watcher sockets stay open for as long as someone is in the
5353- # maze; players heartbeat every 3s, watchers every 3s, so the default
5454- # 60s read timeout would only bite on a wedged client, but a long window
5555- # keeps idle spectators connected.
5656- locations."/ws/" = {
5757- proxyPass = "http://127.0.0.1:${toString port}";
5858- proxyWebsockets = true;
5959- extraConfig = ''
6060- proxy_read_timeout 1h;
6161- proxy_send_timeout 1h;
6262- limit_conn mazegame_conns 32;
6363- limit_req zone=mazegame_joins burst=30 nodelay;
6464- limit_conn_status 429;
6565- limit_req_status 429;
6666- '';
6767- };
6868- locations."/api/" = {
6969- proxyPass = "http://127.0.0.1:${toString port}";
7070- };
7171- };
7272-}
-38
2configs/nixcon-quiz.nix
···11-let
22- domain = "quiz.nixcon.org";
33- port = 8773;
44-in
55-{ self, ... }:
66-{
77- # nixcon-quiz: live multiplayer quiz for NixCon. Everyone answers the same
88- # question against the same server clock; /spectate is the 16:9 screen for
99- # the livestream with a QR code to join.
1010- #
1111- # The questions are not part of any repository (whoever reads them knows
1212- # every answer) and stay out of the world-readable store. Copy them over
1313- # after deploying; until then the service is skipped:
1414- # rsync -r --delete ~/src/nixcon-quiz/questions/ root@neoprism.r:/var/lib/nixcon-quiz/questions/
1515- # ssh root@neoprism.r systemctl restart nixcon-quiz
1616- # Break slides for /spectate (PDF/PNG/JPEG/WebP, not in any repository
1717- # either) are picked up without a restart:
1818- # rsync -r --delete ~/src/nixcon-quiz/slides/ root@neoprism.r:/var/lib/nixcon-quiz/slides/
1919- imports = [ self.inputs.nixcon-quiz.nixosModules.default ];
2020-2121- services.nixcon-quiz = {
2222- enable = true;
2323- host = "127.0.0.1";
2424- inherit port domain;
2525- questionSeconds = 30;
2626- };
2727-2828- # No per-address limits on the event streams: the venue Wi-Fi puts every
2929- # phone at NixCon behind one public address, so a per-IP cap cuts off
3030- # everyone past the first few players. The quiz caps players itself.
3131-3232- # The quiz's first home; old links and QR codes land on the new name.
3333- services.nginx.virtualHosts."quiz.lassul.us" = {
3434- enableACME = true;
3535- forceSSL = true;
3636- globalRedirect = domain;
3737- };
3838-}
···11-@ 3600 IN SOA lassul.us. ns1.lassul.us. 2026092800 7200 3600 86400 3600
11+@ 3600 IN SOA lassul.us. ns1.lassul.us. 2026092801 7200 3600 86400 3600
2233;;@ 3600 IN NS ns1
44@ 3600 IN NS ns1
···5858id IN CNAME neoprism
5959covibe IN CNAME neoprism
6060omni IN CNAME neoprism
6161-maze IN CNAME neoprism
6261where.is IN CNAME neoprism
6362parley IN CNAME neoprism
6463