Security & Privacy
BugPort captures real debugging context — screenshots, console logs, network requests, and response bodies — so the way that data is authenticated, scoped, and stored matters as much as the product itself. This page explains exactly how access works, what is and isn't captured, where heavy media lives, and the responsibilities you carry as the person filing reports. The short version: every request to the API is authenticated and scoped to a single workspace, sensitive data is filtered and redacted on capture, and large binaries never touch the database.
The guiding principle: least privilege, always scoped. A caller can only ever reach resources inside the one workspace its credential belongs to. There is no API path that reads across workspaces.
Authentication
All access to BugPort runs through one of three credential types, each verified on every request. Authentication identity itself is handled by Supabase Auth.
Used by the dashboard and app. Sign in with Google, GitHub, or email/password; Supabase issues a session JWT that the frontend sends as a Bearer token. The FastAPI backend verifies the Supabase JWT on every request before resolving the user and their workspace access. New accounts pass through onboarding to create their first workspace.
Used by the NPM widget for unauthenticated end-user submissions. It is intentionally public — it ships in your frontend bundle — but it is safe because it is origin-restricted (see below) and can only create bug reports, never read them.
Used by AI clients (Claude, Cursor, VS Code, Codex) through the MCP REST bridge. Workspace-scoped, optionally restricted to specific projects, carries explicit scopes, and is revocable. Every call is audit-logged.
Because Supabase owns the identity layer, password storage, social OAuth, and session management follow Supabase's hardened defaults. BugPort never stores raw passwords.
Authorization & scoped access
Authentication answers who is calling. Authorization answers what they may touch — and in BugPort that answer is always bounded by a workspace.
A widget key (bp_pub_…) is created per project per environment (local / staging / production) and carries an allowed-origins list. The API rejects submissions from any origin not on that list. A leaked key cannot be reused from an attacker's domain. Rotate or revoke keys from the dashboard at any time.
Tokens are bound to one workspace, can optionally be limited to specific project_ids, and carry scopes such as bugs:read and bugs:write. Write tools (create_bug_comment, update_bug_status) require bugs:write. The plaintext token is shown once at creation; afterward only a display prefix and last_used_at are visible. Revoke a token and it stops working immediately.
A bug can be shared via a read-only public link (GET /v1/public/share/bug/{share_id}). Anyone with the link can view that one report — and only that report — with no ability to edit, comment, or browse. Share pages respect the same display rules as the dashboard, so a widget report with no screenshot renders cleanly rather than showing a broken panel.
Every authenticated API call is resolved against the caller's workspace membership or the token's workspace binding. There is no cross-workspace leakage: list and detail endpoints only ever return resources inside the in-scope workspace. MCP tools never serve another workspace's data and never expose public-share data.
Security & access model
The diagram below shows the four caller types converging on a single auth-and-scope check, then a hard workspace boundary, with each path scoped to exactly the resources it is allowed to reach.
Notice that no path crosses the workspace boundary into another tenant's data, and the widget and share-link paths are deliberately narrow — create-only and read-one respectively.
Captured data
BugPort only captures what a reporter deliberately triggers. There is no silent background collection.
The NPM widget captures only the report the user creates: feedback text, annotations, an optional screenshot, and page context (URL, viewport, user agent). It does not capture console logs or network traffic.
The browser extension captures more, by design, for deep debugging: screenshot + annotations, console logs, network requests including response bodies (Fetch/XHR/API), page context, and optional session replay. Capture starts only when the reporter clicks start, and stops when they submit.
Network capture filters sensitive headers before anything is stored. Authorization-style headers are not persisted as plain capture data.
Redaction is applied to captured payloads to reduce the chance of obvious secrets being retained.
Response bodies for opaque, cross-origin, or binary responses cannot be captured by the browser. This is a platform limitation that also acts as a privacy boundary — third-party responses your page can't read are not collected.
Because the extension can capture network response bodies, treat extension reports like you would any debug log: they may contain whatever your APIs returned during the reproduction. Capture on a test or staging account when you can.
Media & heavy payload handling
Where data lives is a security property too. BugPort splits storage deliberately:
- Supabase Postgres holds metadata, small logs, comments, activity, usage, and billing records.
- Cloudflare R2 holds all heavy binaries: screenshots, videos, attachments, thumbnails, large logs, HAR files, and heavy debug payloads.
Binaries are uploaded straight to R2 using presigned upload URLs — the client requests a short-lived upload URL (.../media/upload-url), uploads the binary directly to R2, then confirms with .../media/complete.
Binaries never pass through Postgres. Keeping large media out of the database limits the blast radius of any database-level exposure to metadata, not raw screenshots or HAR captures.
Your responsibility
BugPort filters and redacts, but you control what gets reproduced and submitted. Do not capture secrets you don't want stored. The reporter ultimately decides which page state, which API calls, and which screenshots become part of a report.
- Reproduce bugs on test, staging, or demo accounts when the path involves sensitive data.
- Review the screenshot and annotations before submitting — blur or skip anything sensitive on screen.
- Use widget keys scoped to the right environment, and keep allowed-origins lists tight.
- Give MCP tokens the minimum scope and the fewest projects they need; revoke unused tokens.
- Use read-only share links for external sharing instead of inviting outsiders into the workspace.
- Don't reproduce flows that display real customer PII, payment data, or live secrets if you can avoid it.
- Don't paste API keys, tokens, or passwords into feedback text.
- Don't issue a broad
bugs:writeMCP token when a read-only token would do. - Don't add wildcard or unnecessary origins to a widget key's allowed-origins list.
- Don't treat a share link as private — anyone with the URL can view that report.
Privacy checklist
Before rolling BugPort out to a team, confirm:
- Widget keys are per-environment and origin-restricted to your real domains.
- MCP tokens are scoped to the smallest workspace/project set and least scopes.
- Old or leaked widget keys and MCP tokens have been rotated or revoked.
- Your team knows extension reports can include network response bodies.
- Reporters know to avoid capturing secrets and to review screenshots before submitting.
- Share links are only sent to people who should see that specific report.