Skip to main content
A project is the unit a widget embed points at. It decides where reports are filed, which sites may submit to it, and who can see the launcher. You normally edit this through the admin panel rather than by hand, and the panel writes the shape below, with the one exception called out in the table.

Fields

Validation

The relay rejects a project that breaks any of these, rather than storing it and failing later: Origins are scanned on every public request, so the cap on their number and length is a throughput concern as much as a correctness one.

Destinations

destination is one of three shapes, and its kind decides which fields apply.
Only owner and repo are required for GitHub, and only projectId for GitLab. host lets a GitLab destination point at a self-managed instance instead of gitlab.com. A project saved before destinations existed has no destination at all. Those still work: the relay reads the flat teamId, linearProjectId and labelIds fields as an implicit Linear destination, so nothing had to be migrated. If you set both, destination wins, except that a Linear destination with no labelIds of its own falls back to the flat labelIds.

Tokens

The API token is held by the relay and never reaches the browser, which is the reason the relay exists at all. You can set one token for the whole relay, and optionally override it on a single destination. A per-destination token wins where it is set, so one relay can file into two repositories owned by different people without either token being able to reach the other’s. What the token has to be able to do, which is all the relay ever asks of it: Nothing else is called, so a token scoped tightly to one repository or project is enough. Grant no more than that.

Where the screenshot ends up, which differs by destination

This is worth knowing before you pick one, because it decides what retention means for you. Linear and GitLab receive the image itself: the relay uploads it to the tracker, and it lives there for as long as the issue does. Relay retention does not touch it. GitHub has no attachment API the relay can use this way, so the relay stores the screenshot itself and puts a signed link to it in the issue body. The image is served from your own relay, which means retention applies to it: once the window passes, the issue keeps its text and its link stops resolving. If you self-host and file to GitHub, that is an argument for the unlimited retention a Pro licence gives you, or for keeping the screenshots yourself.

Content Security Policy

If your site sends a Content-Security-Policy header, the widget needs two directives to work and a third to take screenshots. Nothing else. The relay’s origin is the host in your embed src: https://feedback.example.com for a self-host, https://relay.burnsidesteps.com for the hosted service. img-src data: is not optional if you want screenshots. The capture renders your page into a data URI and loads it back as an image, so a policy without it fails the capture outright rather than degrading: the report still sends, with no screenshot attached. The browser console says so at the moment it happens, naming this directive. You do not need style-src 'unsafe-inline'. The widget’s own CSS is applied as a constructed stylesheet, which is not an inline style as far as the policy is concerned, so a strict style-src leaves the widget looking exactly as it should. On a browser old enough to lack constructed stylesheets the widget falls back to an inline <style>, which a strict style-src will block; it still works, it just renders unstyled. You do not need font-src either. The capture is told not to fetch remote fonts. A policy that covers all three looks like this:
If you use default-src rather than naming each directive, the same values apply to it.

Plan limits

Only the hosted service enforces this. On a relay you run yourself there is no sweep at all: the Cloudflare Worker template ships no cron, and the Node self-host keeps them until you remove them unless you set SHOT_RETENTION_DAYS, which defaults to keeping them indefinitely. The figure above describes what hosted applies to a Free account, not a ceiling your own relay imposes on you. On hosted, a nightly cron applies the window, so a screenshot outlives it by about a day. That sweep resumes across runs rather than restarting, so as the number of accounts grows a given account is reached every few nights rather than every night. Retention covers screenshots the relay is storing, which in practice means GitHub destinations: see above. The issue itself is never touched. It lives in your tracker, and nothing here can remove it.

Rate limits

These are per relay, and identical across the Cloudflare Worker and the Node self-host. The per-project and per-IP submission limits work together. The per-project ceiling protects your tracker from a flood, and the per-IP limit stops one visitor consuming that whole allowance and locking everyone else out.