# Source maps

> Upload source maps to your node so stack traces show the original file, line and function — and what has and has not been verified.

Production JavaScript is minified, so a raw stack trace points at `index-4f2a1c.js:1:48213`. With source maps uploaded to your node, Belvador shows the original file, line, function and surrounding code. Maps stay on your node; they are never published with your site.

## How it works

1. During the build, `sentry-cli sourcemaps inject` writes a **Debug ID** into each JavaScript file and its `.map` file.
2. You deploy the injected JavaScript files — **without** the `.map` files.
3. CI uploads the `.map` files to your node with an upload token.
4. When an error arrives, the SDK has attached the Debug IDs of the files in its stack. Your node finds the matching maps and restores the original frames.

Frames that could not be resolved show **Minified** in the stack trace.

## Before you start

- A project and the build output folder, for example `./dist`.
- The same `release` value in the SDK's `init` and in the upload, for example the commit SHA.
- An **upload token**: **Project settings → Upload tokens → New upload token**. It is shown once, valid for 365 days, limited to this project — source-map uploads (60 requests per hour) and [release and deploy records](https://belvador.dev/en/docs/releases) (600 per hour). Store it as a CI secret named `BELVADOR_UPLOAD_TOKEN`. Creating and revoking tokens asks for your authenticator code.

## Option A — sentry-cli (recommended)

Belvador's edge accepts sentry-cli's upload API. Run this in CI after the build, before deploying:

```bash
export SENTRY_URL=https://edge.belvador.dev
export SENTRY_AUTH_TOKEN="$BELVADOR_UPLOAD_TOKEN"
export SENTRY_ORG=belvador SENTRY_PROJECT=<project-slug>
npx @sentry/cli sourcemaps inject ./dist
npx @sentry/cli sourcemaps upload --release "$RELEASE" ./dist
```

Project settings → Upload tokens shows the same commands with your project's address filled in. `$RELEASE` must be the value you pass as `release` in `init`.

## Option B — upload a tarball

Without sentry-cli's upload command, inject Debug IDs, pack the maps and post them:

```bash
npx @sentry/cli sourcemaps inject ./dist
(cd dist && find . -name '*.map' | tar -czf ../sourcemaps.tgz -T -)
curl -fsS -X POST "https://edge.belvador.dev/n/<node-id>/upload/sourcemaps?project=<project-id>&release=$RELEASE" \
  -H "Authorization: Bearer $BELVADOR_UPLOAD_TOKEN" \
  --data-binary @sourcemaps.tgz
```

The app shows this command with your node and project already filled in.

## Bundler plugins

Sentry's bundler plugins (Vite, webpack, esbuild, Rollup) use the same upload API as sentry-cli, so they can upload to Belvador when pointed at `https://edge.belvador.dev` with your upload token. The edge implements the sentry-cli and tarball paths shown on this page; with a plugin, check the result with a test error.

## Limits

| Limit | Value |
|---|---|
| One source map file | 10 MiB |
| Files per upload | 1,000 |
| Unpacked tarball | 64 MiB |
| Requests per token | 60 per hour |
| How long maps are kept | 90 days after they were last used |

## Do not deploy `.map` files

Remove `.map` files from what you deploy, or configure your host not to serve them. Anyone who can download them can read your original source.

## Check that it works

1. Build, inject, upload and deploy one release.
2. Trigger a test error in that release.
3. Open the issue: frames show your original file names and lines, and **Minified** is gone.

If frames stay minified:

- the `release` in `init` differs from the uploaded one;
- the deployed JavaScript is not the injected build — `inject` must run **before** you deploy, on the same files;
- the upload failed: check the CI log for `401` (wrong or revoked token) or `413` (file too large);
- the token expired after 365 days.

> [!NOTE]
> **What has been verified:** the SDK attaching Debug IDs to events and the node resolving frames from uploaded maps are each covered by Belvador's tests. A single end-to-end run from a CI upload through a production error has not been recorded yet, so check your first release with a test error.
