# 🦈 Shark Attack Fantasy League — Offline Recovery Manual

**Author:** Sho'Nuff | **Audience:** Germaine  
**Last updated:** July 9, 2026  
**Purpose:** Recover the full shark game system without Sho'Nuff being online.

---

## 1. What Is This? (Quick Overview)

The Shark Attack Fantasy League is a game where players draft shark-attack regions and score points when real shark events hit those regions. It has three parts:

| Part | What it does | Where it lives |
|------|-------------|----------------|
| **Backend** | Python web server + database | `/root/shark-game/backend/` — port 8083 |
| **Frontend** | Web pages you see in the browser | `/var/www/shark-game/` |
| **Scraper** | Fetches shark news and scores points | `/root/shark-game/scraper/` — runs via cron |

**Domains:** `shark.iamgmb.com` (the main site) and `shark.itpropartner.com` (backup URL)

---

## 2. Prerequisites — What You MUST Have Installed

If you're reading this on the server, check these are available:

```bash
which sqlite3       # Should show /usr/bin/sqlite3
which python3       # Should show /usr/bin/python3
which aws           # Should show /opt/awscli-venv/bin/aws
which curl          # Should show /usr/bin/curl
which systemctl     # Should show /usr/bin/systemctl
which journalctl    # Should show /usr/bin/journalctl
```

If any of those are missing, install them:

```bash
# Debian/Ubuntu
apt-get install -y sqlite3 curl systemd

# AWS CLI (if missing)
apt-get install -y python3-pip
pip3 install awscli
```

---

## 3. System Architecture — What Runs Where

```
┌─────────────────────────────────────────────────────────┐
│                      Internet                            │
└──────────┬──────────────────────────────┬────────────────┘
           │                              │
    shark.iamgmb.com              shark.itpropartner.com
           │                              │
           ▼                              ▼
    ┌──────────────────────────────────────────┐
    │            Caddy (reverse proxy)          │
    │   /etc/caddy/Caddyfile                   │
    │   shark.iamgmb.com → 127.0.0.1:8083     │
    │   shark.itpropartner.com → static files  │
    └──────────────┬───────────────────────────┘
                   │
    ┌──────────────▼───────────────────────────┐
    │   FastAPI Backend (port 8083)             │
    │   /root/shark-game/backend/server.py      │
    │   Runs as: shark-game.service (systemd)   │
    └──────────────┬───────────────────────────┘
                   │
    ┌──────────────▼───────────────────────────┐
    │   SQLite Database                         │
    │   /root/shark-game/backend/game.db        │
    └───────────────────────────────────────────┘
```

**Frontend files** are served by Caddy from `/var/www/shark-game/`:
- `index.html` — Main game page
- `draft-room.html` — Draft interface
- `league.html` — League dashboard
- `settings.html` — User settings
- `how-to-play.html` — Rules
- `help.html` — Help page
- `sw.js` — Service worker (push notifications)

---

## 4. Daily Operations — How to Check the Game

### 4.1 Is the game running?

```bash
systemctl status shark-game
```

**What to look for:**
- **Active: active (running)** ✅ — everything is fine
- **Active: inactive (dead)** ❌ — the service has stopped
- **Active: failed** ❌ — something crashed

### 4.2 View recent logs

```bash
# Last 50 lines
journalctl -u shark-game -n 50

# Follow live logs (like tail -f)
journalctl -u shark-game -n 50 -f

# View today's logs
journalctl -u shark-game --since today

# Filter for errors
journalctl -u shark-game -n 200 | grep -i "error\|traceback\|exception"
```

### 4.3 Check the database

```bash
# Open interactive SQLite shell
sqlite3 /root/shark-game/backend/game.db

# Or run a query directly
sqlite3 /root/shark-game/backend/game.db "SELECT * FROM users;"

# Useful queries:
sqlite3 /root/shark-game/backend/game.db "SELECT id, email, display_name, is_admin FROM users;"
sqlite3 /root/shark-game/backend/game.db "SELECT id, name, status, invite_code FROM leagues;"
sqlite3 /root/shark-game/backend/game.db "SELECT * FROM scores ORDER BY created_at DESC LIMIT 10;"
```

