# Switch from Sentry

If you already run a Sentry SDK, you do not have to install anything to try Bucker. Bucker's
ingest speaks Sentry's envelope grammar, so **an unmodified Sentry SDK pointed at a Bucker DSN
reports here.** The change is one string.

This page is deliberately specific about what that gets you today, because "Sentry-compatible"
is easy to claim and easy to over-claim. **Errors, traces, logs, release health, cron check-ins, user feedback and text, log and
minidump attachments carry over. Sentry Session Replay and profiles do not,** and they are
refused by name rather than dropped silently. The table below says which is which, and the
[certified matrix](sentry-compat.md) says which SDK versions were run against it. The wire contract itself is in [the ingest protocol](ingest-protocol.md).

## 1. Change the DSN

Create a project in Bucker, copy its **Sentry-SDK DSN** (Settings → Projects → keys; it sits next to
the ordinary DSN), and replace the DSN your Sentry SDK is initialized with. It looks like
`https://<publicKey>@us.ingest.bucker.io/<number>`: the host is region-prefixed, there is no
secret half, and the last segment is the project's **numeric** id. Use this form with a Sentry SDK:
every Sentry SDK requires a numeric project id and silently disables itself (JavaScript) or
fails at init (Python) on Bucker's ordinary DSN, whose project segment is not a number. Both
forms reach the same project.

| SDK | Before → after |
| --- | --- |
| JavaScript (browser, Node, Next.js, React, …) | `Sentry.init({ dsn: "<BUCKER_DSN>" })` |
| Python | `sentry_sdk.init(dsn="<BUCKER_DSN>")` |
| Go | `sentry.Init(sentry.ClientOptions{Dsn: "<BUCKER_DSN>"})` |
| Ruby | `Sentry.init { \|config\| config.dsn = "<BUCKER_DSN>" }` |
| PHP | `\Sentry\init(['dsn' => '<BUCKER_DSN>']);` |
| Java | `Sentry.init(options -> options.setDsn("<BUCKER_DSN>"));` |
| .NET | `SentrySdk.Init(o => o.Dsn = "<BUCKER_DSN>");` |

Where you set the DSN through the environment (`SENTRY_DSN`), change the value there and leave the
code alone. The server SDKs above read `SENTRY_DSN` when no `dsn` is passed; browser JavaScript has
no environment, so its DSN lives in your build configuration.

Nothing else changes: all three credential placements Sentry SDKs have used over the years
(`X-Sentry-Auth`, `Authorization`, `?sentry_key=`) are accepted, so an SDK of any vintage
authenticates, and Bucker sends `X-Sentry-Rate-Limits` in Sentry's own format so SDKs back off per
category instead of retrying into a limit.

**What "works" means here.** The wire grammar is Sentry's, and error events are stored, grouped and
turned into [essences](error-essence.md). The JavaScript (browser and Node, 7 through 11) and
Python SDKs are **certified**: a conformance suite runs the real, pinned packages against a local
Bucker and the [certified matrix](sentry-compat.md) is its output. The other languages above share
the same grammar but are not in that suite, so treat them as "the grammar is the same", not as a
certification. Mobile SDKs are *errors only*: Bucker has withdrawn its own
mobile surface, so there is no native symbolication for iOS or Android crashes.

## 2. What carries over

This table is the contract. It is maintained next to the code that decides it (the ingest worker's
list of item types it does not consume yet) and is checked against it, so a type listed as
"Not yet" here is a type the worker drops today.

An envelope is **never refused** for carrying an item Bucker does not consume. The error in it is
stored; the unconsumed item is dropped with an `unsupported_item_type` outcome you can see on the
**What we dropped, and why** panel (Billing). Nothing is lost silently.

<!-- sentry-support-matrix:start -->
<!-- Edit this table when what the worker consumes changes. `node scripts/check-sentry-switch-matrix.mjs`
     (run by `pnpm check:sentry-matrix`) compares it with DEFERRED_ITEM_TYPES in
     workers/src/pipeline/process.ts and fails when they disagree. Status is one of:
     Yes | Partly | Not yet. The last column is the PLANNED state and is not a promise of a date. -->

