commit 7055991a7e134dcd6f5d4965bc9acd6ac9ca08a2
parent 5ec9e9bb454dbc99a6ddf2245b1de12a72febf85
Author: Andreas Gruhler <agruhl@gmx.ch>
Date: Sat, 2 Aug 2025 23:14:59 +0200
feat(snac): add snac 🍕 job
Diffstat:
6 files changed, 294 insertions(+), 0 deletions(-)
diff --git a/hcl/default/snac/data-volume.hcl b/hcl/default/snac/data-volume.hcl
@@ -0,0 +1,31 @@
+# Register external nfs volume with Nomad CSI
+# https://www.nomadproject.io/docs/commands/volume/register
+type = "csi"
+# Unique ID of the volume, volume.source field in a job
+id = "snac"
+# Display name of the volume.
+name = "snac"
+# ID of the physical volume from the storage provider
+external_id = "csi-snac"
+plugin_id = "nfs"
+
+# You must provide at least one capability block
+# You must provide a block for each capability
+# youintend to use in a job's volume block
+# https://www.nomadproject.io/docs/commands/volume/register
+capability {
+ access_mode = "multi-node-multi-writer"
+ attachment_mode = "file-system"
+}
+
+# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md
+context {
+ server = "turris"
+ share = "csi-snac"
+}
+
+mount_options {
+ # mount.nfs: Either use '-o nolock' to keep locks local, or start statd.
+ mount_flags = ["nolock"]
+}
+
diff --git a/hcl/default/snac/snac.nomad b/hcl/default/snac/snac.nomad
@@ -0,0 +1,107 @@
+# https://git.snac.social/snac/snac-docker-compose
+
+job "snac" {
+ datacenters = ["dc1"]
+
+ vault {}
+
+ group "server" {
+ count = 1
+
+ volume "tls" {
+ type = "csi"
+ source = "certbot"
+ access_mode = "multi-node-multi-writer"
+ attachment_mode = "file-system"
+ }
+ volume "snac" {
+ type = "csi"
+ source = "snac"
+ access_mode = "multi-node-multi-writer"
+ attachment_mode = "file-system"
+ }
+
+ network {
+ port "http" {
+ to = 8001
+ }
+ port "https" {
+ static = 44411
+ }
+ }
+
+ task "nginx" {
+ driver = "podman"
+
+ config {
+ image = "docker.io/library/nginx:stable-alpine"
+ ports = ["https"]
+ volumes = [
+ # mount the templated config from the task directory to the container
+ "local/snac.conf:/etc/nginx/conf.d/snac.conf",
+ ]
+ }
+
+ volume_mount {
+ volume = "tls"
+ destination = "/etc/letsencrypt"
+ }
+
+ template {
+ destination = "${NOMAD_TASK_DIR}/snac.conf"
+ data = file("./templates/nginx.conf.tmpl")
+ }
+
+ resources {
+ memory = 50
+ memory_max = 256
+ cpu = 200
+ }
+ }
+
+ task "snac" {
+ driver = "podman"
+
+ config {
+ image = "127.0.0.1:5000/snac:2.81"
+ force_pull = true
+ ports = ["http"]
+ volumes = [
+ # mount the templated config from the task directory to the container
+ "local/greeting.html:/data/data/greeting.html",
+ "local/server.json:/data/data/server.json",
+ "local/style.css:/data/data/style.css"
+ ]
+ }
+
+ env {
+ # https://comam.es/snac-doc/snac.8.html#ENVIRONMENT
+ #DEBUG = 100
+ }
+
+ volume_mount {
+ volume = "snac"
+ destination = "/data/data"
+ }
+
+ template {
+ destination = "${NOMAD_TASK_DIR}/greeting.html"
+ data = file("./templates/greeting.html.tmpl")
+ }
+ template {
+ destination = "${NOMAD_TASK_DIR}/server.json"
+ data = file("./templates/server.json.tmpl")
+ }
+ template {
+ destination = "${NOMAD_TASK_DIR}/style.css"
+ data = file("./templates/style.css.tmpl")
+ }
+
+ resources {
+ memory = 512
+ memory_max = 1024
+ cpu = 500
+ }
+ }
+ }
+}
diff --git a/hcl/default/snac/templates/greeting.html.tmpl b/hcl/default/snac/templates/greeting.html.tmpl
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html><head>
+<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<link rel="icon" type="image/x-icon" href="https://%host%/favicon.ico"/>
+<style>*{color-scheme:light dark}body{margin:auto;max-width:50em}</style>
+<title>Welcome to %host%</title>
+</head>
+<body>
+<p><b>%host%</b> is a <a href="https://en.wikipedia.org/wiki/Fediverse">Fediverse</a> instance that uses the <a href="https://en.wikipedia.org/wiki/ActivityPub">ActivityPub</a> protocol. In other words, users at this host can communicate with people that use software like Mastodon, Pleroma, Friendica, etc. all around the world.</p>
+<p>This server runs the <a href="https://comam.es/what-is-snac">snac</a> software and there is no automatic sign-up process.</p>
+<p>The following users are part of this community:</p>
+
+%userlist%
+
+<p>This site is powered by <abbr title="Social Networks Are Crap">snac</abbr>.</p>
+</body></html>
diff --git a/hcl/default/snac/templates/nginx.conf.tmpl b/hcl/default/snac/templates/nginx.conf.tmpl
@@ -0,0 +1,72 @@
+# https://comam.es/snac-doc/snac.8.html#EXAMPLES
+# https://codeberg.org/grunfink/snac2/src/branch/master/examples/nginx-alpine-ssl/default.conf
+
+upstream snac {
+ server {{ env "NOMAD_ADDR_http" }} max_fails=5 fail_timeout=60s;
+}
+
+server {
+ listen {{ env "NOMAD_PORT_https" }} ssl http2 default_server;
+ listen [::]:{{ env "NOMAD_PORT_https" }} ssl http2 default_server;
+
+ # SSL configuration
+ ssl_certificate /etc/letsencrypt/live/m.in0rdr.ch/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/m.in0rdr.ch/privkey.pem;
+
+ location /.well-known/webfinger {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_redirect off;
+ proxy_connect_timeout 90;
+ proxy_send_timeout 90;
+ proxy_read_timeout 90;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Proxy "";
+ proxy_pass_header Server;
+ proxy_buffering on;
+ tcp_nodelay on;
+ proxy_pass http://snac;
+ }
+
+ location /.well-known/nodeinfo {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_redirect off;
+ proxy_connect_timeout 90;
+ proxy_send_timeout 90;
+ proxy_read_timeout 90;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Proxy "";
+ proxy_pass_header Server;
+ proxy_buffering on;
+ tcp_nodelay on;
+ proxy_pass http://snac;
+ }
+
+ location / {
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_redirect off;
+ proxy_connect_timeout 90;
+ proxy_send_timeout 90;
+ proxy_read_timeout 90;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Proxy "";
+ proxy_pass_header Server;
+ proxy_buffering on;
+ tcp_nodelay on;
+ proxy_pass http://snac;
+ }
+}
diff --git a/hcl/default/snac/templates/server.json.tmpl b/hcl/default/snac/templates/server.json.tmpl
@@ -0,0 +1,28 @@
+{
+ "host": "m.in0rdr.ch",
+ "prefix": "",
+ "address": "0.0.0.0",
+ "port": 8001,
+ "layout": 2.7,
+ "dbglevel": 0,
+ "queue_retry_minutes": 2,
+ "queue_retry_max": 10,
+ "queue_timeout": 6,
+ "queue_timeout_2": 8,
+ "cssurls": [
+ ""
+ ],
+ "def_timeline_entries": 50,
+ "max_timeline_entries": 50,
+ "timeline_purge_days": 120,
+ "local_purge_days": 0,
+ "min_account_age": 0,
+ "admin_email": "",
+ "admin_account": "in0rdr",
+ "title": "",
+ "short_description": "",
+ "short_description_raw": false,
+ "protocol": "https",
+ "fastcgi": false,
+ "disable_sandbox": false
+}
diff --git a/hcl/default/snac/templates/style.css.tmpl b/hcl/default/snac/templates/style.css.tmpl
@@ -0,0 +1,39 @@
+body { max-width: 48em; margin: auto; line-height: 1.5; padding: 0.8em; word-wrap: break-word; }
+pre { overflow-x: scroll; }
+blockquote { font-style: italic; }
+.snac-embedded-video, img { max-width: 100% }
+.snac-origin { font-size: 85% }
+.snac-score { float: right; font-size: 85% }
+.snac-top-user { text-align: center; padding-bottom: 2em }
+.snac-top-user-name { font-size: 200% }
+.snac-top-user-id { font-size: 150% }
+.snac-announcement { border: black 1px solid; padding: 0.5em }
+.snac-avatar { float: left; height: 2.5em; width: 2.5em; padding: 0.25em }
+.snac-author { font-size: 90%; text-decoration: none }
+.snac-author-tag { font-size: 80% }
+.snac-pubdate { color: #a0a0a0; font-size: 90% }
+.snac-top-controls { padding-bottom: 1.5em }
+.snac-post { border-top: 1px solid #a0a0a0; padding-top: 0.5em; padding-bottom: 0.5em; }
+.snac-children { padding-left: 1em; border-left: 1px solid #a0a0a0; }
+.snac-thread-cont { border-top: 1px dashed #a0a0a0; }
+.snac-textarea { font-family: inherit; width: 100% }
+.snac-history { border: 1px solid #606060; border-radius: 3px; margin: 2.5em 0; padding: 0 2em }
+.snac-btn-mute { float: right; margin-left: 0.5em }
+.snac-btn-unmute { float: right; margin-left: 0.5em }
+.snac-btn-follow { float: right; margin-left: 0.5em }
+.snac-btn-unfollow { float: right; margin-left: 0.5em }
+.snac-btn-hide { float: right; margin-left: 0.5em }
+.snac-btn-delete { float: right; margin-left: 0.5em }
+.snac-btn-limit { float: right; margin-left: 0.5em }
+.snac-btn-unlimit { float: right; margin-left: 0.5em }
+.snac-footer { margin-top: 2em; font-size: 75% }
+.snac-poll-result { margin-left: auto; margin-right: auto; }
+.snac-list-of-lists { padding-left: 0; }
+.snac-list-of-lists li { display: inline; border: 1px solid #a0a0a0; border-radius: 25px;
+ margin-right: 0.5em; padding-left: 0.5em; padding-right: 0.5em; }
+.snac-no-more-unseen-posts { border-top: 1px solid #a0a0a0; border-bottom: 1px solid #a0a0a0; padding: 0.5em 0; margin: 1em 0; }
+@media (prefers-color-scheme: dark) {
+ body, input, textarea { background-color: #000; color: #fff; }
+ a { color: #7799dd }
+ a:visited { color: #aa99dd }
+}
+\ No newline at end of file