Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ jobs:
- name: Install dependencies
run: npm ci

# The deployed service serves src/ as committed, without npm, so the
# vendored files must match what vendor-libs regenerates.
- name: Vendored files in sync
run: npm run vendor-libs && git diff --exit-code

- name: Lint
run: npm run lint

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ AUDIT-*.md
# Local service configuration (token, port). Commit .env.example instead.
.env

# Node binary pinned by `just deploy`
/deploy/bin/

# Log files
*.log

Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Changed

* **The ingest service deploys with `just` instead of shell scripts.** `install-service.sh` and `update-service.sh` are gone; `just deploy` replaces both, and `just status`, `logs`, `down` and `uninstall` cover the rest (see [SERVICE.md](SERVICE.md)). The service no longer needs `npm install` on the server, and node upgrades are picked up on the next deploy.

## 1.17.1 — 2026-08-12

### Fixes
Expand Down
20 changes: 20 additions & 0 deletions SERVICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ Open the viewer URL in a browser. POST a graph (see [API.md](API.md)) and it
renders immediately; subsequent pushes replace the current graph live via
Server-Sent Events.

## Deploying as a systemd service

On a Linux host, from the checkout, as your normal user (commands that need
root use `sudo` themselves; requires [`just`](https://github.com/casey/just)):

```bash
just deploy # pull (ff-only), pin node, install/refresh the unit, restart
just status # systemctl status
just logs # follow the journal
just down # stop the service
just uninstall # disable and remove the unit; code and .env stay
```

The unit is versioned at [`deploy/graph-lens-lite.service`](deploy/graph-lens-lite.service).
`just deploy` renders it with the deploying user and checkout path, and runs
the server with a copy of that user's `node` (the nvm default, if nvm is
installed) kept at `deploy/bin/node`. After upgrading node, run `just deploy`
again to repin it. The service needs no `npm install`: it uses only node
builtins and the committed `src/` tree. Rerunning `just deploy` is safe.

## Configuration

All configuration is environment-based (see [`.env.example`](.env.example)).
Expand Down
26 changes: 26 additions & 0 deletions deploy/graph-lens-lite.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Rendered by `just deploy`: @USER@, @GROUP@ and @DIR@ are filled from the
# deploying user and this checkout, so the repo carries no host specifics.
[Unit]
Description=Graph Lens Lite Ingest Service
After=network.target

[Service]
Type=simple
User=@USER@
Group=@GROUP@
WorkingDirectory=@DIR@
# Pinned copy made by `just deploy`, so nvm upgrades can't break the unit.
ExecStart="@DIR@/deploy/bin/node" --env-file-if-exists=.env server/index.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production

NoNewPrivileges=true
ProtectSystem=full
# No ReadWritePaths: the service holds its graph in memory and never writes to
# disk, so granting write over the whole checkout only widens what a
# compromise of it could reach.
PrivateTmp=true

[Install]
WantedBy=multi-user.target
80 changes: 0 additions & 80 deletions install-service.sh

This file was deleted.

75 changes: 75 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Deploy contract: help / deploy / status / logs / down
set shell := ["bash", "-euo", "pipefail", "-c"]

service := "graph-lens-lite"
branch := "main"

[private]
default: help

# list available recipes
help:
@just --list --unsorted

# pull code, pin node, refresh unit, restart service
deploy: pull build unit
sudo systemctl restart {{service}}

# show service status
status:
systemctl status {{service}} --no-pager

# follow service logs
logs:
journalctl -u {{service}} -f -n 100

# stop the service
[confirm]
down:
sudo systemctl stop {{service}}

# disable and remove the service; code and .env are kept
[confirm]
uninstall:
sudo systemctl disable --now {{service}}
sudo rm -f /etc/systemd/system/{{service}}.service
sudo systemctl daemon-reload

# fast-forward the current branch; asks before deploying a non-deploy branch
[private]
pull:
#!/usr/bin/env bash
set -euo pipefail
current=$(git rev-parse --abbrev-ref HEAD)
if [ "$current" != "{{branch}}" ]; then
read -rp "WARNING: on branch '$current', not '{{branch}}'. Continue? [y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || exit 1
fi
git pull --ff-only

# The server needs only node builtins and the committed src/, so there is no
# npm install here; CI guards that the vendored files in src/ stay in sync.
# Pin the node the unit runs: nvm's default is copied out of nvm's paths (-C
# skips the write when unchanged); a system node is symlinked so package
# upgrades flow through.
[private]
build:
#!/usr/bin/env bash
set -eo pipefail
mkdir -p deploy/bin
nvm_sh="${NVM_DIR:-$HOME/.nvm}/nvm.sh"
if [ -s "$nvm_sh" ]; then
. "$nvm_sh" --no-use
nvm use default --silent
install -C -m755 "$(node -p process.execPath)" deploy/bin/node
else
node_bin=$(command -v node) || { echo "node not found on PATH and no nvm" >&2; exit 1; }
ln -sfn "$node_bin" deploy/bin/node
fi

# render the versioned unit for this user and checkout, install, enable
[private]
unit:
sed -e "s|@USER@|$(id -un)|g" -e "s|@GROUP@|$(id -gn)|g" -e "s|@DIR@|{{justfile_directory()}}|g" deploy/{{service}}.service | sudo tee /etc/systemd/system/{{service}}.service >/dev/null
sudo systemctl daemon-reload
sudo systemctl enable {{service}}
18 changes: 0 additions & 18 deletions update-service.sh

This file was deleted.

Loading