Some checks failed
morphit-release / Build + publish release tarball (push) Has been cancelled
77 lines
3.3 KiB
Desktop File
77 lines
3.3 KiB
Desktop File
[Unit]
|
|
# Morphit indexer — Blurt-chain -> Postgres materialisation + read-only REST
|
|
# API (/v1/*). Matches the morphit-ops deployment: monorepo at /opt/morphit,
|
|
# root-owned config, Postgres in a Docker container.
|
|
#
|
|
# Install (one-time, as root):
|
|
# cp ops/systemd/morphit-indexer.service /etc/systemd/system/
|
|
# systemctl daemon-reload
|
|
# systemctl enable --now morphit-indexer
|
|
# journalctl -u morphit-indexer -f # watch it boot + sync
|
|
#
|
|
# After this, `morphit-ops upgrade` restarts it automatically and it survives
|
|
# reboots — no screen sessions.
|
|
Description=Morphit indexer (Blurt-chain indexer and REST API)
|
|
Documentation=https://git.agorise.net/agorise/morphit
|
|
After=network-online.target docker.service
|
|
Wants=network-online.target
|
|
# Reboot recovery (cp597): disable the start-rate-limit so a power-cut restart
|
|
# never latches this unit into a permanent 'failed' state while Postgres (a
|
|
# Docker container here) is still coming up — it keeps retrying every RestartSec
|
|
# until the DB is reachable, with no ceiling on attempts.
|
|
StartLimitIntervalSec=0
|
|
|
|
[Service]
|
|
Type=simple
|
|
# Matches the current deployment (install tree + operator config are
|
|
# root-owned). To de-privilege, chown /opt/morphit's config + data to a
|
|
# dedicated service user and set User=/Group= here instead.
|
|
User=root
|
|
WorkingDirectory=/opt/morphit/apps/indexer
|
|
# Source the operator env + config exactly like a manual start, then exec the
|
|
# indexer. A shell wrapper (rather than EnvironmentFile=) is deliberate so env
|
|
# files written in either `KEY=value` or `export KEY=value` form both load.
|
|
# We source BOTH layouts, each guarded with `[ -f ]`: the ops-cli layout
|
|
# (/opt/morphit/morphit.env + morphit.config.env, written by `morphit-ops
|
|
# init`) AND the Ansible layout (/etc/morphit/indexer.env, written by the
|
|
# playbook). Whichever a given install produced is sourced; the other is
|
|
# simply absent. loadConfig() reads process.env, so either path works.
|
|
ExecStart=/usr/bin/env bash -c 'set -a; for f in /opt/morphit/morphit.env /opt/morphit/morphit.config.env /etc/morphit/indexer.env; do [ -f "$f" ] && . "$f"; done; set +a; exec /opt/morphit/node_modules/.bin/tsx src/main.ts'
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
KillSignal=SIGTERM
|
|
TimeoutStopSec=30
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=morphit-indexer
|
|
|
|
# --- Hardening (safe with User=root) -------------------------------
|
|
NoNewPrivileges=yes
|
|
ProtectHome=yes
|
|
PrivateTmp=yes
|
|
ProtectKernelTunables=yes
|
|
ProtectKernelModules=yes
|
|
ProtectKernelLogs=yes
|
|
ProtectControlGroups=yes
|
|
ProtectClock=yes
|
|
ProtectProc=invisible
|
|
# cp683 — ProcSubset MUST be 'all', not 'pid'. The indexer computes the
|
|
# /v1/health system block (cpu_pct, mem) from /proc/stat + /proc/meminfo; with
|
|
# ProcSubset=pid the kernel hides those non-process files, so os.cpus() can't
|
|
# read CPU counters (cpu_pct stays permanently null) and /proc/meminfo reads
|
|
# fail. These are non-sensitive system-wide stats; ProtectProc=invisible above
|
|
# still hides other processes' details, which is the meaningful protection.
|
|
ProcSubset=all
|
|
RestrictRealtime=yes
|
|
RestrictSUIDSGID=yes
|
|
LockPersonality=yes
|
|
RestrictNamespaces=yes
|
|
SystemCallArchitectures=native
|
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
|
# A core dump could contain secrets read from env at boot.
|
|
LimitCORE=0
|
|
# Node's V8 JIT writes+executes in memory, so W^X must stay off.
|
|
MemoryDenyWriteExecute=no
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|