Security
cronpad isolates workspaces from each other, encrypts stored secrets, signs every outgoing webhook, and refuses to send requests to internal networks on the hosted service. Self-hosted servers trust their operators and enable powerful features (shell commands, private-network webhooks) by default; setting mode = "hosted" switches every one of them to the safe default for a public, multi-tenant server.
Accounts and access
- Sign-in only through Google or GitHub (verified email required); no passwords are stored.
- API keys are random, shown once and stored as SHA-256 hashes. Each belongs to one workspace and can be scoped read-only.
- Session cookies are
HttpOnly,SameSite=LaxandSecureover HTTPS; requests that change data with a cookie also need a CSRF token. - Every database query is scoped to the caller’s workspace. Requests for another workspace’s objects return
404, so IDs can’t be probed. - Every change (create, update, pause, delete, key creation, secret change, auto-disable) is written to the audit log with the actor.
Development authentication is opt-in with auth.dev_login = true. It trusts X-Dev-User (default developer) and creates a personal workspace for that identity; use it only on a trusted development server, never on a public deployment.
Secrets
Stored secrets are encrypted with AES-256-GCM, are write-only through the API, and are redacted from run history, logs and traces. Webhook signing secrets and cached idempotency responses are also encrypted with the same key. Protect the database, encryption key and backups as credentials.
Outgoing requests
- Signed. Every webhook carries a Standard Webhooks HMAC signature and timestamp so receivers can reject forgeries and replays. See Verify signatures.
- SSRF protection. In hosted mode, cronpad resolves each hostname itself and refuses loopback, private, link-local (including cloud metadata at
169.254.169.254), carrier-grade NAT, IPv6 unique-local and deprecated site-local, multicast, unspecified, documentation and benchmarking addresses, the IETF192.0.0.0/24range, and NAT64, 6to4, Teredo and deprecated IPv4-compatible translation/tunnel prefixes, on every connection and every redirect hop. Only ports 80 and 443 are allowed. - Bounded. Timeouts, response size caps, and auto-disable stop a job from hammering an endpoint indefinitely.
Hosted mode
mode = "hosted" is how cronpad Cloud runs, and what you should use if strangers can sign up to your server.
| Setting | Self-hosted default | Hosted mode |
|---|---|---|
| Command sink | On | Off |
| Webhooks to private networks | Allowed | Blocked |
| Email alert channels | Usable immediately | Address must be verified |
| Sign-up | Off after the first user (auth.allow_signup) | Rate-limited per IP; turn on with auth.allow_signup = true |
| Retry attempts, timeouts, auto-disable threshold | Unlimited / per job | Capped by server limits |
| Usage limits | Unlimited | From [limits] |
Each can still be overridden explicitly in configuration.
Reporting a vulnerability
Please report security issues privately through the repository’s security advisory page (Security → Report a vulnerability) rather than in a public issue.