Screen Architecture — 5 Screens, Bottom Tab Nav
Tab 1 — Home (Dashboard) Purpose: Daily health check at a glance Layout top to bottom:
Project selector dropdown (top bar) 3 metric cards row: Crashes Today / Affected Users / Active Sessions 7-day crash trend line chart "Recent Crashes" list
Each card: error name, node path, time ago, user count, 🎬 if replay available Color dot: red = new, yellow = seen, green = resolved
Tab 2 — Crashes Purpose: Full crash inbox, triage workflow Layout:
Filter bar: All / New / Resolved / Ignored Sort: Most recent / Most users / First seen Crash cards list
Error title
Backend plan for crash ingestion, replay storage, triage, alerts, billing, and team management.
Build a backend that accepts SDK traffic from Godot games, normalizes crash and replay data, powers the dashboard, and reliably notifies teams when issues need attention.
Primary goals:
- Ingest crashes and replay telemetry with low SDK overhead
- Deduplicate, group, and enrich crash events
- Support fast dashboard queries for projects, crashes, alerts, and replays
- Deliver dependable notifications through email, push, and Discord
- Support project, team, billing, and plan management
- Keep the data model flexible enough for future features like traces, screenshots, and performance profiling
- SDK ingestion API
- Crash processing pipeline
- Replay upload and retrieval
- Projects, users, teams, and roles
- Alert rules and alert events
- Metrics aggregation for dashboard cards and trends
- Share links for crashes and replays
- Billing and subscription state
- Audit logging for security-sensitive actions
- Full source map / script deobfuscation pipeline
- Advanced distributed tracing
- Long-term raw replay retention beyond plan limits
- Custom on-prem deployment
- Web client authentication federation beyond standard email and OAuth
The backend should start as a modular monolith with clear service boundaries, then split into workers and dedicated services where needed.
- API gateway / HTTP server
- Auth and organization service
- Project service
- Ingestion service for SDK events
- Processing workers for grouping and enrichment
- Replay service
- Alerts service
- Billing service
- Admin and audit service
- Primary API server
- Background job worker(s)
- Queue / broker for async tasks
- Relational database for source of truth
- Object storage for replay blobs and attachments
- Cache for hot dashboard reads and rate limiting
- Email / push / webhook delivery integrations
- SDK sends crash or replay payload
- API authenticates project DSN or signed token
- Raw payload is validated and stored durably
- A background job enriches, fingerprints, and groups the crash
- Aggregates are updated for dashboard cards and trends
- Alert rules are evaluated
- Notification jobs are queued and delivered
- Dashboard reads precomputed summaries and detail records
- Write path: ingestion, validation, persistence, job enqueueing
- Read path: dashboard queries, crash details, replay playback, alert history
- Control path: projects, users, billing, settings, alert configuration
Use a relational database as the canonical store. Suggested core entities:
usersorganizationsorganization_membersrolesapi_keyssessionsaudit_logs
projectsproject_membersproject_settingsenvironments
crash_groupscrash_eventscrash_occurrencesstack_framesscene_snapshotscontext_snapshotsbreadcrumbs
replaysreplay_framesreplay_snapshotsreplay_assets
alert_rulesalert_eventsnotification_channelsnotification_deliveries
subscriptionsplansusage_countersbilling_invoices
tagsproject_tagssaved_viewsshare_linkswebhook_endpoints
crash_groupis the user-facing issue;crash_eventis the raw occurrence- Replays should be linked to crash occurrences, not just groups
- Store hot dashboard aggregates separately from raw events
- Keep raw payload JSON alongside normalized rows for forward compatibility
POST /v1/sdk/crashesPOST /v1/sdk/replaysPOST /v1/sdk/sessions/startPOST /v1/sdk/sessions/endPOST /v1/sdk/breadcrumbs
GET /v1/projectsGET /v1/projects/:projectId/overviewGET /v1/projects/:projectId/crashesGET /v1/crashes/:crashIdGET /v1/crash-groups/:groupIdGET /v1/replaysGET /v1/replays/:replayIdGET /v1/alerts/rulesGET /v1/alerts/events
POST /v1/projectsPATCH /v1/projects/:projectIdPOST /v1/projects/:projectId/membersPOST /v1/alerts/rulesPATCH /v1/alerts/rules/:ruleIdPOST /v1/billing/checkoutPOST /v1/billing/webhook
- Use versioned routes from day one
- Return stable IDs and cursor-based pagination
- Separate write payload validation from read-model serialization
- Keep SDK endpoints tolerant to partial payloads and schema evolution
- Use idempotency keys for retry-safe uploads
- Validate DSN or project token
- Normalize platform, environment, build version, and device metadata
- Persist raw payload first
- Compute fingerprint from error name, top frames, node path, and scene path
- Map into or create a
crash_group - Increment occurrence counts and affected-user counts
- Update first-seen / last-seen timestamps
- Accept replay metadata and frame stream separately or as a bundled upload
- Compress and store large payloads in object storage
- Associate replay with session, crash occurrence, and project
- Generate a lightweight preview record for list views
- Parse stack traces into structured frames
- Normalize platform labels
- Derive scene names and node path hints
- Attach alert evaluation inputs
- Generate dashboard aggregates
The backend needs stable grouping so the dashboard feels trustworthy.
- Error class or message
- Top stack frame function and file
- Node path
- Scene path
- Platform if the issue is platform-specific
- Start with deterministic rules, not ML
- Preserve manual override capability for merges and splits
- Recompute fingerprints when symbolication or normalization improves
- Store both fingerprint version and group version for future changes
newseenresolvedignored
Status history should be auditable rather than overwritten silently.
- Store and retrieve replay timelines
- Keep preview metadata for list views
- Serve data for the replay viewer scrubber, markers, and overlays
- Support access control for share links and team members
- Small replay metadata in the database
- Large frame payloads in object storage
- Optional precomputed thumbnails or preview manifests
- Query by replay ID and time range
- Support marker types: scene, signal, input, crash
- Support speed controls and frame stepping in the client
- Deliver compact payloads so the dashboard stays responsive
- Crash count threshold
- New crash group
- Platform-specific crash
- Project-specific or global rules
- Push
- Discord webhook
- Crash or aggregate changes arrive
- Rules are evaluated asynchronously
- Matching rules create alert events
- Delivery jobs fan out to channels
- Delivery status is tracked per attempt
- Debounce repeated notifications for the same group
- Respect quiet hours where configured
- Cap notifications during incident storms
- Allow per-project suppression rules
- Email/password or passwordless for dashboard users
- DSN or signed project token for SDK traffic
- Session cookies or bearer tokens for dashboard clients
- Organization-level roles: owner, admin, member
- Project-level permissions for read/write operations
- Strict access checks for replay playback and share links
- Separate internal admin access from customer access
- Rate limiting per DSN, IP, and token
- Payload size limits
- Schema validation and content type enforcement
- Audit logs for membership, billing, and rule changes
- Plan tiers: Free, Indie, Studio
- Project quotas: events, retention, alert rules, team members
- Metered usage: crash events, replay storage, notification volume
- Enforce limits at ingestion and storage layers
- Surface soft warnings before hard caps are reached
- Keep billing state separate from crash processing so ingestion remains resilient
- Use a billing provider webhook for subscription lifecycle updates
- Keep invoice and entitlement records locally for fast reads
- Request latency and error rate
- Queue lag and job failures
- Ingestion throughput by project
- Notification delivery success and retries
- Storage growth by project and environment
- Dead-letter queue for failed jobs
- Admin replay of ingestion payloads for debugging
- Manual resend for notifications
- Feature flags for rollout control
- Ingestion acknowledgment: fast enough for SDK retries to stay rare
- Dashboard read latency: sub-second for common queries
- Notification delivery: near-real-time for new crash alerts
- Idempotent writes for SDK retries
- Backward-compatible payload evolution
- Multi-tenant isolation by project and organization
- GDPR-friendly data retention controls
- Predictable cost growth with retention and replay volume
- Graceful degradation if notification providers fail
- Project and organization schema
- SDK ingestion endpoint for crash events
- Crash grouping and deduplication
- Dashboard read APIs for overview and crash list
- Basic replay metadata support
- Auth for dashboard users and project tokens
- Minimal alert rule storage
- Background job queue
- Retry-safe ingestion and idempotency keys
- Enrichment pipeline for stack traces and metadata
- Crash detail APIs with scene tree and context snapshots
- Status transitions: new, seen, resolved, ignored
- Audit logging for triage actions
- Full replay frame upload and storage
- Replay retrieval APIs and share links
- Alert evaluation engine
- Notification delivery integrations
- Notification history and retry tracking
- Subscription and plan enforcement
- Usage metering
- Retention policies by plan
- Cache layer for hot dashboards
- Object storage lifecycle rules
- Rate-limit hardening and abuse detection
- Attachments and screenshots
- Symbolication / source map support
- Team activity feed
- Public status and incident pages
- Webhook events for customer integrations
- Export jobs for analytics and compliance
The exact stack can vary, but the backend should support these capabilities cleanly.
- API server: Node.js, TypeScript, or Go
- Database: PostgreSQL
- Cache: Redis
- Queue: Redis queue, SQS, or RabbitMQ
- Object storage: S3-compatible storage
- Deployment: containerized services
- Monitoring: OpenTelemetry-compatible tracing and metrics
If the team wants the fastest path, a TypeScript monolith with a worker process is the simplest starting point.
- Replay storage cost may grow quickly without strong retention rules
- Crash grouping needs careful tuning to avoid noisy duplicates or over-merging
- SDK retry behavior must be matched by backend idempotency
- Alert delivery can become noisy without debounce and suppression logic
- Billing limits need to be enforced without blocking critical crash ingestion
- Privacy policy needs to define what SDK context data is allowed by default
The backend can be built and validated before any production game integration exists.
- Synthetic crash payloads that match the SDK schema
- Replay fixtures with marker timelines and scene snapshots
- Contract tests that replay captured request/response examples
- Load tests using generated crash bursts and replay uploads
- A tiny Godot sample project that only exercises init, crash capture, and replay markers
- A CLI payload generator for API smoke tests
- Postman or HTTP client collections for manual endpoint checks
- Golden files for crash grouping and dashboard aggregate calculations
- Ingestion accepts valid and invalid payloads correctly
- Crash grouping is stable across repeated submissions
- Replay uploads are stored and retrievable
- Alert rules fire from synthetic crash thresholds
- Dashboard read APIs return consistent summaries from seeded data
This keeps backend development unblocked even if there are no real games available yet, and it reduces risk before SDK integration lands.
- Define the public API contract for SDK ingestion and dashboard reads
- Choose the initial stack and deployment target
- Finalize the relational schema for projects, crashes, replays, and alerts
- Implement crash ingestion and grouping first
- Add replay storage second
- Add notifications and billing after the core read/write flow is stable