Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add CI/CD pipeline and Nomad deployment for tailnet-only hosting

Dockerfile: multi-stage Node 22 build + Caddy static file server
Caddyfile: MPA routing for all 7 editor entry points + immutable asset caching
Nomad job: Tailscale sidecar on big-nix → atmosphere-docs.lobster-hake.ts.net
Gitea Actions: test → build → deploy → verify pipeline
service.json: tailnet service contract metadata

Requires Nomad host volume and variable setup before first deploy.

+196 -1
+11
.dockerignore
··· 1 + node_modules 2 + dist 3 + .git 4 + .chainlink 5 + .claude 6 + deploy 7 + tests 8 + *.test.ts 9 + vitest.config.* 10 + playwright.config.* 11 + tsconfig.json
+41
Caddyfile
··· 1 + :8080 { 2 + root * /srv 3 + encode gzip 4 + 5 + # MPA entry points — rewrite clean URLs to their index.html 6 + @docs path /docs /docs/* 7 + @sheets path /sheets /sheets/* 8 + @forms path /forms /forms/* 9 + @slides path /slides /slides/* 10 + @diagrams path /diagrams /diagrams/* 11 + @calendar path /calendar /calendar/* 12 + 13 + handle @docs { 14 + try_files {path} {path}/ /docs/index.html 15 + } 16 + handle @sheets { 17 + try_files {path} {path}/ /sheets/index.html 18 + } 19 + handle @forms { 20 + try_files {path} {path}/ /forms/index.html 21 + } 22 + handle @slides { 23 + try_files {path} {path}/ /slides/index.html 24 + } 25 + handle @diagrams { 26 + try_files {path} {path}/ /diagrams/index.html 27 + } 28 + handle @calendar { 29 + try_files {path} {path}/ /calendar/index.html 30 + } 31 + 32 + # Hashed assets are immutable 33 + @immutable path /assets/* 34 + header @immutable Cache-Control "public, max-age=31536000, immutable" 35 + 36 + handle { 37 + try_files {path} {path}/ /index.html 38 + } 39 + 40 + file_server 41 + }
+11
Dockerfile
··· 1 + FROM node:22-alpine AS builder 2 + WORKDIR /app 3 + COPY package*.json ./ 4 + RUN npm ci 5 + COPY . . 6 + RUN npm run build 7 + 8 + FROM caddy:2-alpine 9 + COPY Caddyfile /etc/caddy/Caddyfile 10 + COPY --from=builder /app/dist /srv 11 + EXPOSE 8080
+132
deploy/atmosphere-docs.nomad.hcl
··· 1 + job "atmosphere-docs" { 2 + type = "service" 3 + datacenters = ["homelab"] 4 + priority = 50 5 + 6 + constraint { 7 + attribute = "${node.unique.name}" 8 + operator = "=" 9 + value = "big-nix" 10 + } 11 + 12 + update { 13 + max_parallel = 1 14 + min_healthy_time = "30s" 15 + healthy_deadline = "5m" 16 + auto_revert = true 17 + } 18 + 19 + group "atmosphere-docs" { 20 + count = 1 21 + 22 + network { 23 + mode = "bridge" 24 + port "http" { 25 + to = 8080 26 + } 27 + } 28 + 29 + volume "tailscale-atmosphere-docs" { 30 + type = "host" 31 + source = "tailscale-atmosphere-docs" 32 + read_only = false 33 + } 34 + 35 + restart { 36 + attempts = 3 37 + interval = "30m" 38 + delay = "15s" 39 + mode = "fail" 40 + } 41 + 42 + reschedule { 43 + delay = "30s" 44 + delay_function = "exponential" 45 + max_delay = "1h" 46 + unlimited = true 47 + } 48 + 49 + ephemeral_disk { 50 + size = 300 51 + } 52 + 53 + service { 54 + provider = "nomad" 55 + name = "atmosphere-docs" 56 + port = "http" 57 + 58 + check { 59 + type = "http" 60 + path = "/" 61 + interval = "30s" 62 + timeout = "5s" 63 + } 64 + } 65 + 66 + task "tailscale" { 67 + driver = "docker" 68 + 69 + lifecycle { 70 + hook = "prestart" 71 + sidecar = true 72 + } 73 + 74 + config { 75 + image = "gitea.lobster-hake.ts.net/lanos-familia/tailscale-proxy:1.94.1-r8" 76 + privileged = true 77 + } 78 + 79 + env { 80 + TS_HOSTNAME = "atmosphere-docs" 81 + TS_SERVE_PORT = "8080" 82 + TS_TAGS = "tag:server" 83 + } 84 + 85 + resources { 86 + cpu = 50 87 + memory = 256 88 + } 89 + 90 + volume_mount { 91 + volume = "tailscale-atmosphere-docs" 92 + destination = "/var/lib/tailscale" 93 + read_only = false 94 + } 95 + 96 + template { 97 + data = <<-EOT 98 + {{ with nomadVar "nomad/jobs/atmosphere-docs" }} 99 + TS_AUTHKEY={{ .ts_authkey }} 100 + {{ end }} 101 + EOT 102 + destination = "secrets/tailscale.env" 103 + env = true 104 + } 105 + 106 + template { 107 + data = <<-EOT 108 + {{ with nomadVar "nomad/jobs/ts-oauth" }} 109 + TS_OAUTH_CLIENT_ID={{ .ts_oauth_client_id }} 110 + TS_OAUTH_CLIENT_SECRET={{ .ts_oauth_client_secret }} 111 + {{ end }} 112 + EOT 113 + destination = "secrets/ts-oauth.env" 114 + env = true 115 + } 116 + } 117 + 118 + task "atmosphere-docs" { 119 + driver = "docker" 120 + 121 + config { 122 + image = "gitea.lobster-hake.ts.net/lanos-familia/atmosphere-docs:latest" 123 + force_pull = true 124 + } 125 + 126 + resources { 127 + cpu = 200 128 + memory = 128 129 + } 130 + } 131 + } 132 + }
+1 -1
src/index.html
··· 181 181 </div> 182 182 </div> 183 183 184 - <div class="version-badge">v%APP_VERSION%</div> 184 + <div class="version-badge tailnet-footer">v%APP_VERSION%</div> 185 185 186 186 <!-- Drag-and-drop import overlay --> 187 187 <div class="drop-overlay" id="drop-overlay" style="display:none;">