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.placesubdomains - 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.fsrecords - 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#
-
Custom Domains (
/*)- Serves verified custom domains (example.com)
- DB lookup:
custom_domainstable
-
Wisp Subdomains (
/*.wisp.place/*)- Serves registered subdomains (alice.wisp.place)
- DB lookup:
domainstable
-
DNS Hash Routing (
/hash.dns.wisp.place/*)- DNS verification routing for custom domains
- DB lookup:
custom_domainsby hash
-
Direct Serving (
/s.wisp.place/:identifier/:site/*)- Direct access without DB lookup
:identifiercan 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#
# 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 stringPORT- 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#
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:
srcattributes (images, scripts, iframes)hrefattributes (links, stylesheets)actionattributes (forms)poster,dataattributes (media)srcsetattributes (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:
<!-- Original HTML -->
<link rel="stylesheet" href="/style.css">
<img src="/images/logo.png">
<!-- Served at /s/did:plc:abc123/mysite/ becomes -->
<link rel="stylesheet" href="/s/did:plc:abc123/mysite/style.css">
<img src="/s/did:plc:abc123/mysite/images/logo.png">
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