| Item type | Status | Today | Planned |
| --- | --- | --- | --- |
| `event` | Yes | Errors and messages: stored, grouped, symbolicated where Debug IDs allow, turned into an essence | |
| `session` | Yes | Per-session release health: crash-free sessions and users | |
| `client_report` | Yes | Accounted at the accept edge, so what your SDK discarded shows beside what Bucker dropped | |
| `sessions` | Partly | Aggregate session buckets (request-mode sessions; the Node, Python and other server SDKs) feed crash-free *sessions* and adoption. Crash-free *users* is shown as "not derivable from aggregates" rather than guessed: an aggregate carries no per-session identity | |
| `transaction` | Yes | Stored as spans in the same table OTLP spans land in, named after the transaction; metered per span, as OTLP is | |
| `span` | Yes | Bucker's own `span` shape, and Sentry JS v11's streamed span batches | |
| `log` | Yes | Bucker's own `log` shape, and Sentry's log record (`level`, typed `attributes`, environment, release) | |
| `check_in` | Yes | Recorded on the cron monitor named by `monitor_slug`; a monitor is created from `monitor_config` when it does not exist yet | |
| `feedback` | Yes | Stored as feedback (also `user_report`), linked to the issue of the event it names, with the widget route's spam scoring | |
| `attachment` | Partly | Text and logs (scrubbed first) and minidumps are stored and shown on the event's Attachments panel; screenshots only where the project opts in. Other binaries and credential-named files are refused by name, not billed | |
| `profile` | Refused | Refused by name (`unsupported_item_type:profile`, also `profile_chunk`), not billed. Bucker has no profiler; set the profile sample rate to zero | |
| `replay_recording` | Partly | Bucker consumes **its own** recorder's format. A Sentry Replay (rrweb `replay_event` + `replay_recording`) is refused by name as `unsupported_item_type:sentry_rrweb_replay`, not billed | |
| `state_patches` | Not yet | Not a Sentry item: a Bucker SDK item the worker does not consume yet | |

<!-- sentry-support-matrix:end -->

Read the table as follows. *Status* is what is true now. *Planned* is the target state, work that
is scheduled but not shipped; none of it should influence a decision today.

**What this means in practice.** Switch the DSN and your exceptions, traces, logs, cron check-ins and user
feedback arrive, and so does release health: crash-free sessions and users from SDKs that report
individual sessions (browser SDKs do), crash-free sessions alone from the server SDKs' aggregate
`sessions` item. Attachments arrive too, with the limits in the table. If you rely on Sentry for
Session Replay or profiling, **keep Sentry running for those** until the rows above change. Section 4 is how to do that without running two SDKs.

## 3. Source maps

Your build already stamps Debug IDs if it runs `@sentry/vite-plugin`, `@sentry/webpack-plugin` or
`sentry-cli sourcemaps inject`, and Bucker reads the same `_sentryDebugIds` registry, so **the Debug
IDs arrive on every event.** The upload carries over too: Bucker serves the part of Sentry's API
that source-map uploads use, so the tool you already run uploads to Bucker when you change two
variables.

```sh
SENTRY_URL=https://api.bucker.io
SENTRY_AUTH_TOKEN=bkr_org_…             # a Bucker org CI token (Settings › Tokens), or a personal access token
SENTRY_ORG=<your Bucker org slug>
SENTRY_PROJECT=<your Bucker project slug>
```

For a bundler plugin, the same four values are its `url`, `authToken`, `org` and `project`
options. Nothing else in the build changes. What is served, and what it maps onto:

| Sentry request | Bucker |
| --- | --- |
| `GET`/`POST /api/0/organizations/{org}/chunk-upload/` | Chunk options and chunk storage (`gzip` chunks, SHA-1 checked) |
| `POST …/artifactbundle/assemble/` | Unpacks the bundle and stores each source map under its Debug ID, then **verifies** each one |
| `POST …/releases/`, `PUT …/releases/{version}/` | `releases new`, `releases finalize` (and a full commit sha from `set-commits`) |
| `POST …/releases/{version}/deploys/` | `releases deploys new` |

