CLItrail Docs

Security and secrets

Every credential you give CLItrail is encrypted and kept in a secret store apart from the main database, under keys that live apart from both. Tokens that only need to be recognised are stored as hashes that cannot be reversed.

#Write-only credentials

After you save an API secret, access token or key, the dashboard and API show only that it is set, its last four characters (for values of 12 characters or more), an 8-character fingerprint and when it was updated. Forms never prefill a secret. Replace saves a new value; removing a destination or log stream deletes its credentials. Webhook and log-stream signing secrets are generated by CLItrail and shown once, when created or rotated.

#A separate secret store

  • Apart from the main database. Sealed credentials live in their own store: a separate SQLite-backed Durable Object class, CLItrailVault, with its own namespace and migration history. The main database keeps only an opaque reference (sec_…) and the display details above.
  • Reachable only by the service. The store serves no HTTP. The service reaches it over Cloudflare’s internal RPC, and every call must carry a token derived from the master keys; anything else is refused. It stores only encrypted envelopes and refuses anything that is not one, so plaintext can never reach it.
  • Encryption. Each value is sealed with AES-256-GCM under a key derived with HKDF-SHA256 from the master key, and bound as authenticated data to its reference and to its record, such as destination:<website>:<destination>. Moved to another reference or read for another record, it fails to decrypt.
  • Keys apart from both. The master keys are platform secrets, never stored in either database. The service refuses to start in production without them. Keys can be rotated without downtime: envelopes under an older key are re-sealed under the new one.
  • Deletion is real. Deleting or replacing a secret first overwrites its envelope with zeros, then removes it from the store.
  • Used briefly. Credentials are decrypted only when needed, for one destination or stream at a time, and dropped afterwards. Google access tokens made from a service account key stay in memory until they expire and are never stored.

#Hashed or encrypted

ValueAt rest
Destination credentials: GA4 API secrets, Google Ads service account keys, Meta, TikTok and X Ads access tokens, X Ads consumer keys and token secretsEncrypted, in the secret store
Webhook signing secrets, log-stream signing secrets, S3 access keysEncrypted, in the secret store
Session tokens, visit receipts, handoff tokensSHA-256 hash; the value is never stored
Invitation linksKeyed hash (HMAC-SHA256); the link is shown once
IP addresses and networks, for rate limits and reconstructionKeyed hash, per website; the address is never stored
Receipts, handoff tokens and IP addresses in LogsKeyed hash
The operator’s admin tokenCompared in constant time, never stored in a database

The test suite checks this: it stores known credential values, then scans every table of both stores and fails if any value appears in plaintext.

#In transit

The service refuses any request that carries a credential, or would reveal a generated one, with secrets_require_https unless it arrives over https. Platform error messages are scrubbed of known secret values and common credential shapes before they are stored or shown, and Logs redact credentials in every recorded request and response.

#No connect flows

No destination is connected by signing in to the platform: you paste credentials you created and control, and you can revoke them on the platform at any time. CLItrail holds no refresh tokens for your ad accounts.

#The web app

  • Sign-in is Google or a passwordless email link, through Firebase; CLItrail sessions are HttpOnly, SameSite=Strict and Secure cookies, stored as hashes. Dashboard API writes must come from the service’s own origin.
  • Every response carries X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, a restrictive Permissions-Policy, X-Content-Type-Options: nosniff, Cross-Origin-Opener-Policy: same-origin (same-origin-allow-popups on the dashboard, for the Google sign-in popup) and Strict-Transport-Security. Pages are served with a Content Security Policy that allows only the service’s own scripts and styles, plus, on the dashboard, the Google origins sign-in needs.
  • The Firebase sign-in code is served from CLItrail itself, not from a third-party script origin.

#Report a security issue

Email founders@pilotprotocol.network with what you found and the steps to reproduce it. Never include live credentials; rotate any you shared by mistake.