Reverse proxy & TLS
Jangka doesn't terminate TLS itself. In production, put it behind a reverse proxy that serves HTTPS and forwards requests to the app on port 3000.
Checklist
| Setting | Why |
|---|---|
PUBLIC_URL=https://… | Must be the exact address people open. Session cookies are marked Secure when it starts with https://. |
| No response buffering | Assistant replies stream as Server-Sent Events. A buffering proxy delivers them all at once at the end. |
| Long read timeout | Drafting a section can stream for minutes. |
TRUST_PROXY=true | Login rate limits and the audit log record the client's IP instead of the proxy's. |
| App port bound to localhost | Only the proxy should reach the app, so nobody can bypass it or spoof X-Forwarded-For. |
Prepare the compose file
Bind the app's port to the host's loopback interface and trust the proxy:
app:
# …
environment:
DATABASE_URL: postgres://jangka:jangka@postgres:5432/jangka
MASTER_KEY: ${MASTER_KEY:?set MASTER_KEY (openssl rand -base64 32)}
PUBLIC_URL: ${PUBLIC_URL:-http://localhost:3000}
TRUST_PROXY: "true"
ports:
- "127.0.0.1:3000:3000"PUBLIC_URL=https://jangka.example.comApply it with docker compose up -d.
Caddy
Caddy gets and renews a certificate automatically, sets X-Forwarded-For, and flushes event streams as they
arrive. No extra settings are needed:
jangka.example.com {
reverse_proxy 127.0.0.1:3000
}nginx
nginx buffers responses by default, so turn buffering off and raise the read timeout:
server {
listen 443 ssl;
server_name jangka.example.com;
ssl_certificate /etc/letsencrypt/live/jangka.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/jangka.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off; # SSE: send tokens as they arrive
proxy_read_timeout 300s; # long drafting runs
}
}
server {
listen 80;
server_name jangka.example.com;
return 301 https://$host$request_uri;
}Check it
Open PUBLIC_URL, start a draft with the assistant, and watch the reply: it should appear word by word. If
it arrives all at once after a pause, the proxy is still buffering.
The health endpoint confirms that the proxy reaches the app and the app reaches the database:
curl -fsS https://jangka.example.com/api/v1/health
# {"status":"ok"}