### 4.4 Check disk space

```bash
df -h /root/shark-game/
```

### 4.5 Restart the game

```bash
systemctl restart shark-game
```

Then verify it came back up:

```bash
sleep 3 && systemctl status shark-game
```

---

## 5. Backup & Restore

### 5.1 How backups work

The game database (`game.db`) is NOT backed up separately — it's included in the **Hermes live-sync** backup, which runs every 15 minutes and uploads to Wasabi S3.

### 5.2 Checking backups exist

```bash
aws s3 ls s3://hermes-vps-backups/ --endpoint-url https://s3.us-east-1.wasabisys.com
```

You should see folders like `live/`, `live-sync/`, `standby/`.

### 5.3 Restoring the database from S3

**Step 1:** Stop the game (so it doesn't write to the DB while restoring)

```bash
systemctl stop shark-game
```

**Step 2:** Download the latest state.db backup

```bash
aws s3 cp s3://hermes-vps-backups/live-sync/state.db /root/shark-game/backend/game.db \
  --endpoint-url https://s3.us-east-1.wasabisys.com
```

*(Note: The backup is named `state.db` on S3 but we save it locally as `game.db`)*

**Step 3:** Restart the game

```bash
systemctl start shark-game
```

### 5.4 Restoring the frontend files

The frontend files are in `/var/www/shark-game/`. If they get deleted, you can rebuild them from:

1. **If the git repo exists:** `cd /root/shark-game && git pull`
2. **If the S3 backup has them:** Download them from the `live/` prefix and copy to `/var/www/shark-game/`
3. **If neither exists:** Contact me — I'll need to regenerate the frontend files

### 5.5 Taking a manual backup (for safety)

```bash
cp /root/shark-game/backend/game.db /root/shark-game/backend/game.db.backup.$(date +%Y%m%d-%H%M%S)
```

---

## 6. Common Issues & How to Fix Them

### 6.1 "Port 8083 already in use"

**Symptom:** `systemctl start shark-game` fails, or you see "Address already in use" in logs.

**Fix:**
```bash
fuser -k 8083/tcp
systemctl restart shark-game
```

### 6.2 "Database locked" errors

**Symptom:** Users see errors when trying to join leagues, make picks, or view scores. Logs show "database is locked".

**Fix:**
```bash
rm -f /root/shark-game/backend/game.db.lock
rm -f /root/shark-game/backend/game.db-shm
rm -f /root/shark-game/backend/game.db-wal
systemctl restart shark-game
```

### 6.3 "500 Internal Server Error" when joining a league

**Symptom:** Users can't join leagues. You see "500" errors in the browser.

**Fix:**
```bash
journalctl -u shark-game -n 50 | grep -A10 "500\|Error\|Traceback"
```

Common causes:
- Missing environment variables
- Database schema mismatch (after restoring wrong backup)
- Invalid invite code

### 6.4 Push notifications not sending

**Symptom:** Users report they're not getting push notifications.

**Check 1:** Is the service worker loaded?
```bash
curl -I https://shark.iamgmb.com/sw.js
```

**Check 2:** Are the VAPID keys set?
```bash
systemctl show shark-game | grep -i vapid
```

**Check 3:** Test push
```bash
curl -X POST https://shark.iamgmb.com/api/push/test
```

**Fix:** If VAPID keys are missing, restart the service:
```bash
systemctl restart shark-game
```

### 6.5 Website shows blank page / 502 Bad Gateway

**Symptom:** `shark.iamgmb.com` shows a blank page or 502 error.

**Fix:**
```bash
systemctl status shark-game
systemctl status caddy
systemctl restart shark-game
systemctl restart caddy
```

### 6.6 SSL certificate expired

**Symptom:** Browser shows "Your connection is not private" warning.

**Fix:** Caddy auto-renews certificates. Restart if needed:
```bash
systemctl restart caddy
```

---

## 7. Scraper & Scoring

### 7.1 How scoring works

The scraper searches for shark news articles, scores them, and writes pending scores to:
`/root/shark-game/data/pending-scores.json`

The backend picks up these files and awards points to players who drafted the affected regions.

### 7.2 Scheduled runs

The scraper runs automatically via system cron at **8:00 AM ET** every day:

```
0 8 * * * /root/shark-game/scraper/run.sh
```

There's also a **draft reminder** that runs every 15 minutes during draft season.

### 7.3 Manual scrape

If you need to run the scraper right now:

```bash
cd /root/shark-game
source .venv/bin/activate
python3 scraper/scrape.py >> data/scraper.log 2>&1
deactivate
```

Or use the wrapper script:

```bash
cd /root/shark-game && bash scraper/run.sh
```

### 7.4 Check scraper results

```bash
tail -50 /root/shark-game/data/scraper.log
```

```bash
cat /root/shark-game/data/pending-scores.json
```

### 7.5 View the current cron jobs

```bash
crontab -l
```

You should see entries for the scraper and draft reminder.

---

## 8. Push Notifications — How They Work

The game uses **Web Push API** (browser push notifications):

1. **VAPID keys** authenticate the server to send pushes
2. **Service worker** (`/var/www/shark-game/sw.js`) receives pushes in the browser
3. **Users** subscribe via the browser's permission prompt when they visit the site

**VAPID keys** are stored as environment variables and embedded in the server code at `/root/shark-game/backend/server.py`. They include:
- `VAPID_PRIVATE_KEY`
- `VAPID_PUBLIC_KEY`
- `VAPID_CLAIMS` → `mailto:g@germainebrown.com`

**Push subscription data** is stored in the `push_subscriptions` table in the database.

### Push notification test

```bash
curl -X POST https://shark.iamgmb.com/api/push/test
```

### Check if users are subscribed

```bash
sqlite3 /root/shark-game/backend/game.db "SELECT COUNT(*) FROM push_subscriptions;"
```

---

## 9. Quick Command Reference

### Service Management
```bash
systemctl status shark-game    # Check if running
systemctl restart shark-game    # Restart the backend
systemctl stop shark-game       # Stop the backend
systemctl start shark-game      # Start the backend
```

### Logs
```bash
journalctl -u shark-game -n 100          # Last 100 log lines
journalctl -u shark-game -n 100 -f       # Live tail
journalctl -u shark-game --since today   # Today's logs
journalctl -u shark-game -n 200 | grep -i error  # Errors only
```

### Database
```bash
sqlite3 /root/shark-game/backend/game.db "SELECT * FROM users;"
sqlite3 /root/shark-game/backend/game.db "SELECT * FROM leagues;"
sqlite3 /root/shark-game/backend/game.db "SELECT * FROM scores ORDER BY created_at DESC LIMIT 20;"
sqlite3 /root/shark-game/backend/game.db "SELECT id, display_name, email FROM users WHERE is_admin=1;"
sqlite3 /root/shark-game/backend/game.db "PRAGMA integrity_check;"
```

### Scraper
```bash
cd /root/shark-game && bash scraper/run.sh             # Force scrape now
tail -50 /root/shark-game/data/scraper.log             # View scraper log
cat /root/shark-game/data/pending-scores.json          # View pending scores
crontab -l                                             # List scheduled jobs
```

### Backups
```bash
cp /root/shark-game/backend/game.db /root/shark-game/backend/game.db.backup.$(date +%Y%m%d)
aws s3 ls s3://hermes-vps-backups/ --endpoint-url https://s3.us-east-1.wasabisys.com
systemctl stop shark-game
aws s3 cp s3://hermes-vps-backups/live-sync/state.db /root/shark-game/backend/game.db \
  --endpoint-url https://s3.us-east-1.wasabisys.com
systemctl start shark-game
```

### Port / Network
```bash
fuser -k 8083/tcp
ss -tlnp | grep 8083
curl -I https://shark.iamgmb.com
curl http://127.0.0.1:8083/
```

### Push Notifications
```bash
curl -X POST https://shark.iamgmb.com/api/push/test
curl https://shark.iamgmb.com/api/push/vapid-public-key
sqlite3 /root/shark-game/backend/game.db "SELECT COUNT(*) FROM push_subscriptions;"
```

---

## 10. Complete Recovery Flow — If Everything Is Broken

Follow these steps in order if the entire game is down.

### Step 1: Check server is reachable
```bash
ping -c 2 shark.iamgmb.com
```

### Step 2: Check web server (Caddy)
```bash
systemctl status caddy
# If not running: systemctl restart caddy
```

### Step 3: Check backend service
```bash
systemctl status shark-game
# If not running: systemctl start shark-game
```

### Step 4: Check port 8083
```bash
ss -tlnp | grep 8083
# If nothing listening: systemctl restart shark-game
# If wrong process: fuser -k 8083/tcp && systemctl restart shark-game
```

### Step 5: Check the database
```bash
sqlite3 /root/shark-game/backend/game.db "PRAGMA integrity_check;"
# Should return "ok"
```

### Step 6: Check the scraper ran today
```bash
tail -20 /root/shark-game/data/scraper.log
# If last run was yesterday, run it manually
cd /root/shark-game && bash scraper/run.sh
```

### Step 7: Test the site
```bash
curl -I https://shark.iamgmb.com
# Should return HTTP/2 200
```

### Step 8: Restore from backup (if DB is corrupted)
```bash
systemctl stop shark-game
aws s3 cp s3://hermes-vps-backups/live-sync/state.db /root/shark-game/backend/game.db \
  --endpoint-url https://s3.us-east-1.wasabisys.com
systemctl start shark-game
```

---

## 11. File Locations Map

| What | Path |
|------|------|
| Backend server | `/root/shark-game/backend/server.py` |
| Database | `/root/shark-game/backend/game.db` |
| Python virtual environment | `/root/shark-game/backend/venv/` |
| Frontend files | `/var/www/shark-game/` |
| Service worker (push) | `/var/www/shark-game/sw.js` |
| Scraper script | `/root/shark-game/scraper/scrape.py` |
| Scraper wrapper | `/root/shark-game/scraper/run.sh` |
| Scraper dependencies | `/root/shark-game/scraper/requirements.txt` |
| Scraper log | `/root/shark-game/data/scraper.log` |
| Pending scores | `/root/shark-game/data/pending-scores.json` |
| Systemd service | `/etc/systemd/system/shark-game.service` |
| Caddy config | `/etc/caddy/Caddyfile` |

---

## 12. Database Tables Reference

| Table | Purpose |
|-------|---------|
| `users` | Players — email, display name, admin status, notification prefs |
| `leagues` | Leagues — name, invite code, status (draft/active/closed), max players |
| `league_members` | Which users belong to which leagues |
| `league_excluded_regions` | Regions excluded from a league's draft |
| `regions` | Shark attack regions (geo-areas players draft) |
| `draft_picks` | Which player drafted which region in which league |
| `scores` | Score events — what happened, how many points, which region |
| `user_scores` | Running total scores per user |
| `push_subscriptions` | Web push notification subscriptions |

---

## 13. Environment Variables

| Variable | Purpose |
|----------|---------|
| `VAPID_PRIVATE_KEY` | Private key for Web Push notifications |
| `VAPID_PUBLIC_KEY` | Public key for Web Push notifications |

To check if they're loaded:

```bash
systemctl show shark-game -P Environment
```

---

## 14. Contact / Escalation

If you've tried everything in this manual and the game is still broken:

1. **Save the logs:**
   ```bash
   journalctl -u shark-game -n 500 > /tmp/shark-game-logs.txt
   ```

2. **Save the database:**
   ```bash
   cp /root/shark-game/backend/game.db /root/shark-game/backend/game.db.crash
   ```

3. **Send me the files.** I can work from logs alone if needed.

---

> **🦈 Remember:** The most common fix is simply `systemctl restart shark-game`. Try that first before doing anything complicated!
