# Security & token rotation

## Secrets you handle

| Secret | Who needs it | Rotate with |
| --- | --- | --- |
| API key | Automation that manages monitors | Create a new key, then revoke the old one in Settings → API keys |
| Heartbeat ping URL | The monitored job | `POST /monitors/{id}/rotate-token` or the dashboard (**Generate new URL**) |
| HTTP Check header values | Stored in Tickingora, sent to your endpoint | `PATCH` with a new `value` |

## Good practice

- Give each integration its own key with the **minimum scopes**; prefer `monitors:read` for dashboards and reporting.
- Set an expiry on keys used by temporary automation or agents.
- Keep ping URLs and keys in environment files (mode 600, owned by the job user) or a secret manager. Do not commit them, do not print them in CI logs. Cron and systemd do not see your interactive shell's variables.
- Use dedicated, low-privilege tokens for HTTP Check headers.
- If anything leaks: rotate first, investigate afterwards. Rotation takes effect immediately.

## What Tickingora does

- API keys are stored as hashes and shown only once. Usage is logged without the key.
- Heartbeat tokens are stored as SHA-256 hashes, like API keys: the ping URL is shown once (on create and rotation, in the API or the dashboard) and cannot be shown again — a lost URL is replaced by rotating. It is never written to access logs.
- The only other copy is inside an idempotent replay of `POST /monitors` (same `Idempotency-Key`, same body): that stored response is encrypted and deleted after 24 hours.
- `external_id` is unique per account at the database level (`UNIQUE(user_id, external_id)`), so even simultaneous requests cannot create duplicates.
- HTTP Check header values are encrypted at rest, never returned, and removed from error messages.
- HTTP Checks reach only public HTTPS addresses; DNS is validated before every attempt and the connection is pinned to the validated address (SSRF protection). Redirects are not followed.
- Monitors are visible only to their account — other accounts' IDs return `404`.
- Ping tokens are kept out of logs at every layer: no access logs (web server, PHP-FPM), the web servers' error logs replace `/ping/<token>` with `/ping/[REDACTED]`, PHP stack traces do not include argument values, the rate limiter keys on a hash of the token, and API usage logs record only `/api` paths. No CDN or third-party proxy sits in front of Tickingora.
- Extra alert recipients must confirm their address and can unsubscribe at any time.
- Everything runs over HTTPS.

Found a vulnerability? Write to biuro@sixt4.pl.
