sq 0.5.6

A minimal client-server for phext hosting
# SQ Router v0.5.5 - Token-Based Multi-Tenant Routing


## Overview


The SQ Router provides token-based authentication and routing for multi-tenant SQ deployments. Instead of exposing each tenant's SQ instance directly, you run:
- **One router** on a public port (e.g., 443 or 1337)
- **Multiple SQ instances** on private ports, one per tenant

The router reads the `Authorization` header from incoming requests, looks up which backend port serves that token, and proxies the request transparently.

## Architecture


```
Client Request (Authorization: pmb-v1-abc123)
Router (port 1337) - reads token from header
Backend SQ (port 1338) - serves tenant 1's data
Response
```

Each tenant gets:
- Unique `pmb-v1-xxx` authentication token
- Dedicated SQ instance: `sq host <port> --key <token> --data-dir <path>`
- Isolated data directory

## Quick Start


### 1. Create router configuration


```json
{
  "tenants": [
    {
      "token": "pmb-v1-user1-abc123",
      "port": 1338,
      "data_dir": "/var/lib/sq/tenants/user1"
    },
    {
      "token": "pmb-v1-user2-def456",
      "port": 1339,
      "data_dir": "/var/lib/sq/tenants/user2"
    }
  ]
}
```

Save as `router-config.json`.

### 2. Start backend SQ instances (one per tenant)


Terminal 1:
```bash
sq host 1338 --key pmb-v1-user1-abc123 --data-dir /var/lib/sq/tenants/user1
```

Terminal 2:
```bash
sq host 1339 --key pmb-v1-user2-def456 --data-dir /var/lib/sq/tenants/user2
```

### 3. Start the router


```bash
sq route router-config.json 1337
```

### 4. Test with curl


```bash
# User 1 request

curl -H "Authorization: pmb-v1-user1-abc123" \
     http://localhost:1337/select/1.1.1/1.1.1/1.1.1

# User 2 request

curl -H "Authorization: pmb-v1-user2-def456" \
     http://localhost:1337/select/1.1.1/1.1.1/1.1.1
```

Each user sees only their own data.

## Configuration Format


```json
{
  "tenants": [
    {
      "token": "pmb-v1-xxx",    // Auth token (must match backend --key)
      "port": 1338,             // Backend SQ instance port (localhost)
      "data_dir": "/path"       // Tenant data directory
    }
  ]
}
```

**Validation:**
- No duplicate tokens allowed
- Backend ports must be listening before router starts
- Token format: any string (convention: `pmb-v1-<identifier>`)

## Security Features


1. **Token-based auth**: Only requests with valid tokens are routed
2. **Tenant isolation**: Each backend serves one tenant's data directory
3. **Path validation**: Backend SQ prevents directory traversal (`..`, `/`, `\`)
4. **Timeouts**: 30-second timeout on proxy connections
5. **Header limits**: 16 KB max header size

## Usage


```bash
# Start router with default config and port

sq route

# Start router with custom config

sq route my-config.json

# Start router with custom config and port

sq route my-config.json 443
```

**Default values:**
- Config file: `router-config.json`
- Listen port: `1337`

## Production Deployment


### systemd service for router


```ini
[Unit]
Description=SQ Router
After=network.target

[Service]
Type=simple
User=sq
WorkingDir=/var/lib/sq
ExecStart=/usr/local/bin/sq route /etc/sq/router-config.json 1337
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

### systemd service for backend (template)


```ini
[Unit]
Description=SQ Backend for %i
After=network.target

[Service]
Type=simple
User=sq
WorkingDir=/var/lib/sq/tenants/%i
ExecStart=/usr/local/bin/sq host ${PORT} --key ${TOKEN} --data-dir /var/lib/sq/tenants/%i
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

Create one instance per tenant:
```bash
systemctl enable sq-backend@user1
systemctl start sq-backend@user1
```

## TLS/HTTPS


The router operates at HTTP level. For HTTPS:

**Option A: nginx reverse proxy**
```nginx
server {
    listen 443 ssl;
    server_name sq.example.com;
    
    ssl_certificate /etc/letsencrypt/live/sq.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sq.example.com/privkey.pem;
    
    location / {
        proxy_pass http://127.0.0.1:1337;
        proxy_set_header Authorization $http_authorization;
        proxy_set_header Host $host;
    }
}
```

**Option B: Caddy**
```
sq.example.com {
    reverse_proxy localhost:1337
}
```

## API Compatibility


The router is fully transparent - clients use the same SQ API:
- `GET /select/<coord>` - Read scroll
- `POST /insert?library=X&shelf=Y&...` - Write scroll
- `POST /update?library=X&shelf=Y&...` - Update scroll
- `GET /toc` - Table of contents

Only difference: add `Authorization: pmb-v1-xxx` header.

## Token Generation


Generate secure tokens:

```bash
# Random 32-character token

echo "pmb-v1-$(openssl rand -hex 16)"

# Output: pmb-v1-a3f2b8c4d5e6f7a8b9c0d1e2f3a4b5c6

```

Convention: `pmb-v1-<32-hex-chars>`

## Monitoring


Router logs to stdout:
```
╔══════════════════════════════════════════════════════════╗
║             SQ Router v0.5.5 (Token-based)              ║
╚══════════════════════════════════════════════════════════╝

Listening on: 0.0.0.0:1337
Tenants configured: 3
Config file: router-config.json

[1] Routing to backend port 1338
[2] Routing to backend port 1339
[3] Invalid token: pmb-v1-i
[3] Unauthorized - Invalid token
```

## Error Responses


- **401 Unauthorized**: Missing or invalid token
- **400 Bad Request**: Malformed HTTP request
- **502 Bad Gateway**: Backend SQ not responding
- **500 Internal Server Error**: Router error

## Migration from Direct SQ


**Before (direct access):**
```bash
sq host 1337
curl http://localhost:1337/select/1.1.1/1.1.1/1.1.1
```

**After (routed access):**
```bash
# Start backend

sq host 1338 --key pmb-v1-abc123 --data-dir /data/tenant1

# Start router

sq route config.json 1337

# Client adds auth header

curl -H "Authorization: pmb-v1-abc123" \
     http://localhost:1337/select/1.1.1/1.1.1/1.1.1
```

## Limitations


- Router runs single-threaded (one connection at a time)
- Backend SQ instances must be started separately
- Config file not hot-reloaded (restart router to update)
- HTTP only (use nginx/Caddy for HTTPS)

## Future Enhancements


- Multi-threaded request handling
- Hot config reload (SIGHUP)
- Built-in HTTPS support
- Rate limiting per tenant
- Prometheus metrics endpoint
- Backend health checks

## Support


- Issues: https://github.com/wbic16/SQ/issues
- Docs: https://phext.io/
- Discord: https://discord.com/invite/clawd