From 337f38d58efcb3b87871b17a0ebda34ef8c1c3d7 Mon Sep 17 00:00:00 2001 From: Kawa Date: Wed, 12 Aug 2026 12:58:07 +0200 Subject: [PATCH] feat: add systemd, nginx and Caddy deployment configs Serve src/ directly from the web server with no Node process involved. The Caddyfile is the shortest path to TLS, which every non-localhost deployment needs: on a plain-HTTP LAN address Chromium drops WebCodecs and blocks IndexedDB, so export slows to real-time capture and projects stop saving. Co-Authored-By: Claude Opus 5 (1M context) --- deploy/Caddyfile | 27 +++++++++++++++++++++++ deploy/motionity.service | 43 ++++++++++++++++++++++++++++++++++++ deploy/nginx.conf | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 deploy/Caddyfile create mode 100644 deploy/motionity.service create mode 100644 deploy/nginx.conf diff --git a/deploy/Caddyfile b/deploy/Caddyfile new file mode 100644 index 0000000..3b590bc --- /dev/null +++ b/deploy/Caddyfile @@ -0,0 +1,27 @@ +# Serves src/ directly — no Node process needed, and Caddy gets a real +# certificate automatically, which the app requires away from localhost: +# WebCodecs (fast export) and IndexedDB (project saving) are secure-context +# only, so plain http:// on a LAN address silently degrades both. +# +# sudo caddy run --config deploy/Caddyfile +# +# Replace the site address with your hostname. For a purely local install use +# `localhost` and Caddy will install its own trusted certificate. + +motionity.example.com { + root * /opt/motionity/src + file_server + + encode zstd gzip + + @static { + path *.js *.css *.svg *.png *.jpg *.jpeg *.gif *.webp *.woff2 *.woff *.mp3 *.wav *.mp4 *.webm + } + header @static Cache-Control "public, max-age=604800" + header /index.html Cache-Control "no-cache" + + header { + X-Content-Type-Options nosniff + Referrer-Policy no-referrer + } +} diff --git a/deploy/motionity.service b/deploy/motionity.service new file mode 100644 index 0000000..bf04464 --- /dev/null +++ b/deploy/motionity.service @@ -0,0 +1,43 @@ +; Bare-metal unit for the built-in Node server. +; +; Install: +; sudo useradd --system --home /opt/motionity --shell /usr/sbin/nologin motionity +; sudo cp -r . /opt/motionity && sudo chown -R motionity: /opt/motionity +; sudo -u motionity node /opt/motionity/scripts/vendor.mjs +; sudo cp deploy/motionity.service /etc/systemd/system/ +; sudo systemctl enable --now motionity +; +; Binds to loopback by default. To serve other machines, put a TLS reverse +; proxy in front (deploy/nginx.conf or deploy/Caddyfile) — WebCodecs and +; IndexedDB are unavailable over plain http:// on a non-localhost origin. + +[Unit] +Description=Motionity static server +After=network.target + +[Service] +Type=simple +User=motionity +Group=motionity +WorkingDirectory=/opt/motionity +Environment=HOST=127.0.0.1 +Environment=PORT=8080 +ExecStart=/usr/bin/node /opt/motionity/scripts/server.cjs +Restart=on-failure +RestartSec=2 + +# The process only ever reads static files. +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ProtectHome=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictAddressFamilies=AF_INET AF_INET6 +RestrictNamespaces=true +MemoryDenyWriteExecute=false +LockPersonality=true + +[Install] +WantedBy=multi-user.target diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..db267c9 --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,47 @@ +# Serves src/ directly. Drop into /etc/nginx/sites-available/motionity and +# point ssl_certificate at your own files. +# +# TLS is not decoration here: WebCodecs (the fast exporter) and IndexedDB +# (project saving) are secure-context only, so the app loses both when reached +# over plain http:// on anything other than localhost. + +server { + listen 80; + listen [::]:80; + server_name motionity.example.com; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name motionity.example.com; + + ssl_certificate /etc/letsencrypt/live/motionity.example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/motionity.example.com/privkey.pem; + + root /opt/motionity/src; + index index.html; + + # The audio and video panels seek, which needs byte ranges (on by default). + # Large media should not be buffered through gzip. + gzip on; + gzip_types text/css text/javascript application/javascript application/json image/svg+xml; + gzip_min_length 1024; + + add_header X-Content-Type-Options nosniff always; + add_header Referrer-Policy no-referrer always; + + location = /index.html { + add_header Cache-Control "no-cache" always; + } + + location ~* \.(js|css|svg|png|jpe?g|gif|webp|woff2?|mp3|wav|mp4|webm)$ { + add_header Cache-Control "public, max-age=604800" always; + } + + location / { + try_files $uri $uri/ =404; + } +}