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

CredentialPrefixIngestReadWhere it lives
Secret keystl_sk_…yeseverything in the projectServer environment variables only
Public keystl_pk_…auto-capture batches onlynothingSafe in browsers and public repos
Feed tokenstl_ft_…noone actor's events & sessionsBrowser, 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.

Never expose secret keys in client-side code or public repositories. The dashboard itself never holds an API key in the browser — it uses its own session — so you can use it from any device.

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_id pins), 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-mask on 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.