# Wisp Hosting Service
Minimal microservice for hosting static sites from the AT Protocol. Built with Hono and Bun.
## Features
- **Custom Domain Hosting**: Serve verified custom domains
- **Wisp.place Subdomains**: Serve registered `*.wisp.place` subdomains
- **DNS Hash Routing**: Support DNS verification via `hash.dns.wisp.place`
- **Direct File Serving**: Access sites via `/s/:identifier/:site/*` (no DB lookup)
- **Firehose Worker**: Listens to AT Protocol firehose for new `place.wisp.fs` records
- **Automatic Caching**: Downloads and caches sites locally on first access or firehose event
- **SSRF Protection**: Hardened fetch with timeout, size limits, and private IP blocking
## Routes
1. **Custom Domains** (`/*`)
- Serves verified custom domains (example.com)
- DB lookup: `custom_domains` table
2. **Wisp Subdomains** (`/*.wisp.place/*`)
- Serves registered subdomains (alice.wisp.place)
- DB lookup: `domains` table
3. **DNS Hash Routing** (`/hash.dns.wisp.place/*`)
- DNS verification routing for custom domains
- DB lookup: `custom_domains` by hash
4. **Direct Serving** (`/s.wisp.place/:identifier/:site/*`)
- Direct access without DB lookup
- `:identifier` can be DID or handle
- Fetches from PDS if not cached
- **Automatic HTML path rewriting**: Absolute paths (`/style.css`) are rewritten to relative paths (`/s/:identifier/:site/style.css`)
## Setup
```bash
# Install dependencies
bun install
# Copy environment file
cp .env.example .env
# Run in development
bun run dev
# Run in production
bun run start
```
## Environment Variables
- `DATABASE_URL` - PostgreSQL connection string
- `PORT` - HTTP server port (default: 3001)
- `BASE_HOST` - Base domain (default: wisp.place)
## Architecture
- **Hono**: Minimal web framework
- **Postgres**: Database for domain/site lookups
- **AT Protocol**: Decentralized storage
- **Jetstream**: Firehose consumer for real-time updates
- **Bun**: Runtime and file serving
## Cache Structure
```
cache/sites/
did:plc:abc123/
sitename/
index.html
style.css
assets/
logo.png
```
## Health Check
```bash
curl http://localhost:3001/health
```
Returns firehose connection status and last event time.
## HTML Path Rewriting
When serving sites via the `/s/:identifier/:site/*` route, HTML files are automatically processed to rewrite absolute paths to work correctly in the subdirectory context.
**What gets rewritten:**
- `src` attributes (images, scripts, iframes)
- `href` attributes (links, stylesheets)
- `action` attributes (forms)
- `poster`, `data` attributes (media)
- `srcset` attributes (responsive images)
**What's preserved:**
- External URLs (`https://example.com/style.css`)
- Protocol-relative URLs (`//cdn.example.com/script.js`)
- Data URIs (`data:image/png;base64,...`)
- Anchors (`/#section`)
- Already relative paths (`./style.css`, `../images/logo.png`)
**Example:**
```html
```
This ensures sites work correctly when served from subdirectories without requiring manual path adjustments.
## Security
### SSRF Protection
All external HTTP requests are protected against Server-Side Request Forgery (SSRF) attacks:
- **5-second timeout** on all requests
- **Size limits**: 1MB for JSON, 10MB default, 100MB for file blobs
- **Blocked private IP ranges**:
- Loopback (127.0.0.0/8, ::1)
- Private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
- Link-local (169.254.0.0/16, fe80::/10)
- Cloud metadata endpoints (169.254.169.254)
- **Protocol validation**: Only HTTP/HTTPS allowed
- **Streaming with size enforcement**: Prevents memory exhaustion from large responses