Public site
For LLMs and agents
This is the docs, the privacy policy, and the terms as one markdown file. Copy it, or copy the link and let an agent fetch the file itself.
# Bugwalk
The public site in one file: the docs, the privacy policy, and the terms.
An agent can fetch the same file at /llms.txt.
# Bugwalk documentation
The full public docs in one file, for an LLM or a coding agent. Pages follow the same order as the docs site.
Privacy: passwords, tokens, cookies, card numbers, and authorization headers are removed in the application before a batch is sent. Project, Privacy can name extra fields. Those are dropped on ingest and are not stored. See the Privacy page in this file.
# Introduction
Bugwalk keeps a person’s visit, then an AI debug engineer explains the failure from that visit and your code.
## What it does
A person uses your app. Bugwalk keeps the page views, clicks, requests, errors, and log lines from that visit, on one timeline, attached to the id or email you already have for them.
When something breaks, you open that timeline and run an investigation. The AI reads the visit and the repositories you connected, then writes what happened and which line failed. Every claim cites an event or a file and line. It can read your events and your code. It cannot change either.
## Parts of the app
After you sign in, the sidebar is the whole product. Pick a project at the top. Everything below it is that project.
### Overview
The last 24 hours: how many events, sessions, and people, the open issues, and the recent sessions.
### People
Search by the id or email you passed to identify() or forPerson(). Open a person to see their journey, then a session to read the log of that visit. A cron or queue run with no person stays on Overview, under recent sessions.
### Issues
Errors grouped by cause. Filter them, open one, and run an investigation on the group rather than on a single visit.
### Setup and settings
Project, Setup is where you connect a repository and install the SDK. New projects are created from the project menu, including the repositories to link. Account, in the profile menu, is the plan. Project, Usage shows what this period has used.
---
# Projects
A project is one app. This is how you add one, and what the project key is for.
## What a project is
A project is one app you are watching. The frontend and the backend of that app share it, so a click and the request it caused land on the same timeline.
A second app gets a second project. Staging and production can be two projects, or one project with an environment on each event. The environment is a label the SDK sends, such as production or staging. It does not create a new project.
## Add the first project
Sign in and open Setup. If the workspace has no project yet, that page asks for a name before anything else.
1. **Name it** — Use the name of the app. You can change which project you are looking at later. You cannot rename it from this screen.
2. **Create the project** — You land on Setup for that project. The first card is the GitHub repository. The install command is under it.
3. **Copy the project key** — The key is on the same page. It is safe in a browser bundle. It can only write events for this project.
## Add another project
Open the project menu in the sidebar and choose New project. That is the same name form. After you create it, Setup is for the new project, including its own repository. Connecting a second app does not replace the first.
## The project key
Every batch the SDK sends carries the project key, which is how an event is filed under the right app. Frontend and backend of the same app use the same key. Two apps do not.
The key is not a secret for reading data. Anyone with it can write events into the project. They cannot read timelines or run investigations with it.
---
# Install
Install by framework, paste the project key, send the first event, then name the person.
## Run the wizard
In the app you want to watch, run the wizard. It finds the framework, installs the package, and writes the project key from Setup.
The wizard opens a browser tab so you can approve the terminal. Approve it, go back to the terminal, and let it finish. If you would rather paste, use the snippets below. They are the same edits the wizard makes.
1. **From the app you want to watch** — `npx @bugwalk/wizard`
2. **Replace the project key** — Every snippet uses YOUR_PROJECT_KEY. Copy the real key from Setup and paste it in place of that string.
## Frontend integrations
Install the package, drop the project key in, and wrap or register the SDK once at startup. Use the same key on the backend half of the app.
### Angular (TypeScript, frontend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
npx @bugwalk/wizard
```
### Next.js (TypeScript, frontend)
Install:
```
npm install @bugwalk/next
```
Add to `app/layout.tsx`:
```
import { BugwalkProvider } from '@bugwalk/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<BugwalkProvider dsn="YOUR_PROJECT_KEY">{children}</BugwalkProvider>
</body>
</html>
);
}
```
### Nuxt (TypeScript, frontend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
npx @bugwalk/wizard
```
### React (TypeScript, frontend)
Install:
```
npm install @bugwalk/react
```
Add to `src/main.tsx`:
```
import { BugwalkProvider } from '@bugwalk/react';
createRoot(document.getElementById('root')!).render(
<BugwalkProvider dsn="YOUR_PROJECT_KEY">
<App />
</BugwalkProvider>,
);
```
### Script tag (HTML, frontend)
Install:
```
<script src="https://cdn.bugwalk.dev/v1.js" data-dsn="YOUR_PROJECT_KEY" async></script>
```
Add to `index.html`:
```
<script src="https://cdn.bugwalk.dev/v1.js" data-dsn="YOUR_PROJECT_KEY" async></script>
```
### SvelteKit (TypeScript, frontend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
npx @bugwalk/wizard
```
### Vue (TypeScript, frontend)
Install:
```
npm install @bugwalk/vue
```
Add to `src/main.ts`:
```
import { bugwalk } from '@bugwalk/vue';
createApp(App).use(bugwalk, { dsn: 'YOUR_PROJECT_KEY' }).mount('#app');
```
## Backend integrations
Register middleware or a module before your routes. Requests from an identified browser session attach to the same person automatically.
### Django (Python, backend)
Install:
```
pip install "bugwalk-sdk[django]"
```
Add to `settings.py`:
```
BUGWALK_DSN = "YOUR_PROJECT_KEY"
MIDDLEWARE = [
"bugwalk.django.BugwalkMiddleware",
*MIDDLEWARE,
]
```
### Echo (Go, backend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
go get github.com/bugwalk-dev/bugwalk/sdks/go/echo
```
### Express (TypeScript, backend)
Install:
```
npm install @bugwalk/express
```
Add to `src/server.ts`:
```
import { bugwalk } from '@bugwalk/express';
const app = express();
app.use(bugwalk({ dsn: 'YOUR_PROJECT_KEY' }));
```
### FastAPI (Python, backend)
Install:
```
pip install "bugwalk-sdk[fastapi]"
```
Add to `main.py`:
```
from bugwalk.fastapi import BugwalkMiddleware
app.add_middleware(BugwalkMiddleware, dsn="YOUR_PROJECT_KEY")
```
### Fastify (TypeScript, backend)
Install:
```
npm install @bugwalk/fastify
```
Add to `src/server.ts`:
```
import { bugwalk } from '@bugwalk/fastify';
const app = fastify();
await app.register(bugwalk, { dsn: 'YOUR_PROJECT_KEY' });
```
### Flask (Python, backend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
pip install "bugwalk-sdk[flask]"
```
### Gin (Go, backend)
Install:
```
go get github.com/bugwalk-dev/bugwalk/sdks/go/gin
```
Add to `main.go`:
```
import bugwalkgin "github.com/bugwalk-dev/bugwalk/sdks/go/gin"
router.Use(bugwalkgin.Middleware("YOUR_PROJECT_KEY"))
```
### Hono (TypeScript, backend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
npx @bugwalk/wizard
```
### Koa (TypeScript, backend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
npx @bugwalk/wizard
```
### NestJS (TypeScript, backend)
Install:
```
npm install @bugwalk/nestjs
```
Add to `src/app.module.ts`:
```
import { BugwalkModule } from '@bugwalk/nestjs';
@Module({
imports: [BugwalkModule.forRoot({ dsn: 'YOUR_PROJECT_KEY' })],
})
export class AppModule {}
```
### net/http (Go, backend)
No paste snippet in docs yet. Run `npx @bugwalk/wizard` in the repo; it detects the stack and writes files.
Install hint:
```
go get github.com/bugwalk-dev/bugwalk/sdks/go
```
## Java, .NET, Ruby, and PHP
No Bugwalk package goes into those services. Point the OpenTelemetry agent you already run at Bugwalk and send the project key in a header.
### Java, .NET, Ruby, and PHP (OpenTelemetry, otel)
Install:
```
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.bugwalk.dev
```
Add to `environment`:
```
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.bugwalk.dev
OTEL_EXPORTER_OTLP_HEADERS=x-bugwalk-key=YOUR_PROJECT_KEY
```
## How the first event arrives
You do not click a button to send the first event. The SDK sends a batch in the background after the app starts. A page view is usually enough.
Setup polls every few seconds. While it is waiting, the corner says Waiting for the first event. When a batch lands, that becomes Open the dashboard. The first page view is often there within a few seconds of the app starting.
If nothing arrives, the app is not running, the project key is a different project, or the request never left the browser. The key on the Setup page has to be the one in the snippet.
## Name the person
Events record even if you never say who they belong to. They show up on Overview as sessions. They do not show up under People, and they are not a journey, until you call identify().
Call it once you know the id or email, usually right after sign-in. Events before and after that call join the same person, including requests the backend made during the visit.
People shows the email when you sent one, otherwise the id. Search accepts either, including a partial email.
### identify()
- Call once you know who the person is. Everything before and after joins them.
```
// Same call from React, Vue, Next.js, and the script tag SDK.
bugwalk.identify({ id: user.id, email: user.email });
```
- When you have an internal id but no email yet.
```
bugwalk.identify({ id: user.id });
```
- When email is what support already has.
```
bugwalk.identify({ email: user.email });
```
## Background work
A cron, a queue message, a webhook, or a script answers two questions, and either one can be left out. What is the work? Pass a stable id and a kind: cron, queue, or task. Who is it for? Pass the same id or email you would pass to identify().
withWork names the run and starts a new session. forPerson attributes one iteration to a person, then clears that person so the next iteration does not inherit them. Call forPerson inside withWork when the job runs for many people. Call forPerson alone when the work is for one person and has no job name.
A run with no person shows on Overview as a session. It does not show under People. A run that calls forPerson shows each person under People, the same way a signed-in visit does. An error is still an issue. It names the person when one was set, and it keeps the job when the run was named.
Leave identify() for web requests. Calling it with an id such as cron:daily-digest files that string under People.
### withWork and forPerson
- Nobody owns this run. It shows on Overview as a session. It does not show under People.
```
import { withWork } from '@bugwalk/node';
await withWork({ id: 'temp-cleanup', kind: 'cron' }, async () => {
await deleteOldFiles();
});
```
- The job is the whole run. forPerson uses the same id and email as identify(), on that person’s own session, then clears them.
```
import { forPerson, withWork } from '@bugwalk/node';
await withWork({ id: 'daily-digest', kind: 'cron' }, async () => {
for (const person of people) {
await forPerson({ id: person.id, email: person.email }, async () => {
await sendDigest(person);
});
}
});
```
- kind is queue. The person is still forPerson, not a fake identify() id.
```
await withWork({ id: 'process-order', kind: 'queue' }, async () => {
await forPerson({ id: message.personId }, async () => {
await fulfill(message);
});
});
```
- An invoice, a webhook, or a retry. forPerson alone puts them under People. Add withWork with kind task when you also want a name on the run.
```
await forPerson({ id: person.id, email: person.email }, async () => {
await sendInvoice(person);
});
await withWork({ id: 'send-invoice', kind: 'task' }, async () => {
await forPerson({ id: person.id }, () => sendInvoice(person));
});
```
- @Cron sits above @BugwalkJob so the scheduler calls the wrapped method. The decorator does not walk the list. The loop calls forPerson.
```
import { BugwalkJob, forPerson } from '@bugwalk/nestjs';
@Cron('0 2 * * *')
@BugwalkJob({ id: 'daily-digest', kind: 'cron' })
async sendDigests() {
for (const person of await this.people.due()) {
await forPerson({ id: person.id, email: person.email }, () => this.mailer.send(person));
}
}
```
- with_work and for_person. The person does not stick to the process.
```
def send(person):
bugwalk_sdk.capture("digest_sent")
def run(_run):
for person in people:
bugwalk_sdk.for_person(
lambda person=person: send(person),
id=person["id"],
email=person["email"],
)
bugwalk_sdk.with_work(run, id="daily-digest", kind="cron")
```
- Pass the context into Capture. Identify on the client would leak across goroutines.
```
err := bugwalk.WithWork(ctx, bugwalk.Work{ID: "daily-digest", Kind: "cron"}, func(ctx context.Context, _ bugwalk.Run) error {
for _, person := range people {
if err := bugwalk.ForPerson(ctx, bugwalk.Person{ID: person.ID, Email: person.Email}, func(ctx context.Context) error {
return sendDigest(ctx, person)
}); err != nil {
return err
}
}
return nil
})
```
OpenTelemetry: set `bugwalk.job.id` and `bugwalk.job.runner` (`cron`, `queue`, or `task`) on the span. Person attributes stay as they are. One span per person matches one `forPerson` call.
---
# GitHub
Why a project has a repository, and how to connect it.
## Why you connect a repository
An investigation can describe a timeline with no code at all. It can point at a line only if it can read the code that was deployed.
That code belongs to one app, so the connection belongs to the project, not to the whole workspace. The agent reads the commit your release reported. It does not read whatever is on the default branch today, unless that is all it has.
## Connect it during setup
After you name a project, Setup opens with the repository card first. You can skip it and send events anyway. Come back when you want a report to cite a line.
1. **Connect GitHub** — This installs the Bugwalk GitHub App on the accounts you choose. You pick the repositories it may see. Bugwalk can pull those repositories, run their tests, create one branch, and open a pull request. It cannot push to the default branch, comment, merge, or write to your database.
2. **Pick the repository for this project** — The list is what that installation can reach. Connect is per project. Switch projects in the sidebar to connect a different repository.
3. **Set the release** — In the build, set BUGWALK_RELEASE to the commit SHA. Project, Repositories lists releases and shows the short SHA, or No commit when the release could not be placed.
## What access looks like
Access is through the GitHub App, and only on the repositories you grant. Uninstalling the app revokes it. A qualifying report can open a pull request on its own within the monthly allowance. After that allowance is used, a member can ask from the report. Merging stays theirs. Bugwalk cannot push to the default branch, comment, or write to your database.
You can disconnect a repository from the same card, on Project, Setup or Project, Repositories. Pro includes one connected repository. Team includes as many as you have projects for.
---
# Events
One recorded thing. This is the line on a timeline, and the unit a plan counts.
## What an event is
An event is one recorded thing: a page view, a click, an HTTP request, an error, or a log line. A typical session is 20 to 60 events.
The browser SDK records what happened in the page. The server SDK records the requests that page made. Both use the same project key, which is why they share a timeline. Passwords, tokens, cookies, card numbers, and authorization headers are removed inside your app, before the batch leaves the process. The privacy page lists every rule, and Project, Privacy is where you name extra fields to drop.
## What the timeline shows
Open a session. Each row is an event, in order, with the time. A web badge is the browser. An api badge is the server. A failing row is marked. Click a row to open the raw payload.
That timeline is the log. There is not a second log viewer. Search finds the person or the issue. The timeline is where you read what they hit.
## What a plan counts
Plans count events per month and keep them for a set number of days. An event with no person still counts. Skipping identify() does not skip the bill. It only means the event is not on a journey.
---
# Privacy
How passwords, tokens, and other secrets in a request are kept out of a timeline.
## Removed before it leaves your app
Redaction runs inside your process, before an event is queued. A value that matches a rule below never reaches Bugwalk, so it cannot leak from storage: we never received it. The rules cannot be turned off. You can add names on top of them. You cannot subtract.
Email addresses are kept. Naming a person by email is the point of identify(), and People search uses it.
## Headers that are dropped
These headers are removed from the request and the response. They are not replaced with a mask, because a mask would still show that the header was there. Matching ignores case.
authorization, proxy-authorization, cookie, set-cookie, x-api-key, x-auth-token, x-csrf-token, x-xsrf-token, and x-bugwalk-key.
## Field names that are dropped
Names are normalised first: lowercased, with spaces, hyphens, and underscores removed. user_password, userPassword, and USER-PASSWORD are the same field. The field is removed at every depth of a body, including inside arrays.
A long, unambiguous name is dropped when it appears anywhere in the field name: password, passwd, secret, credential, apikey, accesstoken, refreshtoken, idtoken, authtoken, bearertoken, sessiontoken, authorization, privatekey, clientsecret, creditcard, cardnumber, securitycode, aadhaar, and passport.
A short name is dropped only when the whole field is that name, so pan does not delete company and ssn does not delete classname: token, key, ssn, pan, cvv, cvc, pin, and otp.
On a URL, the parameter stays and the value becomes [redacted]. Removing the parameter would change the shape of the URL, and issues group on that shape.
## Values that are replaced
Every remaining string is scanned. A match is replaced in place and the rest of the string stays, so a log line can still be read.
A PEM private key becomes [redacted:key]. A JWT (a string starting with eyJ and two more segments) becomes [redacted:jwt]. Known token prefixes, including sk_live_, sk_test_, ghp_, gho_, github_pat_, xoxb-, and AKIA, become [redacted:token]. A 13 to 19 digit number that passes the Luhn check becomes [redacted:card], which is what leaves order numbers and timestamps alone. An Indian PAN becomes [redacted:pan]. A grouped Aadhaar number becomes [redacted:aadhaar].
## What is never read
No SDK reads form field values, other than the visible text of a clicked button or link. It does not read localStorage, sessionStorage, IndexedDB, keystrokes, or environment variables. Request bodies on GET and HEAD are not captured.
Request and response bodies are captured only when the app opts in with captureBodies: true, only for JSON, and only up to 8 KB. The same field rules still apply.
## Fields you choose not to keep
Project, Privacy takes extra field names, one per line. Those names are dropped from request and response bodies, headers, query values, and person traits before the event is stored. The same list applies to OpenTelemetry attributes, matching the attribute key or its last segment.
account_number and accountNumber are one entry. The list does not turn off the rules above. A name you add is extra. Clearing the list goes back to the built-in rules only.
beforeSend still runs in your app, after the built-in rules and before the batch is sent. Return null from it to drop an event entirely.
---
# Search
How to find a person, a visit, and the log of what they hit.
## Search by person
Open People. The box searches the id and the email you passed to identify(). Partial matches work. A blank box lists people already seen.
If the list is empty and events are arriving, identify() or forPerson() has not been called. Those events are still on Overview, under recent sessions, without a name. That includes a cron or queue that did not name a person.
1. **Open People** — Search for the id or a part of the email.
2. **Open the person** — The page is their journey: every session, newest first, with the event count, release, and environment.
3. **Open a session** — The timeline is the log of that visit. Run investigation is on the same page.
## Search errors
Issues is the list of errors grouped by cause, newest activity first. Filter by All, Open, Regressed, Resolved, or Ignored. Each row shows the route, how many people were affected, how many events, and when it was last seen.
Open an issue for a sample event, the people who hit it, and Run investigation. Mark resolved, reopen, or ignore from that page. Resolved means you consider it fixed. Ignored hides it from the open list. Regressed means it came back after it was resolved.
## The last 24 hours
Overview does not take a search query. It is the recent picture: event count, sessions, people, open issues, and the latest sessions. Use it when you do not have an email yet and want to see what just arrived, including the first events from a new install.
---
# Journeys
Everything one person has done, across every visit, on one list.
## What a journey is
A journey is one person, across all of their sessions. It is the list you open from People: when they first showed up, how many visits, and each visit in order.
A session is one continuous visit. A new session starts after 30 minutes with no events. A journey is those visits stacked, so you can see that checkout failed today and also failed last Tuesday.
## How one starts
Call identify() with the id or email you already have for that person. From then on, events in that browser and the matching backend requests attach to them. Background work uses forPerson with those same fields, on a new session for that iteration.
If two devices sign in as the same id, they are the same person. If you never call identify() or forPerson(), the events stay on an anonymous session and do not appear under People.
## How to open one
Open People, search, and open the person. Each row is a session: when it started, how many events, the release, the environment, and whether it ended in an error. Open a session to scrub the timeline. The journey is the list of visits. The session is one visit.
---
# Issues
Errors that share a cause, so you fix it once.
## What an issue is
An issue is events that share an error fingerprint. The title is the error. The page shows a sample event, how many people were affected, and when it was first and last seen.
If nobody was identified when it happened, the people list says so. Call identify() on a web request, or forPerson() inside background work, and later events on that issue can name them. A named job still links the issue to that run when no person was set.
## Work an issue
Filter the list, open the issue, and read the sample. The sessions on the right are the visits that hit it. Open one to see the timeline around the error.
Mark resolved when you have shipped a fix. Reopen puts it back. Ignore drops it out of the open list. If it returns after you resolved it, the status becomes regressed.
Run investigation on the issue explains the group, not one visit. The same button on a session explains that visit.
---
# Investigations
What happens when you press Run investigation, and what the report may claim.
## Where the button is
Run investigation sits on a session and on an issue. A session investigation explains that one visit. An issue investigation explains the group of errors that share a fingerprint.
Press it once. While it runs, the panel says it is reading the timeline and your code. That usually takes under a minute. Investigate again starts a new run. A follow-up question stays on the report you already have.
## What it reads
The AI debug engineer reads that person’s timeline, other visits that look the same, and the repositories connected to this project. It can search log text and search code, including with a pattern it writes, then read the matching lines at the commit your release reported.
An investigation only reads. It cannot change your database. A separate step can pull the repositories you chose, run their tests, and open a pull request on a new branch when the report qualifies and the monthly allowance remains. A member can ask from the report after that. Merging stays theirs. It cannot push to the default branch or comment.
Overview lists each pull request with the issue, the people who hit it, and whether the failure was in the web app or the API. Open the request to read the diff and the tool calls. Both are stored with the request.
Connect at least one repository before you run an investigation. A web app, an API, and a worker can all be attached to the same project. After you connect one, Bugwalk reads the repository and shows the language and whether it looks like a frontend, a backend, or a worker. Set BUGWALK_RELEASE to the commit SHA in your build so the lines it cites are the ones that were deployed.
## What the report contains
A headline, a confidence level, what happened, the root cause, and the findings. Every finding cites an event id or a file and line. A finding with no citation is marked that way, on purpose.
Low confidence is shown, with the reason. Treat that report as a lead. A suggested fix appears when the agent has one. A run that fails before it finishes is not charged.
## What it costs
Each finished investigation counts toward the monthly number on your plan. Pro includes 100, Team includes 1,000. The count is on Project, Usage.
The agent has to be switched on for the workspace. If the button is disabled, that is why.
---
# Pricing
What is counted, when you are charged, and where to see it.
## What you are charged for
Three meters run for the billing period: events, finished investigations, and pull requests. History is not a meter. It is how many days of events the plan keeps. A project, a member, and a connected repository are limits on the plan, not line items.
Pro is $39 per month. Team is $149 per month. Enterprise is custom. A 3-day trial on Pro and Team asks for a card so the plan continues without a gap. You are not charged until the trial ends.
## How the bill works
Each plan includes a number of events, a number of investigations, and a number of pull requests for the period. Project, Usage shows used against included, and the date the period resets.
On Pro and Team, extra events are billed as you use them, and you get an email before that starts. Investigations and pull requests stop at the included number. There is no charge past that number for either. A run that fails before it finishes does not count as an investigation. A pull request counts when the fix run starts, including a run that does not open one. An install pull request does not count toward that number.
A failed payment or a canceled subscription pauses recording. Events already stored stay readable. Canceling at the end of the period keeps the paid plan until that date.
## Change or cancel
Change plan from Account, in the profile menu. The change is prorated for the rest of the period. Manage payment and invoices opens the billing portal. Usage for the current project is on Project, Usage.
## The plans
These are the live limits. The pricing page is the same numbers, with the button to start a plan.
---
# Self-host
The server is Apache 2.0. Run it yourself and keep every byte.
## Run it yourself
The SDKs and the server are Apache 2.0. Self-host with one command and the data stays on your infrastructure. Community support is on GitHub.
Point BUGWALK_ENDPOINT at your own install. The SDKs need no other change, and a project key from your server works the same way as one from the cloud.
1. **Start the server** — `docker compose up`
---
# Privacy policy
What Bugwalk stores when you use the hosted product, who else handles it, and how long it stays.
This page covers the hosted product at bugwalk.dev. It describes your account, the events your app sends, and the code we read when you connect a repository. If you self-host, the data stays on your infrastructure and this page does not apply.
Updated 5 October 2026.
## Two kinds of data
We hold data about you, the member with the account, and data your app sends about the people who use it. A person is someone in your app. A member is someone on your Bugwalk workspace.
## Your account
We store the email you sign up with, an optional name, and your GitHub login if you connect GitHub. A password is stored only as a bcrypt hash. We never keep the password itself. An account created with a magic link or GitHub can have no password at all.
## The session
The dashboard session is an httpOnly cookie. In production it is marked Secure, and SameSite is Lax. The token is not stored in localStorage. Ingest from your app does not use that cookie. It uses a write-only project key, and it accepts no credentials.
## What your app sends
The SDK records page views, clicks, HTTP requests, errors, and log lines from a visit, on one timeline. If you call identify(), that timeline is attached to the id or email you already have for that person. If you do not call identify(), the visit stays on a session id. Background work uses the same id and email when you call forPerson(). A cron or queue with no person stays on a session id.
You choose what is sent. You can sample, filter, or add your own redaction rules before a batch leaves your app.
## Secrets
The SDK removes secrets inside your app, before an event is queued. Those rules cannot be turned off. Authorization and cookie headers are dropped. Field names such as password, secret, token, and card number are dropped. Known private keys, JWTs, and card numbers found inside a string are replaced. A value that matches never reaches Bugwalk.
The SDK does not read form field values, other than the visible text of a clicked button or link. It does not read localStorage, sessionStorage, IndexedDB, keystrokes, or environment variables. Request and response bodies are sent only if you opt in, only as JSON, and only up to 8 KB. The same rules still run.
Email addresses are kept when you send them. Naming a person by email is what identify() is for. Project, Privacy drops any extra field names you list, before they are stored.
[Full list of redaction rules](/docs/privacy)
## Repositories
Bugwalk can pull the repositories you chose, run their tests, create one branch, and open a pull request. It cannot push to the default branch, comment, merge, or write to your database. A qualifying report can open a pull request on its own within the monthly allowance. After that, a member can ask from the report. Merging stays yours. Disconnect the repository, or uninstall the app, and that access stops.
## Investigations
An investigation reads the session and, when a repository is connected, the code around the failing line. That evidence is sent to the model provider configured for the product, OpenAI unless another provider is set, so it can write the report. The report is stored with the issue so it can be read again.
We do not use your repositories or your customers’ sessions to train a model of our own.
## Payment
Paid plans are billed through Polar. Bugwalk does not store your card number. Polar stores the payment method and tells us the plan, the status, and the period.
## Email
We send mail through Resend: magic links, billing notices, and the messages the product says it will send. We do not sell your email address, and we do not send marketing you did not ask for.
## How long we keep it
The product shows history for the days on your plan: 30 on Pro, and 90 on Team. A workspace already on Free keeps 3 days. Events are deleted by month. A month is deleted after every day in it is older than that window, so a visit can remain stored for the rest of its month plus the plan window. It is not kept longer than that.
Account records, issues, and investigation reports stay while the account is open. They are not on the event clock.
## Who else handles it
We use these companies to run the hosted product. Each one receives only what that job needs.
- Supabase holds the Postgres database.
- Fly runs the API.
- Cloudflare serves the website.
- Polar handles payment.
- Resend sends email.
- GitHub, when you connect a repository.
- OpenAI, or the model provider configured in its place, receives the evidence an investigation needs.
## How we protect it
The hosted site and API are served over HTTPS. On top of that:
- Secrets are stripped in your process, before they reach us.
- Passwords are hashed with bcrypt. We cannot read them back.
- The session cookie is httpOnly, so a script on the page cannot read it.
- The dashboard API accepts signed-in requests only from the Bugwalk web origin.
- GitHub access is limited to the repositories you grant. Bugwalk can open a pull request on a new branch. It cannot push to the default branch, comment, merge, or write to your database.
- You can self-host the same server and keep every byte on your own machines.
## What you can do
Skip identify() and people stay anonymous. Add redaction rules, or do not send a field. Disconnect a repository, or uninstall the GitHub App. Self-host if you want the data on your own infrastructure.
Email hello@bugwalk.dev to delete the account and the workspace data we still hold, or to ask for a copy of the account record. Event history already expires on the schedule above.
## Children
Bugwalk is a tool for teams building software. We do not aim it at children, and we do not knowingly keep an account for anyone under 16. If you believe we have one, email hello@bugwalk.dev and we will delete it.
## Changes
If this page changes, the date at the top changes with it. If a change takes more data than we take today, we email the account before it applies.
## Contact
Questions about this page go to hello@bugwalk.dev. The rules for using the hosted product are on the Terms page.
---
# Terms
The rules for using the hosted Bugwalk product: accounts, data, plans, and ending it.
These terms cover the hosted product at bugwalk.dev. Creating an account, or sending events to hosted ingest, means you agree to them and to the Privacy page. The Apache 2.0 license on the SDKs and the server is separate, and these terms do not change it.
Updated 5 October 2026.
## What you agree to
You are agreeing for yourself, or for the company whose account you are opening. You need authority to do that, and a real email you control.
## The service
Bugwalk records events from your app, keeps a timeline per person, and can run an investigation that reads that timeline and the repositories you connect. We provide the hosted service as it runs today. An investigation can be wrong. Read the citations before you act on a report.
## Your account
Keep the password to yourself. You are responsible for what members of the workspace do with it. Do not share a login. If you think someone else has it, change the password and email hello@bugwalk.dev.
## Data from your app
You decide what your app sends. You are responsible for telling the people who use your app that visits are recorded, and for having the right to send that data to Bugwalk. Do not send data you are not allowed to share.
You keep your code, your events, and your customers’ data. We use them to run the product for you: to store the timeline, to show it to your members, and to write an investigation when you ask. The Privacy page says how that works and how long it stays.
## Acceptable use
Do not use Bugwalk to break the law, to probe systems you do not own, to store malware, or to send data you have no right to send. Do not interfere with another workspace, or try to get past a plan limit by abusing the API.
We can suspend an account that does this. We email you when we can do so without making the problem worse.
## Repositories
Connecting GitHub lets Bugwalk pull the repositories you select, run their tests, create one branch, and open a pull request. That grant ends when you disconnect the repository or uninstall the app. Bugwalk will not push to the default branch, comment, merge, or write to your database.
## Plans and payment
Pro and Team are billed monthly through Polar. A 3-day trial asks for a card so the plan can continue without a gap. You are not charged until the trial ends. Extra events on Pro and Team are billed as you use them, and we email you before that starts. A missed payment pauses recording. Bugwalk does not store your card number.
You can change or cancel the plan from Account. A change is prorated for the rest of the period. Fees already due stay due. Polar may add tax where it applies.
## Uptime and investigations
We run the hosted service with care. We do not promise it will be up at every moment. Enterprise can add an SLA in a separate agreement. Self-hosting is available if you would rather run the server yourself.
## Closing an account
You can stop by canceling the plan and emailing hello@bugwalk.dev to close the account. We delete the account and the workspace data we still hold, except records we must keep for tax or for a dispute that is already open.
We can close an account that breaks these terms, or that we cannot keep serving. We email the account when we do.
## Your code stays yours
You keep the rights to your code and your data. We keep Bugwalk: the hosted product, the site, and the name. Feedback you send us can be used to improve the product, with no obligation to pay for it.
## Liability
To the extent the law allows, Bugwalk is not liable for lost profits, lost data, or indirect damage arising from the hosted service. Our total liability for a claim about the service is limited to the fees you paid us for it in the three months before the claim. This does not limit liability the law says we cannot limit.
## Changes
We can update these terms. The date at the top changes when we do. If a change reduces what the plan includes, or takes new rights over your data, we email the account before it applies. If you keep using the hosted product after that, the new terms apply.
## Contact
Write to hello@bugwalk.dev. If something is wrong, start there and we will try to sort it out before anyone calls a lawyer.