prometheus: deploy — systemd unit, angie vhost, TLS pin, deploy recipe
Everything the VPS needs to run Prometheus is in Prometheus/deploy/:
prometheus-service.service systemd unit, Node on 127.0.0.1:3700,
ReadWritePaths narrowed to the dirs the
service actually writes (db, uploads, keys,
build). Hardened with NoNewPrivileges,
PrivateTmp, ProtectSystem=strict,
ProtectHome. Reads /etc/default/prometheus
for instance env (admin name, VT key,
payment config) so the repo carries no
per-install secrets.
prometheus-x.conf angie vhost for the BNS name's apex.
TLS-terminated with a self-signed cert that
the on-chain `tls` record pins (no CA
involved). Reverse-proxies / to the Node
service, 2 GiB client_max_body_size for
POST /api/upload, proxy_request_buffering
off so large uploads do not spool to disk.
make-tls-cert.sh ECDSA P-256, 10-year expiry (the on-chain
pin is what actually expires, not the cert
metadata). Prints the DER sha256 that goes
in the `tls` record. Idempotent — rerun to
rotate.
README.md First-time install recipe (useradd →
rsync → npm install --omit=dev → make-tls
→ angie reload → systemctl enable --now),
the matching Argus register.js command for
the record change, update + rollback.
Execution — the deploy itself, the record change, enabling payments —
is the operator's call, not something a script here runs on its own.
This commit is contained in:
parent
34efb319cc
commit
40bf5fc641
4 changed files with 225 additions and 0 deletions
96
deploy/README.md
Normal file
96
deploy/README.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Prometheus — deploy
|
||||
|
||||
Everything in this directory lands on a VPS the first time Prometheus is set
|
||||
up on it, plus files that get swapped out on each release.
|
||||
|
||||
```
|
||||
deploy/
|
||||
prometheus-service.service systemd unit — the Node service on :3700
|
||||
prometheus-x.conf angie vhost — TLS-terminated reverse proxy
|
||||
prometheus-snapshot.service systemd unit — weekly snapshot job
|
||||
prometheus-snapshot.timer systemd timer — Sunday 04:00 UTC
|
||||
apps-silentmode-st.conf angie vhost — SEO mirror (apps.silentmode.st)
|
||||
make-tls-cert.sh (re-)generate the self-signed cert, print the pin
|
||||
```
|
||||
|
||||
## First-time install (VPS, root)
|
||||
|
||||
```bash
|
||||
# Code
|
||||
useradd -r -s /usr/sbin/nologin -d /opt/prometheus prometheus
|
||||
mkdir -p /opt/prometheus
|
||||
rsync -av --exclude=node_modules --exclude=db.sqlite\* --exclude=uploads \
|
||||
--exclude=.keys --exclude=build \
|
||||
<workstation>:Prometheus/ /opt/prometheus/
|
||||
cd /opt/prometheus && npm install --omit=dev
|
||||
chown -R prometheus:prometheus /opt/prometheus
|
||||
mkdir -p /opt/prometheus/{uploads,.keys,build}
|
||||
chown prometheus:prometheus /opt/prometheus/{uploads,.keys,build}
|
||||
|
||||
# TLS cert for prometheus.x (prints the fingerprint — save it for #6)
|
||||
sudo bash /opt/prometheus/deploy/make-tls-cert.sh
|
||||
|
||||
# angie
|
||||
install -m644 /opt/prometheus/deploy/prometheus-x.conf /etc/angie/http.d/
|
||||
install -m644 /opt/prometheus/deploy/apps-silentmode-st.conf /etc/angie/http.d/
|
||||
angie -t && systemctl reload angie
|
||||
|
||||
# systemd
|
||||
install -m644 /opt/prometheus/deploy/prometheus-service.service /etc/systemd/system/
|
||||
install -m644 /opt/prometheus/deploy/prometheus-snapshot.service /etc/systemd/system/
|
||||
install -m644 /opt/prometheus/deploy/prometheus-snapshot.timer /etc/systemd/system/
|
||||
|
||||
# Instance-specific environment (NOT in the repo)
|
||||
cat > /etc/default/prometheus <<EOF
|
||||
ADMIN_PUBLISHER=<a BNS name you own>
|
||||
# VIRUSTOTAL_API_KEY=<key> # optional
|
||||
# PAYMENT_REQUIRED=1 # enable paid wave
|
||||
# PROMETHEUS_PAYMENT_ADDRESS=<cashaddr> # required when PAYMENT_REQUIRED=1
|
||||
# PROMETHEUS_LISTING_FEE_USD=1
|
||||
EOF
|
||||
chmod 600 /etc/default/prometheus
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now prometheus-service.service
|
||||
systemctl enable --now prometheus-snapshot.timer
|
||||
|
||||
# Verify
|
||||
curl -k https://127.0.0.1/healthz # should return 200 and {ok:true,…}
|
||||
```
|
||||
|
||||
## Register the on-chain record (workstation)
|
||||
|
||||
```bash
|
||||
IP=$(curl -s https://ifconfig.io) # or the VPS's public IP as you know it
|
||||
TLS=<the fingerprint make-tls-cert.sh printed>
|
||||
|
||||
node Argus/src/register.js prometheus.x "{\"ip\":\"$IP\",\"tls\":\"$TLS\"}"
|
||||
# Prompts for the chipnet keystore password (or set DEVIANT_KEYSTORE_PASSWORD).
|
||||
```
|
||||
|
||||
The current record (as of 2026-10-08) is `{"u":"https://silentmode.st/prometheus/"}`.
|
||||
The register command above replaces it. Within a few minutes of the TX
|
||||
confirming on chipnet, Theseus and Ariadne resolve `prometheus.x` to the live
|
||||
service rather than the silentmode.st teaser.
|
||||
|
||||
## Updates
|
||||
|
||||
Code: rsync Prometheus/ again, `npm install --omit=dev` (safe no-op if deps
|
||||
unchanged), `systemctl restart prometheus-service`.
|
||||
|
||||
Snapshot: nothing to do — the timer runs itself.
|
||||
|
||||
Cert rotation: re-run make-tls-cert.sh, grab the new fingerprint, re-register
|
||||
the `tls` record, reload angie. Theseus and Ariadne pick up the new cert as
|
||||
soon as the record TX confirms.
|
||||
|
||||
## Rollback
|
||||
|
||||
Code: rsync the previous checkout, restart. There is no "schema down migration"
|
||||
discipline yet — the DB file is append-only in practice (new columns, new
|
||||
tables); if a rollback hits a schema change you have to decide what to do with
|
||||
the newer rows.
|
||||
|
||||
Record: `node Argus/src/register.js prometheus.x '{"u":"https://silentmode.st/prometheus/"}'`
|
||||
puts the name back to redirecting at the ICANN mirror. Takes the same few
|
||||
minutes as any other record change.
|
||||
47
deploy/make-tls-cert.sh
Normal file
47
deploy/make-tls-cert.sh
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generate a self-signed cert for prometheus.x and print its DER sha256.
|
||||
#
|
||||
# The sha256 is what you register as the `tls` record on chain for the name.
|
||||
# Theseus and Ariadne refuse any certificate whose DER sha256 does not match
|
||||
# that value, which is the whole point of the pinning model.
|
||||
#
|
||||
# Run on the VPS. Writes to /etc/ssl/prometheus-x/, so sudo.
|
||||
#
|
||||
# Usage:
|
||||
# sudo bash deploy/make-tls-cert.sh
|
||||
# -> prints: tls fingerprint: 9671bd5f44047f74203a76439430bda7bcf7c36d913c0fbd4cc75e24e374dde3
|
||||
#
|
||||
# To rotate: run again; it overwrites the files and prints the new fingerprint.
|
||||
# Then: node Argus/src/register.js prometheus.x '{"ip":"<ip>","tls":"<new fp>"}'
|
||||
# After the record TX confirms (~5 min on chipnet, longer on mainnet), the
|
||||
# clients see the new cert.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DIR=/etc/ssl/prometheus-x
|
||||
mkdir -p "$DIR"
|
||||
|
||||
# ECDSA P-256 — small and fast. 10-year expiry; the on-chain pin, not the cert
|
||||
# metadata, is what expires. SAN includes both the BNS name and the "localhost"
|
||||
# proxy destination so a sanity-check curl against the vhost works.
|
||||
openssl req \
|
||||
-x509 -nodes \
|
||||
-newkey ec \
|
||||
-pkeyopt ec_paramgen_curve:P-256 \
|
||||
-keyout "$DIR/privkey.pem" \
|
||||
-out "$DIR/fullchain.pem" \
|
||||
-days 3650 \
|
||||
-subj "/CN=prometheus.x" \
|
||||
-addext "subjectAltName=DNS:prometheus.x,DNS:localhost" \
|
||||
2>/dev/null
|
||||
|
||||
chmod 600 "$DIR/privkey.pem"
|
||||
chmod 644 "$DIR/fullchain.pem"
|
||||
|
||||
FP=$(openssl x509 -in "$DIR/fullchain.pem" -outform DER | openssl dgst -sha256 | awk '{print $NF}')
|
||||
|
||||
echo "cert path: $DIR/fullchain.pem"
|
||||
echo "key path: $DIR/privkey.pem"
|
||||
echo "tls fingerprint: $FP"
|
||||
echo
|
||||
echo "Register with: node Argus/src/register.js prometheus.x '{\"ip\":\"<ip>\",\"tls\":\"$FP\"}'"
|
||||
35
deploy/prometheus-service.service
Normal file
35
deploy/prometheus-service.service
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
[Unit]
|
||||
Description=Prometheus — the service behind prometheus.x
|
||||
Documentation=https://code.silentmode.st/silentmode/prometheus
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=prometheus
|
||||
Group=prometheus
|
||||
WorkingDirectory=/opt/prometheus
|
||||
Environment=NODE_ENV=production
|
||||
Environment=BIND=127.0.0.1
|
||||
Environment=PORT=3700
|
||||
Environment=BNS_NETWORK=chipnet
|
||||
# ADMIN_PUBLISHER, VIRUSTOTAL_API_KEY, PAYMENT_REQUIRED, PROMETHEUS_PAYMENT_ADDRESS
|
||||
# and PROMETHEUS_LISTING_FEE_USD go in the drop-in file at
|
||||
# /etc/systemd/system/prometheus-service.service.d/override.conf so they are
|
||||
# visible to systemd but not committed to the repo.
|
||||
EnvironmentFile=-/etc/default/prometheus
|
||||
ExecStart=/usr/bin/node service/server.js
|
||||
Restart=on-failure
|
||||
RestartSec=3s
|
||||
|
||||
# Hardening
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
# WritablePaths: the service writes the DB, the uploads directory, and (when
|
||||
# the key does not yet exist) the service key file.
|
||||
ReadWritePaths=/opt/prometheus/db.sqlite /opt/prometheus/uploads /opt/prometheus/.keys /opt/prometheus/build
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
47
deploy/prometheus-x.conf
Normal file
47
deploy/prometheus-x.conf
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# prometheus.x — the standalone service, served by this VPS.
|
||||
#
|
||||
# BCNR serves this name via its `ip`+`tls` records: Theseus and Ariadne
|
||||
# resolve prometheus.x by fetching the on-chain record, which says
|
||||
# "connect to <this IP>, pin the TLS cert whose DER sha256 equals <this
|
||||
# fingerprint>". The resolver reverse-proxies to this vhost; a browser
|
||||
# with Ariadne's Thread installed does the same.
|
||||
#
|
||||
# The cert is self-signed and pinned on chain — no CA is involved and
|
||||
# none needs to be, which is a narrower claim than a CA makes and does
|
||||
# not expire on anyone else's schedule. Rotating is one record update.
|
||||
#
|
||||
# To regenerate the cert and read out its DER sha256:
|
||||
# bash /opt/prometheus/deploy/make-tls-cert.sh
|
||||
#
|
||||
# The vhost listens only on HTTPS; the on-chain `tls` pin means plain
|
||||
# HTTP would be refused by the resolver anyway, and we do not want a
|
||||
# scanner to be able to read ANYTHING here over cleartext.
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
http2 on;
|
||||
server_name prometheus.x;
|
||||
|
||||
ssl_certificate /etc/ssl/prometheus-x/fullchain.pem;
|
||||
ssl_certificate_key /etc/ssl/prometheus-x/privkey.pem;
|
||||
|
||||
# The TLS resolver reaches us over the raw IP but passes `prometheus.x`
|
||||
# as the Host header. The service's canonical URL generator looks at
|
||||
# that header; preserving it keeps rel=canonical honest.
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3700;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
|
||||
# Publishers upload binaries that can be hundreds of MB. 2 GiB
|
||||
# matches the service's own cap in uploads.js.
|
||||
client_max_body_size 2g;
|
||||
proxy_request_buffering off;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue