Skip to content
bucker

DSN and the region model

Format

https://<publicKey>@<region>.<ingestHost>/<projectId>

Example: https://a1b2c3d4@us.ingest.example.com/clx0project0id

Part Rules
scheme https (http is tolerated for local development only)
publicKey [A-Za-z0-9_-]+, unique across the install
password half must be empty — a DSN carrying a secret is rejected as malformed
region first hostname label; [a-z][a-z0-9-]{0,15}
ingestHost everything after the region label, port included
path exactly one segment, the project id
query / fragment must be absent

Parsing lives in domain/src/lib/dsn.ts (parseDsn / buildDsn) and, for SDKs, in shared/src/protocol/dsn.ts. create-bucker re-implements the same rules locally so that npx pulls down zero dependencies.

The public key is not a credential for anything but ingest

ProjectKey.publicKey appears in client bundles by design. It authenticates ingest only — it cannot read an issue, list a project, or call the control plane. There is no secret half to protect, which is why listing keys back to project members is safe and why the onboarding endpoint can return the DSN.

Legacy Sentry DSNs carried a secret in the password slot. Bucker DSNs never do, and parseDsn rejects one that tries.

Why the region prefix exists on day one

The first hostname label is the data region. Adding an EU region later is then purely additive — a new hostname, the same identifier scheme — instead of a migration that rewrites every customer's DSN and every config file that quotes it.

The DSN is derived, never stored: key.service.ts builds it from { publicKey, projectId, region } on every read. Changing INGEST_HOST or INGEST_REGION propagates to every key immediately.

Today only us and eu are accepted values for the server's own INGEST_REGION, and only one region actually runs. The parser accepts any well-formed region label, so SDKs and the CLI are already forward-compatible.

Ingest endpoint

POST https://<region>.<ingestHost>/api/<projectId>/envelope/
Content-Type: application/x-sentry-envelope

The bare form without the trailing slash is registered too, so curl and hand-rolled clients do not hit a surprise 404.

Authentication is the public key, sent either as the sentry_key query parameter or inside the envelope header's dsn field. The browser SDK uses the query parameter deliberately: it keeps the request CORS-simple, so there is no preflight round trip in front of every event.

A 200 carries { "id": "<eventId>" } and an X-Bucker-Ingest-Disposition header. It means the envelope was accepted and queued, not that an issue exists yet — that happens when a worker drains the queue.

Configuration

Variable Default Meaning
INGEST_REGION us Region label this install stamps into DSNs
INGEST_HOST localhost:4000 Ingest host without the region prefix
INGEST_INLINE_MAX_BYTES 65536 Above this, the raw envelope goes to blob storage instead of the row
INGEST_MAX_ENVELOPE_BYTES 20971520 Hard reject above this, with an accounted too_large outcome