summaryrefslogtreecommitdiff
path: root/hostnix/elmo
diff options
context:
space:
mode:
Diffstat (limited to 'hostnix/elmo')
-rw-r--r--hostnix/elmo/configuration.nix1
-rw-r--r--hostnix/elmo/typetype.nix158
2 files changed, 159 insertions, 0 deletions
diff --git a/hostnix/elmo/configuration.nix b/hostnix/elmo/configuration.nix
index a230b31..ad44fa2 100644
--- a/hostnix/elmo/configuration.nix
+++ b/hostnix/elmo/configuration.nix
@@ -19,6 +19,7 @@
./rss.nix
./syncthing.nix
./system.nix
+ ./typetype.nix
./usenet.nix
./web.nix
./wireguard.nix
diff --git a/hostnix/elmo/typetype.nix b/hostnix/elmo/typetype.nix
new file mode 100644
index 0000000..e94f5f8
--- /dev/null
+++ b/hostnix/elmo/typetype.nix
@@ -0,0 +1,158 @@
+{ lib, pkgs, ... }:
+
+# Self-hosted TypeType instance: https://github.com/TypeType-Video/TypeType
+#
+# Upstream only ships container images, so this is a translation of their
+# docker-compose.yml rather than a native service. Omitted from the upstream
+# stack: typetype-downloader, garage, garage-config (the download/S3
+# subsystem) and typetype-secrets (replaced by /var/secrets, below).
+
+# TODO downloads: needs typetype-downloader + a Garage bucket bootstrapped by
+# hand (scripts/bootstrap-garage.sh does layout assign / bucket create / key
+# create), plus the typetype_downloader database.
+# TODO SSO
+
+let
+ network = "typetype";
+
+ # The frontend image's nginx resolves these names over Docker's embedded DNS
+ # (resolver 127.0.0.11), so retain the original container names.
+ containers = [
+ "typetype"
+ "typetype-server"
+ "typetype-token"
+ "typetype-postgres"
+ "typetype-dragonfly"
+ ];
+
+ # Pin by version tag and digest.
+ images = {
+ web = "ghcr.io/typetype-video/typetype:1.3.1@sha256:4da200fb96d858cfa3bc2a8cbb98a9682a560f40a055b9c407f3e173a28dcf82";
+ server = "ghcr.io/typetype-video/typetype-server:1.3.1@sha256:f1ad7fd31e5c1cb994601f714df82e8207c3769d759df232e21a3876751a8faf";
+ token = "ghcr.io/typetype-video/typetype-token:1.3.1@sha256:8dfcc6d84cc09c33d18add0ec807093c2182be10857a021a4c61ace9a3f561d5";
+ };
+
+ secrets = "/var/secrets/typetype";
+in
+
+{
+ systemd.tmpfiles.rules = [
+ "d /var/lib/typetype 0750 root root -"
+ # Bind mounted rather than a Docker volume so backup.nix picks it up; 999
+ # is the postgres uid inside the image.
+ "d /var/lib/typetype/postgres 0700 999 999 -"
+ "d ${secrets} 0750 root root -"
+ ];
+
+ systemd.services =
+ lib.genAttrs (map (c: "docker-${c}") containers) (_: {
+ after = [ "docker-network-typetype.service" ];
+ requires = [ "docker-network-typetype.service" ];
+ unitConfig.AssertPathExists = "${secrets}/env";
+ })
+ // {
+ # Initially create network.
+ docker-network-typetype = {
+ wantedBy = [ "multi-user.target" ];
+ after = [ "docker.service" ];
+ requires = [ "docker.service" ];
+ path = [ pkgs.docker ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ };
+ script = ''
+ docker network inspect ${network} >/dev/null 2>&1 ||
+ docker network create ${network}
+ '';
+ };
+ };
+
+ virtualisation.oci-containers.containers = {
+ typetype = {
+ image = images.web;
+ networks = [ network ];
+ dependsOn = [
+ "typetype-server"
+ "typetype-token"
+ ];
+ ports = [ "127.0.0.1:8082:80" ];
+ };
+
+ typetype-server = {
+ image = images.server;
+ networks = [ network ];
+ dependsOn = [
+ "typetype-postgres"
+ "typetype-dragonfly"
+ "typetype-token"
+ ];
+ # Sets DATABASE_PASSWORD.
+ environmentFiles = [ "${secrets}/env" ];
+ environment = {
+ ALLOWED_ORIGINS = "https://tt.elmo.mou.fo";
+ DATABASE_URL = "jdbc:postgresql://typetype-postgres:5432/typetype";
+ DATABASE_USER = "typetype";
+ DRAGONFLY_URL = "redis://typetype-dragonfly:6379";
+ YOUTUBE_REMOTE_LOGIN_ENABLED = "false";
+ YOUTUBE_REMOTE_LOGIN_SERVICE_URL = "http://typetype-token:8081";
+ YOUTUBE_REMOTE_LOGIN_CALLBACK_BASE_URL = "http://typetype-server:8080";
+ YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN_FILE = "/run/typetype-secrets/youtube_remote_login_internal_token";
+ YOUTUBE_SESSION_ENCRYPTION_KEY_FILE = "/run/typetype-secrets/youtube_session_encryption_key";
+ };
+ volumes = [ "${secrets}:/run/typetype-secrets:ro" ];
+ };
+
+ typetype-token = {
+ image = images.token;
+ networks = [ network ];
+ environment = {
+ NODE_ENV = "production";
+ YOUTUBE_REMOTE_LOGIN_ENABLED = "false";
+ YOUTUBE_REMOTE_LOGIN_INTERNAL_TOKEN_FILE = "/run/typetype-secrets/youtube_remote_login_internal_token";
+ };
+ volumes = [ "${secrets}:/run/typetype-secrets:ro" ];
+ # --ipc=host is upstream's; it only matters once remote login is enabled
+ # and the service starts driving a headless browser.
+ extraOptions = [
+ "--init"
+ "--ipc=host"
+ ];
+ };
+
+ typetype-postgres = {
+ image = "postgres:17";
+ networks = [ network ];
+ # Sets POSTGRES_PASSWORD.
+ environmentFiles = [ "${secrets}/env" ];
+ environment = {
+ POSTGRES_DB = "typetype";
+ POSTGRES_USER = "typetype";
+ };
+ volumes = [ "/var/lib/typetype/postgres:/var/lib/postgresql/data" ];
+ };
+
+ typetype-dragonfly = {
+ image = "docker.dragonflydb.io/dragonflydb/dragonfly:v1.39.0";
+ networks = [ network ];
+ extraOptions = [
+ "--ulimit"
+ "memlock=-1"
+ ];
+ };
+ };
+
+ # TODO allocate domain
+ services.nginx.virtualHosts."tt.elmo.mou.fo" = {
+ useACMEHost = "elmo.mou.fo";
+ forceSSL = true;
+ locations."/" = {
+ proxyPass = "http://127.0.0.1:8082";
+ proxyWebsockets = true;
+ };
+ # Matches client_max_body_size in the frontend image's nginx.conf.
+ extraConfig = ''
+ client_max_body_size 2g;
+ '';
+ };
+}