Linux team service
This is a systemd template for a fresh, separately reviewed Linux installation. It is not an overlay script for an existing host and does not provision DNS/TLS/firewall. The operator needs Git, npm, supported Node 24+, systemd and approved sudo access. Names and paths are illustrative.
Read-only preflight
command -v node
node --version
npm --version
git --version
systemctl --version
getent passwd fg-editor
getent group fg-editor
ls -ld /srv/field-guide /srv/field-guide/app /srv/field-guide/data
Expected missing identity/paths are findings, not permission to overwrite. Stop if names already belong to unrelated work. Confirm intended source access, install location, read/write model, public reader account and rollback before creating anything. Do not paste credentials into a clone URL.
| Path | Access |
|---|---|
| App, source | Editor reads and writes authorized content |
| Private data | Service, operator only |
| Output parent | Service creates staging siblings and renames |
| Reader output | Web server reads, traverses intended artifact |
| Engine, dependencies | Selected executable, readable packages |
| Backup storage | Restricted outside public root |
Prepare a fresh service identity
Run only after preflight confirms this new installation plan. Inspect each result; do not execute blindly against an existing server.
sudo useradd --system --create-home --home-dir /srv/field-guide \
--shell /usr/sbin/nologin fg-editor
sudo install -d -o fg-editor -g fg-editor -m 0750 /srv/field-guide/data
sudo -u fg-editor git clone \
https://github.com/KineticEnforcer/MarkStackV2.git /srv/field-guide/app
cd /srv/field-guide/app
git rev-parse HEAD
git status --short
Select/inspect the approved exact engine revision before installing, not changing branch head blindly. A private repo needs an authorized authenticated install route controlled by the operator. Repository access is separate from editor login accounts.
sudo -u fg-editor npm ci
sudo -u fg-editor npm test
sudo -u fg-editor npm run build
sudo -u fg-editor env MARKSTACK_USERS_FILE=/srv/field-guide/data/users.json \
npm run editor:create-admin -- --user admin
Stop on a failed step. Create-admin uses the hidden prompt, not a password argument. Keep the selected users path consistent with service. Initial build consumes available safe runtime/default inputs; after runtime branding/links changes, use a new build/publication with correct selection.
Review reader read/traverse permissions separately. The service home/source may be restrictive. Add only intended output access through your OS model; do not grant web server access to data just because an ancestor blocks traversal. A successful service-user build does not prove nginx can read output.
systemd unit template
Save through your approved host-config process as /etc/systemd/system/field-guide-editor.service. Replace executable after checking command -v node as service identity, actual hostname and paths.
[Unit]
Description=Field Guide MarkStack V2 editor
After=network.target
[Service]
Type=simple
User=fg-editor
Group=fg-editor
WorkingDirectory=/srv/field-guide/app
ExecStart=/usr/bin/node /srv/field-guide/app/editor-server.js
Environment=MARKSTACK_EDITOR_HOST=127.0.0.1
Environment=MARKSTACK_EDITOR_PORT=3001
Environment=MARKSTACK_USERS_FILE=/srv/field-guide/data/users.json
Environment=MARKSTACK_EDITOR_ALLOWED_HOSTS=editor.example.com
Environment=MARKSTACK_EDITOR_COOKIE_SECURE=1
Environment=MARKSTACK_TRUSTED_PROXIES=loopback
Restart=on-failure
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/srv/field-guide
UMask=0027
[Install]
WantedBy=multi-user.target
| Directive | Meaning, limit |
|---|---|
| User, Group | Dedicated identity; not root |
| Workdir, ExecStart | Exact installation and supported Node |
| Restart | Restart on process failure; not application health proof |
| NoNewPrivileges | Prevent privilege gain; not a network gate |
| ProtectSystem | Read-only filesystem policy with reviewed exceptions |
| ReadWritePaths | Writable namespace exception; does not grant Unix permissions |
| UMask | Creation mask; existing files, ACLs still matter |
The write exception is deliberately whole illustrative installation because source saves and publication siblings require it. It is broader than immutable engine isolation. Narrow only after mapping real write requirements and testing save/publish/runtime rotation; do not block required output-parent operations accidentally.
A shell-managed/version-manager Node path might not exist in a non-login service environment. Use an accessible absolute executable and verify its supported version. Do not assume operator node is the same binary systemd runs. After=network.target is ordering, not a guarantee every external endpoint is ready.
Validate before enable
sudo systemd-analyze verify /etc/systemd/system/field-guide-editor.service
Fix errors before reloading/enabling. Syntax verification is not startup, filesystem access, TLS or reader acceptance. Only after review, stage acceptance and intended maintenance window:
sudo systemctl daemon-reload
sudo systemctl enable --now field-guide-editor.service
sudo systemctl status field-guide-editor.service
sudo journalctl -u field-guide-editor.service --since '10 minutes ago'
Logs may contain private operational paths. Inspect locally and sanitize before support sharing. A retrying restart loop needs root-cause diagnosis, not repeated enable commands or world-writable data.
HTTPS and acceptance
| Check | Required evidence |
|---|---|
| Listener | Intended loopback 3001 only |
| Proxy | Exact allowed Host, trusted peer and safe forwarding |
| Cookie | Secure mode through actual HTTPS |
| Files | Private accounts, history inaccessible to reader |
| Writes | Save plus staged publish, backup rotation on staging |
| Reader | Current static artifact independent of editor |
| Recovery | Service stop, start, return and protected backup tested |
Configure the HTTPS proxy before browser sign-in because Secure cookies require HTTPS. The unit does not issue certificates or restrict external proxy access. Readers requiring privacy need a separate gate. Private team editor covers perimeter/account checks.
Restart signs writers out and loses current in-memory job/log/limiter state. It does not stop a separate static host or erase saved accounts. Coordinate running publication before stopping the service. Preserve actual failed/live paths before rollback.
Verification scope and source
The illustrative unit previously passed local systemd-analyze verify; that proves syntax only. Service-user creation, ownership, sandbox access, production startup and availability were not exercised on your host. Re-verify your final adapted unit and staging behavior.
Checked 12 October 2026: systemd execution/user/permissions/sandbox guidance. Engine/account local checks and their limits are in Validation limits.