Docs
Security
No vanity claims — these are the practical controls in place today, and the ones teams should verify before sending production events.
Three credentials, three jobs
| Credential | Prefix | Ingest | Read | Where it lives |
|---|---|---|---|---|
| Secret key | stl_sk_… | yes | everything in the project | Server environment variables only |
| Public key | stl_pk_… | auto-capture batches only | nothing | Safe in browsers and public repos |
| Feed token | stl_ft_… | no | one actor's events & sessions | Browser, short-lived (60 s – 24 h) |
Public keys are ingest-only. They can submit auto-capture events through POST /v1/events/batch and nothing else — any read attempt returns 403. That is a deliberate property: embedding a public key in a page can never expose other users' activity.
To show a user their own history in the browser, your server mints a feed token with the secret key (POST /v1/feed-tokens). The token is read-only and pinned to that one actor (and optionally one target); it cannot ingest, export, or read stats, and it expires. See the Feed Component guide.
Key lifecycle
- Keys are shown once at creation and stored hashed — plaintext is never persisted.
- Deleting a key revokes it instantly: the next request with it fails with
401. - To rotate, create a new key on the API Keys page, deploy it, then delete the old one. Feed tokens minted with the old key stay valid until they expire (at most 24 hours).
Project isolation
Every event, actor, and session is scoped to a project at the database level. Each credential resolves to exactly one project and every query filters on that project ID — there is no cross-project read path. Within a project, feed tokens add a second boundary: each one is pinned to a single actor, so a customer-facing feed can only ever show that customer their own history.
CORS
Your customers' browsers legitimately talk to two things: the batch endpoint (with a public key) and the read endpoints (with a feed token). Those paths answer Access-Control-Allow-Origin: * without credentials — exactly as safe as the token they carry. Everything else (dashboard and account endpoints) is restricted to Softechlog's own origins.
Rate limiting
Redis-backed sliding window limits protect the ingest path:
- 1,000 requests/min per secret key
- 200 batch calls/min per public key (auto-capture batches events client-side to stay well under this)
Exceeding a limit returns 429 with a Retry-After header; the SDKs back off and retry automatically, and never surface it to your app. The ingest limiter fails open: if Redis is unavailable, events are still accepted rather than dropped. (Login and sign-up endpoints use fail-closed limiters so an outage can't strip brute-force protection.)
Transport & storage
- All API traffic is TLS-encrypted in transit.
- API keys are stored as hashes; feed tokens are signed and expire.
- Auto-capture never reads form field values and strips query strings and fragments from URLs by default (see Auto-Capture · Privacy).
- Retention is enforced automatically per plan (see Data Retention).
Your responsibilities
- Keep secret keys in environment variables, not code — and only ever on the server.
- Mint feed tokens only for the signed-in user (that's what the
actor_idpins), with the shortest TTL that works for your page. - Don't put secrets, tokens, or raw PII you can't store into event
metadata— it's stored as-is. - Use
data-stl-ignore/data-stl-maskon sensitive UI so auto-capture never records it.
Evaluating Softechlog for a security-sensitive rollout? Schedule a technical call — we're happy to walk through the architecture.