deviant/admin/deviant-admin.service
Local Dev 1328f13026 admin: ship MVP for mobile wallet error inbox
New /admin service — single Node file + static dashboard shell + systemd
unit + nginx snippet. Receives error reports from the mobile wallet
posted to silentmode.st/apps/deviant/errors and renders them at
silentmode.st/apps/deviant-admin/ behind HTTP basic auth over TLS.

Receiver (server.js, no deps, Node 20+ built-ins only):
- POST /ingest  - 32 KB cap, JSON-only, 60 req/min/IP sliding window,
  atomic write to $DATA_DIR/<id>.json via tmp+rename. CORS *.
  Required fields: id, ts, version, scope, message. Server adds
  receivedAt + sourceIp before persist.
- GET  /api/errors  - server-paginated list, filter by scope/version/
  message substring/ts range. Reverse-chronological.
- GET  /api/errors/<id>  - single report, full JSON.
- GET  /, /errors/<id>, /app.{js,css}  - static dashboard shell.

Dashboard (public/, vanilla HTML + CSS + JS, no build):
- Reverse-chrono list with id/ts/scope/version/message/UA columns.
- Debounced search + scope/version/ts-range filters.
- Row click opens a drawer with id, timestamps, source IP, message,
  stack, extra as a labelled grid, and raw JSON pretty-printed.
- Deep-link /apps/deviant-admin/errors/<id> opens that one report.
- Keyboard: R refresh, C clear, Esc close drawer.

Hardening posture: service runs as www-data; systemd ReadWritePaths
locks writes to just the data dir; PrivateTmp, ProtectHome, no new
privs, filesystem /proc protections mirror admin-hermes / coinspectrum-
admin. No auth in app code (nginx owns basic auth).

Field policy enforced client-side (mobile shell writes `extra`
explicitly) and reinforced here: raw payload is only shown behind the
drawer's click-to-expand sections. No private keys / mnemonics / PINs
/ cookies / session fingerprints anywhere in the pipeline.

Live and smoke-verified: POST → 201, admin list → 1 report, drawer
renders stack + extra + raw JSON. Install notes + patch snippet are in
admin/README.md and admin/nginx-snippet.conf.
2026-11-03 16:39:01 +02:00

39 lines
1.1 KiB
Desktop File

[Unit]
Description=Deviant admin — error inbox receiver + dashboard API
Documentation=file:///opt/silent-mode/apps/deviant-admin/server.js
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/silent-mode/apps/deviant-admin
ExecStart=/usr/bin/node /opt/silent-mode/apps/deviant-admin/server.js
Restart=on-failure
RestartSec=5
Environment=DEVIANT_ADMIN_PORT=7792
Environment=DEVIANT_ADMIN_HOST=127.0.0.1
Environment=DEVIANT_ADMIN_DATA=/opt/silent-mode/data/deviant/errors
Environment=DEVIANT_ADMIN_PUBLIC=/opt/silent-mode/apps/deviant-admin/public
# Hardening — mirror the admin-hermes / coinspectrum-admin posture, scoped to
# only the paths this process actually writes.
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectHome=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
ProtectSystem=strict
ReadWritePaths=/opt/silent-mode/data/deviant/errors
[Install]
WantedBy=multi-user.target