Secrets & credentials
Never put a password, API key, or token literally in a job file — job YAML is the source of truth and is synced to git. Instead, Cronable keeps sensitive values in encrypted stores and you reference them by name. There are three kinds of stored values:
| Store | For | Reference as | Encrypted? |
|---|---|---|---|
| Variables | non-secret config (a region, a base URL) | {{ $vars.region }} | no |
| Secrets | anything that grants access (keys, tokens, PWs) | {{ $secrets.api_key }} | yes |
| Credentials | typed, reusable connections for a job type | {{ $credentials.myapi.token }} | yes |
All three are managed from Settings → Secrets. See Expressions & data flow for how
{{ }} resolves them at run time.
Secrets
A secret is a single named value — an API key, a bearer token, a webhook URL, a password. Reference it
anywhere expressions are allowed as {{ $secrets.NAME }}.
- Encrypted at rest with AES-256-GCM (a key derived from your
CRONABLE_ENCRYPTION_KEY), and masked to***in run logs, stored output, and the live stream. The UI and API only ever return a secret’s name and scope — never its value. - Scope: a secret is either global (visible to every job) or job-scoped (visible only to one job). When a job runs, it sees the global set plus its own — a job-scoped secret can override a global one of the same name.
Credentials
A credential is a typed, reusable connection — the set of fields a job type needs to talk to a service, stored once and referenced by name. Each has a kind that fixes its fields; the sensitive field(s) are encrypted and masked exactly like a secret, while identifying fields (a username, a header name) are not. Credentials are global — any job can reference any credential by name.
Most job types that talk to a service take a credential field naming the credential to use; you can
also read a field directly with {{ $credentials.NAME.field }}. The kinds:
| Group | Kinds |
|---|---|
| HTTP auth | api_key, bearer, basic, header — for an http job’s authScheme |
| Databases | postgres, mysql, mssql — one connection string each, for the database job |
| Messaging / SaaS | slack, telegram, twilio (SMS), imap (the inbox trigger), google_service_account (Sheets/Calendar jobs + watchers) |
| Object storage | sftp, s3 — for a file job’s remote transfers |
| AI | anthropic, openai — optional; blank falls back to the daemon’s ANTHROPIC_API_KEY / OPENAI_API_KEY |
The full field list per kind (and which field is the secret) is in the app’s Add credential form.
Back up your encryption key
Your CRONABLE_ENCRYPTION_KEY protects every stored secret and credential. Back it up
separately from the database. Lose it and those values are unrecoverable by design — there is no
recovery path. Keep it in the environment only, never in the jobs repo. See Security.
Masking is best-effort
Resolved secret values are redacted from logs, output, and the stream. But masking can only catch the literal value — a command that transforms a secret (base64, slicing) before printing it can still leak it. Don’t print secrets.