Files
nosterm-client/Caddyfile
Robert Goodall cd2b900639
ci / build (push) Successful in 1m3s
ci / image (push) Successful in 41s
Initial commit: Nosterm client (terminal-style Nostr chat SPA)
2026-07-24 13:47:53 -04:00

76 lines
3.5 KiB
Caddyfile

# Caddy config for serving the standalone Nosterm client SPA.
#
# In production, replace `:80` with your domain to get automatic HTTPS, e.g.:
# nosterm.example.com { ... }
#
# The `/relay*` proxy is OPTIONAL. It lets the client reach a relay on the same
# origin (ws://<host>/relay), which sidesteps mixed-content and CORS concerns.
# The upstream is configurable via the RELAY_UPSTREAM env var (default
# `relay:3334`, the service name in this dir's docker-compose.yml). If you point
# the client directly at an external relay via PUBLIC_DEFAULT_RELAYS, you can
# delete the `handle /relay*` block.
:80 {
encode gzip zstd
# Tor-overlay relay bridge: proxy to a clearnet→onion bridge sidecar that
# forwards through Tor to an onion-only relay. Exposing it at a same-origin
# wss:// path lets a plain browser reach the onion relay without Tor Browser
# and without an HTTPS-page/insecure-ws mixed-content block. Only wired when
# RELAY_TOR_UPSTREAM is set (the demo deploy sets it); otherwise it points at
# a dead default and simply 502s, which is harmless if unused. MUST precede
# the /relay* block below, which would otherwise swallow this prefix.
handle /relay-tor* {
reverse_proxy {$RELAY_TOR_UPSTREAM:tor-bridge:3335}
}
# Home relay: proxy WebSocket + NIP-11 requests to the configured relay.
handle /relay* {
reverse_proxy {$RELAY_UPSTREAM:relay:3334}
}
# Runtime client config: regenerated by the entrypoint from env on startup.
# Never cache it so per-deployment relay changes take effect immediately.
handle /config.json {
root * /srv
header Cache-Control "no-store"
file_server
}
# Everything else: serve the static SPA with client-side-routing fallback.
handle {
root * /srv
try_files {path} /index.html
file_server
}
# Baseline security headers. No third-party analytics are ever included.
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy no-referrer
# Content-Security-Policy for a client-only SPA:
# - default-src 'self' only same-origin by default
# - connect-src wss:/ws: reach ANY relay the user configures (core)
# - connect-src https: NIP-05 verification fetches a domain's
# /.well-known/nostr.json over HTTPS. Read-only
# JSON GETs; no credentials are ever sent.
# - script-src 'unsafe-inline' required for SvelteKit's hydration
# bootstrap + the anti-FOUC theme script,
# which are inlined into the static fallback
# page (not prerendered, so hashes cannot be
# pinned at build time).
# - script-src 'unsafe-eval' required by NDK's event-emitter dependency
# (tseep), which JIT-compiles listener
# dispatch via `new Function`. Only triggers
# once a relay subscription opens. No REMOTE
# scripts are ever allowed, and remote Nostr
# content is never rendered as HTML, so it
# cannot inject code.
# - object-src 'none' no plugins
Content-Security-Policy "default-src 'self'; connect-src 'self' wss: ws: https:; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
-Server
}
}