# Install And Migration Steps

Assumptions:

- New server runs Ubuntu 24.04 LTS or close.
- App user is `agent`.
- Public document root is `/workspace`.
- App path is `/workspace/digest`.
- Domain is still `tonki.dev` / `www.tonki.dev`, or you will update Nginx and Telegram URLs for the new domain.

## 1. Create User And Base Directories

```bash
sudo adduser agent
sudo usermod -aG sudo agent
sudo mkdir -p /workspace
sudo chown agent:www-data /workspace
sudo chmod 775 /workspace
```

If `www-data` does not exist yet, install Nginx/PHP first.

## 2. Install System Packages

```bash
sudo apt update
sudo apt install -y \
  curl git jq unzip zip rsync ca-certificates gnupg \
  nginx ufw certbot python3-certbot-nginx \
  php-fpm php8.3-cli php8.3-fpm php8.3-curl php8.3-mbstring php8.3-xml php8.3-sqlite3
```

Install Node.js 20.x or newer. Current server uses Node v20.19.6.

Example using NodeSource:

```bash
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v
npm -v
```

## 3. Install Claude Code

Install Claude Code for the `agent` user so `claude` is available at runtime.

Current server path:

`/home/agent/.local/bin/claude`

After install, verify:

```bash
sudo -iu agent
claude --version
claude mcp list
```

Current working version:

`2.1.78 (Claude Code)`

The orchestrator injects this PATH:

`/home/agent/.local/bin:/home/agent/.nvm/versions/node/v22.12.0/bin:/usr/local/bin:/usr/bin:/bin:${PATH}`

Even though current `node` is `/usr/bin/node`, keep `/home/agent/.local/bin` in PATH so systemd can find `claude`.

## 4. Configure Claude Auth And Reddit MCP

The digest depends on Claude Code plus the `reddit-mcp-buddy` MCP server.

Current global Claude config contains:

```json
"mcpServers": {
  "reddit-mcp-buddy": {
    "type": "stdio",
    "command": "npx",
    "args": ["-y", "reddit-mcp-buddy"],
    "env": {}
  }
}
```

On the new server, as `agent`:

```bash
claude mcp add reddit-mcp-buddy -- npx -y reddit-mcp-buddy
claude mcp list
```

Expected:

`reddit-mcp-buddy: npx -y reddit-mcp-buddy - Connected`

Also authenticate Claude Code. If copying current auth, see `SECRETS.md`.

## 5. Restore App Files

Unzip the migration package, then copy the app folder to `/workspace/digest`.

Example:

```bash
cd /tmp
unzip daily-ai-digest-migration-YYYY-MM-DD.zip
sudo rsync -a daily-ai-digest-migration-YYYY-MM-DD/digest/ /workspace/digest/
sudo chown -R agent:agent /workspace/digest
sudo find /workspace/digest -type d -exec chmod 755 {} \;
sudo find /workspace/digest -type f -exec chmod 644 {} \;
sudo chmod +x /workspace/digest/run-digest-orchestrator.sh
sudo chmod +x /workspace/digest/vpn-health.sh
sudo chmod +x /workspace/digest/validate-links.sh
sudo chmod +x /workspace/digest/DIGESTS/0-PRODUCTHUNT/fetch-producthunt.sh
```

If you want to preserve generated logs with group write:

```bash
sudo chmod -R u+rwX /workspace/digest/logs
```

Fix feedback file write permission:

```bash
sudo chown agent:www-data /workspace/digest/SUGGESTIONS-FROM-USER.MD
sudo chmod 664 /workspace/digest/SUGGESTIONS-FROM-USER.MD
```

## 6. Install And Configure NordVPN

Install NordVPN Linux app from NordVPN's official repository, then log in.

Minimum required post-login configuration:

```bash
sudo usermod -aG nordvpn agent
newgrp nordvpn

sg nordvpn -c "nordvpn set technology nordlynx"
sg nordvpn -c "nordvpn set protocol udp"
sg nordvpn -c "nordvpn set firewall enabled"
sg nordvpn -c "nordvpn set killswitch disabled"
sg nordvpn -c "nordvpn set autoconnect enabled"
sg nordvpn -c "nordvpn set meshnet disabled"
sg nordvpn -c "nordvpn set lan-discovery disabled"
sg nordvpn -c "nordvpn allowlist add port 22"
sg nordvpn -c "nordvpn allowlist add port 80"
sg nordvpn -c "nordvpn allowlist add port 443"
sg nordvpn -c "nordvpn connect us"
```

Current server is often connected to Canada even though the health script's default target is `us`; this is fine as long as Reddit/OpenAI/Telegram probes pass.

Verify:

```bash
sg nordvpn -c "nordvpn status"
sg nordvpn -c "nordvpn settings"
/workspace/digest/vpn-health.sh check
```

Expected health output:

`[INFO] Check-only mode: healthy`

## 7. Configure Nginx And PHP

Current Nginx model:

- `root /workspace;`
- PHP files handled by PHP 8.3 FPM socket.
- `/digest/digest-archive/YYYY-MM-DD/` maps directly to `/workspace/digest/digest-archive/YYYY-MM-DD/index.php`.
- Global noindex headers are set.

Install the vhost from `SERVER-CONFIG.md`, or copy the snapshot from `server-config-snapshot/etc-nginx-sites-available-tonki.dev`.

Then:

```bash
sudo nginx -t
sudo systemctl enable --now php8.3-fpm nginx
sudo systemctl reload nginx
```

## 8. Configure TLS

If keeping `tonki.dev`:

1. Point DNS A/AAAA records to the new server.
2. Temporarily stop or migrate the old server only when ready.
3. Issue cert:

```bash
sudo certbot --nginx -d tonki.dev -d www.tonki.dev
```

Current cert path expected by Nginx:

- `/etc/letsencrypt/live/tonki.dev/fullchain.pem`
- `/etc/letsencrypt/live/tonki.dev/privkey.pem`

## 9. Install Systemd Units

Create these files:

- `/etc/systemd/system/digest-orchestrator.service`
- `/etc/systemd/system/digest-orchestrator.timer`
- `/etc/systemd/system/digest-vpn-health.service`
- `/etc/systemd/system/digest-vpn-health.timer`

Use `SERVER-CONFIG.md` or the snapshot files.

Then:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now digest-vpn-health.timer
sudo systemctl enable --now digest-orchestrator.timer
systemctl list-timers --all | grep digest
```

Expected:

- `digest-vpn-health.timer` every 5 minutes
- `digest-orchestrator.timer` daily at 21:00

## 10. Firewall

Current UFW profile:

```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
```

Do not enable a NordVPN kill switch for this server. The current working setup uses NordVPN firewall plus allowlisted inbound service ports.

## 11. Smoke Test

Run as `agent`:

```bash
cd /workspace/digest
./vpn-health.sh check
./DIGESTS/0-PRODUCTHUNT/fetch-producthunt.sh "$(date +%Y-%m-%d)" | head -40
claude mcp list
./run-digest-orchestrator.sh --simulate --ignore-ack --date-iso 2030-01-01 --attempt-timeout-seconds 5 --poll-seconds 1
```

The simulate run should fail unless the fake date appears in `COMPLETED-DAYS.md`; that is expected. Its purpose is to verify logging, locking, health preflight, and process handling without using Claude tokens.

For a real one-off run:

```bash
./run-digest-orchestrator.sh --ignore-ack
```

