TILLPULSE · SYMBOLICATION

Make stack frames readable.

GA

Three upload paths — JavaScript source maps, Android ProGuard mappings, iOS dSYMs. All three are keyed by (project, release, filename or UUID) and used to de-obfuscate your stack frames.

01JavaScript

JavaScript source maps

For React Native, the bundler emits a main.jsbundle.map alongside the bundle.

bash
# Build the release bundle (RN ≥ 0.73)
npx react-native bundle \
  --platform ios \
  --dev false \
  --entry-file index.js \
  --bundle-output main.jsbundle \
  --sourcemap-output main.jsbundle.map

# Upload
curl -X POST 'https://tilldev.dev/api/projects/<id>/source-maps' \
  -H 'Authorization: Bearer tp_9f3a...' \
  -F 'file=@main.jsbundle.map' \
  -F 'release=1.4.0' \
  -F 'filename=main.jsbundle.map' \
  -F 'symbolication_type=js_source_map'

Symbolication runs once per issue group on first creation. Frames are cached in an LRU keyed by release. The dashboard shows a small symbolicated badge on resolved frames.

02Android

Android ProGuard / R8

The ProGuard mapping file is at build/outputs/mapping/release/mapping.txt:

bash
curl -X POST 'https://tilldev.dev/api/projects/<id>/source-maps' \
  -H 'Authorization: Bearer tp_9f3a...' \
  -F 'file=@app/build/outputs/mapping/release/mapping.txt' \
  -F 'release=1.4.0' \
  -F 'filename=mapping.txt' \
  -F 'symbolication_type=proguard'

The parser is pure-JS (no JVM). When a frame's function looks Android-shaped (short class.method tokens), the dispatcher routes through ProGuard; otherwise through SourceMapConsumer. Line-range entries are picked first; first-entry fallback otherwise.

03iOS

iOS dSYM

Upload the inner DWARF Mach-O — not the .dSYM bundle:

bash
# Inside the bundle
# YourApp.app.dSYM/Contents/Resources/DWARF/YourApp

curl -X POST 'https://tilldev.dev/api/projects/<id>/dsyms' \
  -H 'Authorization: Bearer tp_9f3a...' \
  -F 'file=@YourApp.app.dSYM/Contents/Resources/DWARF/YourApp' \
  -F 'release=1.4.0' \
  -F 'filename=YourApp'

TillPulse parses the Mach-O LC_UUID load command (thin and fat binaries supported) and indexes each slice by its UUID. Crash images carry their image UUIDs; the resolver matches incoming frames to the right slice.

On the roadmap
Address-to-symbol resolution for dSYM currently requires a Mac-side helper (atos / dwarfdump) — the dSYM is stored and indexed, and frames render in the dashboard with the binary slice annotated. Full in-cluster lookup is on the roadmap.
04Tooling

CLI

For CI pipelines, use the @tillstack/cli package. Login once with tilldev login; the token is stored at ~/.tilldev/config.json (mode 0600).

bash
tilldev login
tilldev pulse releases create pay 1.4.0 --env production
tilldev pulse sourcemaps upload pay --release 1.4.0 --file main.jsbundle.map
tilldev pulse proguard upload pay --release 1.4.0 --file mapping.txt
tilldev pulse dsym upload pay --release 1.4.0 --file YourApp
05CI · iOS

Xcode build phase (recommended)

Add a Run Script phase after Archive:

bash
if [ -n "$DWARF_DSYM_FOLDER_PATH" ]; then
  for path in "$DWARF_DSYM_FOLDER_PATH"/*.dSYM; do
    name=$(basename "$path" .app.dSYM)
    tilldev pulse dsym upload "$TILLDEV_PROJECT" \
      --release "$MARKETING_VERSION" \
      --file "$path/Contents/Resources/DWARF/$name"
  done
fi
06CI · Android

GitHub Actions (Android)

yaml
- name: Upload ProGuard mapping
  if: success()
  run: |
    npm i -g @tillstack/cli
    tilldev pulse proguard upload "$\{\{ vars.TILLDEV_PROJECT }}" \
      --release "$\{\{ github.sha }}" \
      --file app/build/outputs/mapping/release/mapping.txt
  env:
    TILLDEV_TOKEN: $\{\{ secrets.TILLDEV_TOKEN }}
07API

Listing & deleting

http
GET    /api/projects/:id/source-maps
GET    /api/projects/:id/dsyms
DELETE /api/projects/:id/source-maps/:mapId
08Managed

Storage

Uploaded symbols are stored securely and retained per release — there's nothing for you to host or configure. Manage or delete uploads from the dashboard or via the listing and delete endpoints above.