Three rules, the same as for Bucker's own upload path:

- **Debug-ID bundles only.** A map is stored under the Debug ID in its `debug-id` header, its
  minified file's, or its own `debug_id`. A map with none is skipped, and a bundle in which no map
  has one **fails the upload** with a message saying to run `sentry-cli sourcemaps inject` (or
  turn on the plugin's injection). The legacy release-plus-filename upload (`releases files
  upload`) is not served.
- **A broken map fails the build.** The assemble step runs the same verify gate as
  `@bucker/bundler-plugin`, so a map that does not parse or does not resolve turns the upload
  into an error your CI sees, not a green build that symbolicates nothing.
- **Limits:** 8 MiB chunks, a 128 MiB bundle, 64 MiB per map. A larger build uploads in more
  than one bundle, which the CLIs do on their own.

This is checked rather than believed: the integration suite runs Sentry's JavaScript CLI —
`sentry` 0.45, the one `@sentry/bundler-plugins` 11 drives for every plugin — unmodified against
a Bucker api, creating a release, uploading, finalizing and recording a deploy. The Rust
`sentry-cli` binary is not in that run (it downloads its executable at install time); its
request shapes are tested by hand.

Prefer a Bucker package? [`@bucker/bundler-plugin`](sdk-bundler-plugin.md) does the same for Vite,
Rollup, webpack, esbuild and Next.js, with no Sentry dependency.

## 4. Keep Sentry running: the landing lane

You do not have to move to evaluate. The **landing lane** pulls your existing Sentry issues and
their latest events into Bucker read-only, using a read-only auth token, while your application
keeps sending to Sentry exactly as it does now. It is a mirror of errors:

- It reads projects, issues and each issue's latest event, and **Import history** backfills a
  window of past issues on request. Configuration is a separate, reviewable import (§5).
- Events are de-duplicated on Sentry's own event id, so if you also fan your SDK out to both
  products, the same error is one event, not two.
- You disconnect by deleting one row.

Full detail, the REST calls and the dashboard flow are in
[evaluating without migrating](evaluating-without-migrating.md). Bucker's answer to "how do I
migrate?" is this: run both, compare the same errors in each, and stop sending to Sentry when you
are satisfied. There is deliberately no export-and-restore step to fail halfway.

## 5. What does not carry over at all

- **Configuration, until you import it.** **Landing › Import config** reads alert rules,
  ownership rules, fingerprinting rules, inbound filters and members from Sentry and shows them
  as a `bucker.yml` to review and apply, with members as invites and everything that does not
  map listed by name ([how](evaluating-without-migrating.md#importing-sentrys-configuration)).
  Metric alerts and integrations are not imported.
- **History.** Issues and events already in Sentry stay in Sentry, apart from what the landing lane
  mirrors.
- **Integrations and dashboards** built on Sentry's API (`/api/0/…`). Bucker's control plane is a
  different API; see the API reference on this site.
- **Profiling**, and attachments beyond text, logs and minidumps (see the table).
- **Replay.** Bucker has its own session replay with a different recorder; see
  [session replay and privacy](replay-privacy.md). Sentry Replay data does not appear.

## 6. When nothing arrives after the swap

Start with [if nothing arrives](troubleshooting.md). One case is specific to this page: a Sentry
SDK that reports **only** item types the table marks "Not yet" (for example only `transaction`
items, with error capture off) looks like `waiting` today, because every item in the envelope is
dropped as unsupported. The drops panel shows `unsupported_item_type` rows when this is
happening.

## Prefer Bucker's own SDK?

Bucker publishes first-party SDKs for [browser](sdk-browser.md), [Node](sdk-node.md) and
[Python](sdk-python.md), and `npx create-bucker` wires a project in one command
([framework guides](frameworks.md)). They cover the rows marked "Partly" above in Bucker's own
shapes, and they are what the verification loop is built around. The two paths are not exclusive:
many teams start with the one-line DSN change and move SDK by SDK later.
