Files
MP-Server/macropad-relay/DEPLOY.md
T
shadowdao b5e5bed7dd security(relay): minor hardening follow-ups from PR review
- Uniform auth responses remove the session-enumeration oracle: unknown
  session and wrong password now return an identical 401 at the API layer and
  an identical WS handshake/close; desktop-status and the 503 "not connected"
  signal are only exposed after successful auth.
- Session-count cap re-checked after bcrypt.hash so concurrent creates can't
  overshoot maxSessions.
- Relay web client reconnect backoff resets only after a successful auth
  response (an accept-then-close server no longer defeats the backoff).
- idGenerator comment corrected to match the rejection-sampling; drop unused
  recordFailure return value.
- DEPLOY.md: document ALLOWED_ORIGINS for reverse-proxy deployments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 18:13:33 -07:00

141 lines
3.7 KiB
Markdown

# MacroPad Relay Server - Deployment Guide
## Cloud Node Container Deployment
For AnHonestHost cloud-node-container deployment:
### 1. Build Locally
```bash
cd /home/jknapp/code/macropad/macropad-relay
npm install
npm run build # required: the build is no longer triggered automatically on install
```
> Note: the `postinstall` build hook was removed. Always run `npm run build`
> explicitly (locally or in CI) after `npm install` to produce `dist/`.
### 2. Prepare Deployment Package
The build outputs to `dist/` with public files copied. Upload:
```bash
# Upload built files to your node container app directory
rsync -avz --exclude 'node_modules' --exclude 'src' --exclude '.git' \
dist/ package.json public/ \
user@YOUR_SERVER:/path/to/app/
```
### 3. On Server
The cloud-node-container will automatically:
- Install dependencies from package.json
- Start the app using PM2
- Configure the process from package.json settings
### 4. Create Data Directory
```bash
mkdir -p /path/to/app/data
```
## Directory Structure on Server
```
app/
├── index.js # Main entry (compiled)
├── config.js
├── server.js
├── services/
├── handlers/
├── utils/
├── public/
│ ├── login.html
│ └── app.html
├── data/
│ └── sessions.json # Created automatically
└── package.json
```
## Update After Code Changes
```bash
# On local machine:
cd /home/jknapp/code/macropad/macropad-relay
npm run build
rsync -avz --exclude 'node_modules' --exclude 'src' --exclude '.git' --exclude 'data' \
dist/ package.json public/ \
user@YOUR_SERVER:/path/to/app/
# On server - restart via your container's control panel or:
pm2 restart macropad-relay
```
## Environment Variables
Set these in your container configuration:
- `PORT` - Server port (default: 3000)
- `DATA_DIR` - Data storage path (default: ./data)
- `NODE_ENV` - production or development
- `LOG_LEVEL` - info, debug, error
- `ALLOWED_ORIGINS` - Comma-separated list of allowed browser origins for the
WebSocket Origin check (e.g. `https://macropad.example.com`). **Set this to
your public origin(s) when running behind a reverse proxy** (see note below).
> **Reverse proxy note (`ALLOWED_ORIGINS`):** When `ALLOWED_ORIGINS` is unset,
> the WebSocket upgrade Origin check falls back to comparing the browser's
> `Origin` host against the raw `Host` header seen by the app. Behind a reverse
> proxy the internal `Host` (e.g. `localhost:3000`) often differs from the
> public origin the browser sends (e.g. `https://macropad.example.com`), so
> legitimate browser WebSocket upgrades get **rejected**. Set `ALLOWED_ORIGINS`
> to your public origin(s) to fix this. (Forwarding the public host via
> `proxy_set_header Host $host;`, as in the Nginx example below, also helps, but
> setting `ALLOWED_ORIGINS` explicitly is the reliable fix.)
## Test It Works
```bash
# Test health endpoint
curl http://localhost:3000/health
# Should return (counts only - no session ids are exposed):
# {"status":"ok","desktops":0,"webClients":0,"uptime":1.23}
```
## Nginx/Reverse Proxy (for HTTPS)
```nginx
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket timeout (24 hours)
proxy_read_timeout 86400;
}
```
## Troubleshooting
**Check logs:**
```bash
pm2 logs macropad-relay
```
**Check sessions:**
```bash
cat /path/to/app/data/sessions.json
```
**Port in use:**
```bash
lsof -i :3000
```