Greenlight sits between your automations and real actions, so it's built to be safe to expose to the internet behind HTTPS.
- API — every
/api/*call needs a validX-API-Key. Keys are stored only as SHA-256 hashes; the plaintext is shown exactly once at creation. - UI — a single admin password establishes a session. Login is rate-limited with a lockout after repeated failures.
- Session and CSRF cookies are HMAC-signed with
GREENLIGHT_SESSION_SECRET; tampered or foreign-signed cookies are rejected. - Every state-changing form (login, decide, settings, key/rule management) carries a CSRF token that is validated server-side.
- Cookies are
HttpOnly,SameSite=Lax, and markedSecureautomatically whenGREENLIGHT_PUBLIC_URLishttps://(override withGREENLIGHT_COOKIE_SECURE). - Reaching the app directly over plain
http://<ip>:PORTwhilePUBLIC_URLishttps://makes the browser drop theSecurecookies, so forms fail with a "CSRF token" 403. The fix isGREENLIGHT_COOKIE_SECURE=falseon a trusted LAN — not disabling CSRF, which stays enforced on every mutating route.
Deep links in notifications point at the login-protected request page. No auth token and no approve/reject action link is ever placed in a notification body, so a leaked, cached, or shoulder-surfed push cannot approve anything — a human still has to log in and decide. See Notifications.
Approvals, rejections, cancellations, and timeout defaults all pass through a transactional compare-and-set that only fires if the request is still pending. A user click racing the timeout engine can't double-resolve or send two callbacks.
- Serve behind HTTPS (
GREENLIGHT_PUBLIC_URL=https://…). - Use a long, random
GREENLIGHT_SESSION_SECRET(openssl rand -hex 32). - Use a strong
GREENLIGHT_ADMIN_PASSWORD. - Give each caller its own API key; revoke unused ones under Settings.
- Lock down your ntfy topic (token/basic-auth, not world-readable).
- Restrict who can reach the host/tunnel if you don't need it public.
This is a small self-hosted project. If you find a security problem, open an issue describing the impact (avoid posting a working exploit against a live instance).
