-
Notifications
You must be signed in to change notification settings - Fork 0
Production Deployment
The defaults are tuned for a local trial: both containers publish on loopback only. Before exposing this to a network, work through this page.
Terminate HTTPS at nginx, Caddy or Traefik and forward to 127.0.0.1:8000. Do
not publish port 8000 on a public interface.
The container keeps listening on plain HTTP inside the network — that is expected and correct when your proxy terminates TLS.
server {
listen 443 ssl http2;
server_name writ.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # agents connect over WebSocket
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}The Upgrade/Connection headers are not optional — without them agents cannot
establish their WebSocket and will retry forever.
WRIT_PUBLIC_URL=https://writ.example.com
ALLOWED_HOSTS=writ.example.com
CORS_ORIGINS=https://writ.example.com
ENVIRONMENT=productionWRIT_PUBLIC_URL is what agents dial back on. If it is wrong, agents fail with
Connection refused and nothing explains why.
FORWARDED_ALLOW_IPS=127.0.0.1 # or your proxy's addressNever *. With a wildcard, any client can set X-Forwarded-For and bypass
every per-IP rate limit and ban.
If agents run on other machines, DOC_EXTRACT_URL's loopback default is
unreachable and non-HTML content is skipped silently. Give the service a
route — usually a second server block — and set DOC_EXTRACT_URL to it. Keep
DOC_EXTRACT_SECRET; it is the only thing in front of it.
Enrol a second factor first, then:
REQUIRE_ADMIN_MFA=trueThe coordinator warns at every boot while this is off. Enabling it before enrolling locks you out.
| Setting | Keep it at | Why |
|---|---|---|
ALLOW_PRIVATE_TARGETS |
false |
It is the SSRF guard. Only enable if you deliberately monitor an internal host. |
WRIT_EXPOSE_OPENAPI |
unset | Keeps the route map off a public host. |
ENVIRONMENT |
production |
Enforces strong secrets, host allowlist, non-wildcard CORS. |
The shipped compose is hardened, and you do not need to add these:
-
read_only: true— only/datais writable, so a file-write primitive cannot modify application code or persist across a restart -
/tmpasnoexec,nosuidtmpfs -
cap_drop: ALL,no-new-privileges:true - non-root user; application code is world-readable and writable by nobody
The coordinator refuses to boot with more than one web worker. Its JWT blacklist, rate limits, agent presence and scheduler live in in-process fakeredis on a single SQLite file — a second worker gets its own empty keyspace, so token revocation silently stops working and schedules double-fire.
Scale by adding agents, not web workers.
usewrit/writ · AGPL-3.0-only · Issues · Discussions · Report a vulnerability
Getting started
Using it
Integrations
Operations
Reference