# Deploying the leaderboard on Debian + Caddy

## Architecture (updated)

The heavy lifting moved into StackJack. A Scheduled StackJack automation
("Leaderboard Data Snapshot") runs hourly, pulls Halo data using StackJack's
already-correct tools, computes the composite scores, and writes the result
as JSON into one dedicated Halo KB article. This Debian box's only job is to
fetch that one article every 5 minutes and drop its content into
`snapshot.json` for the static page to read. Much smaller surface area than
the earlier direct-Halo-API approach — one field on one record to verify,
not dozens of ticket/timesheet fields.

**Before any of this works, the automation itself needs to be finished in
the StackJack Portal** — reviewed, consent accepted, credits available, and
promoted out of dry-run. This guide covers the Debian/Caddy side only.

## 1. Layout on the server

```
/opt/leaderboard/
├── fetch-snapshot.js     # fetches the KB article, writes snapshot.json
├── .env                  # secrets — NOT committed, chmod 600
├── public/
│   ├── leaderboard.html  # the display page
│   └── snapshot.json     # written by fetch-snapshot.js, read by leaderboard.html
```

`leaderboard.html` fetches `snapshot.json` from the same directory via a relative
path (`fetch('snapshot.json')`), so both files need to live together under
Caddy's `root`.

## 2. Create a dedicated user (don't run this as root)

```bash
sudo useradd --system --home /opt/leaderboard --shell /usr/sbin/nologin leaderboard
sudo mkdir -p /opt/leaderboard/public
sudo chown -R leaderboard:leaderboard /opt/leaderboard
```

## 3. Copy the files

```bash
sudo cp fetch-snapshot.js /opt/leaderboard/
sudo cp leaderboard.html /opt/leaderboard/public/
sudo chown -R leaderboard:leaderboard /opt/leaderboard
```

## 4. Secrets file

```bash
sudo tee /opt/leaderboard/.env > /dev/null <<'EOF'
HALO_CLIENT_ID=your-client-id
HALO_CLIENT_SECRET=your-client-secret
HALO_TENANT_URL=https://desk.mycloudable.com
EOF
sudo chmod 600 /opt/leaderboard/.env
sudo chown leaderboard:leaderboard /opt/leaderboard/.env
```

## 5. Verify the KB article field name BEFORE enabling the timer

Much smaller check than before: `fetch-snapshot.js` reads one field
(`answer`, HaloPSA's common field name for KB article body content) off one
KB article. Verify it once the automation has run at least one time
(Step 1 of its own instructions creates the article on first run):

```bash
cd /opt/leaderboard
sudo -u leaderboard node fetch-snapshot.js --debug
```

This dumps the raw KB article record. If the body content lives in a
differently-named field on this tenant, fix `KB_BODY_FIELD` at the top of
`fetch-snapshot.js` — that's the only field mapping left to verify.

Once it looks right:

```bash
sudo -u leaderboard node fetch-snapshot.js
cat public/snapshot.json
```

*Then* move on to installing the timer below.

## 6. Install the systemd service + timer

```bash
sudo cp deploy/leaderboard-poll.service /etc/systemd/system/
sudo cp deploy/leaderboard-poll.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now leaderboard-poll.timer
```

Check it's running and see recent output:
```bash
systemctl list-timers leaderboard-poll.timer   # confirms next scheduled run
sudo journalctl -u leaderboard-poll.service -f  # tail logs live
```

Trigger a manual run any time (useful for testing before waiting on the timer):
```bash
sudo systemctl start leaderboard-poll.service
```

## 7. Caddy

```bash
sudo cp deploy/Caddyfile /etc/caddy/Caddyfile
# edit the hostname at the top of the file, then:
sudo systemctl reload caddy
```

Caddy auto-provisions HTTPS via Let's Encrypt for any real domain in the
Caddyfile — nothing extra needed there. If this is staying on an internal-only
hostname with no public DNS, use `tls internal` in the Caddyfile block instead
so Caddy self-signs.

## 8. Point the TV/monitor at it

Whatever's driving the display (browser tab, kiosk Pi, smart TV app) just
loads `https://leaderboard.internal.example/leaderboard.html` — the page
itself handles polling `snapshot.json` every 5 minutes and re-rendering, no
manual refresh needed.

## Sanity checklist once it's live
- [ ] `journalctl -u leaderboard-poll.service` shows successful runs, not repeated auth errors
- [ ] `/opt/leaderboard/public/snapshot.json` timestamp is actually updating
- [ ] The page's "data as of" timestamp on screen matches
- [ ] Killing network briefly doesn't blank the screen — it should just keep showing the last-good data (this is the fail-gracefully behavior already built into both the script and the page)
