You end up owning a fork
Both paths leave you running your own copy. Nothing phones home, and nothing updates itself. Pulling a future release is agit pull from upstream when you decide to, and nothing notifies you that
there is one. That is the trade for having no account, no per-seat pricing, and no vendor between
you and your data.
Prerequisites
git, Node 24 (or 26 and later) and pnpm 9 or later, plus a Cloudflare account. The free plan is
enough. wrangler is deliberately not installed globally: it is a devDependency of relay/
and runs through pnpm. Its own floor is lower, Node 22: its binary exits immediately below it.
Platforms. These commands are written for macOS and Linux, and for WSL on Windows. rm and
openssl are not available in cmd.exe or a default PowerShell. Where this page uses
openssl rand -hex 32, this works anywhere Node does:
pnpm install prints a few Failed to create bin at ... burnside-relay warnings. They are expected
on a fresh checkout: hosted and www depend on relay, whose bin points at
relay/dist/server.js, which nothing has built yet. The install exits 0 and the warnings stop after
the first build.
Have an agent do it
The steps on this page are the ones an agent follows. If you would rather hand it over, paste this into Claude Code, Cursor, or any agent with terminal access:Set up Burnside Steps on my site. The repo is github.com/greenwichsteps/burnside-steps. Deploy the relay to my Cloudflare account, connect it to my Linear workspace, and add the widget to my site. Create the KV namespace and paste its id into relay/wrangler.jsonc before the first deploy, and set ADMIN_SETUP_TOKEN as a secret before it too. Both orderings matter.Swap “my Linear workspace” for “my GitHub repository” or “my GitLab project”. The last step still needs you either way: claiming the admin panel and pasting a destination credential are deliberately manual. The two extra sentences are not padding. Steps 3 and 4 below both have to happen before the first deploy, and an agent handed only the first paragraph will deploy a relay that cannot store a config or claim a panel.
1. Create an API credential for your destination
Feedback can land in Linear, GitHub, or GitLab. Create a credential for whichever you use, and keep it somewhere safe. It is pasted once into the admin panel and is never stored in the repo. Linear. Settings, then Account, then Security and Access, then New API key. Scope it either to full access or to write and create-issue permissions on the teams that will receive feedback. You also need the team UUID for each project, which you can copy from the Linear UI. GitHub. A personal access token withrepo scope, or a fine-grained token with issue write
permission on the target repository. You need the repository owner and name.
GitLab. A personal or project access token with api scope. You need the numeric project
id, and for self-managed GitLab, the host of your instance.
2. Authenticate wrangler with Cloudflare
3. Create the CONFIG KV namespace
The relay stores project configuration and destination tokens in a Workers KV namespace. This is what the admin panel writes to.CONFIG binding in relay/wrangler.jsonc ships without an id, so add the one you just
created:
wrangler.production.jsonc in relay/ is the Greenwich Steps
deploy config, kept in the repo so the project deploys from its own source. It is not useful to
you, and the repo’s tests expect the template to carry no account-specific values, so remove it
now that you have pasted your own id:
pnpm test stays green.
4. Set the admin setup token, before the first deploy
The first-run endpoint that creates an admin password is unauthenticated by nature: anyone who found the URL before you set a password could otherwise claim the panel. It is gated behind anADMIN_SETUP_TOKEN secret.
Generate a long random value, for example with openssl rand -hex 32, and set it as a Worker
secret before deploying:
Run it locally first, if you want to
Optional, and not part of the deploy path.pnpm dev starts wrangler’s local server on
http://localhost:8787, serving the demo page and the admin panel from your own machine.
Local runs do not read the secrets you set with wrangler secret put, which only exist on a
deployed Worker. They read relay/.dev.vars, which is gitignored. Copy the template and fill it
in:
__Host-bs_admin, and a browser takes a __Host- cookie only from an origin it treats as secure.
Current Chrome and Firefox count http://localhost as secure, so the panel works in both. Safari
does not: it accepts the sign-in and then drops the cookie, so you cannot stay signed in there, and
http://127.0.0.1 is no different. To use the panel in Safari, serve local dev over TLS with a
certificate Safari trusts, or use a deployed relay. The demo page and the widget work in every
browser.
5. Deploy
relay/public/, then runs wrangler deploy. Note
the resulting URL, for example https://burnside-steps.<your-subdomain>.workers.dev.
6. Claim the admin panel
Visithttps://<relay-host>/admin.
-
First visit is setup. Enter the
ADMIN_SETUP_TOKENfrom step 4, then choose a password of at least 8 characters. There is no separate account system: one password gates the panel. The token must be at least 24 characters, and setup is refused outright if it is shorter, however correctly it is typed. -
Delete the setup token. It is needed once, but it stays a credential that claims the panel
for as long as it exists: if the credentials are ever cleared, whoever still has that token can
claim the panel before you do.
Self-hosting with Docker or npm: remove the line from your
.envand restart. - Sign in with that password.
- On Integrations, paste the credential from step 1 and test the connection.
- On Projects, add a project. Set its allowed origins, its destination, and any tag to label mapping. Project ids and origins live here, not in the repo, so adding a site later needs no redeploy.
7. Install the script tag on a site
Add this before</body>, or through your platform’s snippet or header-scripts feature:
<project-id> must match a project you added in step 6, and that project’s allowed origins must
include the exact origin of the site, for example https://staging.acme.com.
For a live site, add data-mode="query". The button then appears only after someone visits
any URL with ?feedback=on, which persists for that browser tab. Share that link with the people
you want giving feedback.
There is no integrity attribute by design: the script is first-party, served from a relay you
control, and an SRI hash would break every installed site on each widget deploy.
8. Verify end to end
Open a site with the tag installed, submit one piece of real feedback with an annotation, and confirm the issue appears in your tracker with the screenshot attached. This is the one check no automated test covers, because it exercises your tracker’s handling of a real payload. If your destination is GitHub and the issue arrives with no image, nothing has failed. GitHub has no API for attaching an image to an issue, so the relay stores screenshots itself and needs an R2 bucket, which is not part of the steps above. See Enable screenshots for GitHub under Optional. Linear and GitLab upload the screenshot through their own APIs and need nothing extra.Optional
Enable screenshots for GitHub. Only GitHub needs this. Linear and GitLab upload the screenshot through their own APIs and work with no extra setup. GitHub has no API for attaching an image to an issue, so the relay stores the screenshot itself and links to it. Without a bucket, GitHub issues arrive text-only. Nothing raises an error and nothing is logged: the issue body simply reads_Screenshot unavailable._ where the image would have been.
relay/wrangler.jsonc, beside the existing kv_namespaces block:
SHOTS. Redeploy with pnpm deploy.
Screenshot URLs are unguessable but public: anyone holding one can open the image, because the
<img> GitHub renders carries no cookie. Signing them means only URLs the relay itself produced
are served, so a guessed or altered path is refused:
*.workers.dev, which some corporate filters and ad
blockers do.
Rotate a destination token. Paste a new one into the admin panel’s Integrations tab. No code
change and no redeploy.
Reset the admin password. There is no self-service reset. Clearing the credentials re-opens
first-run setup, so the order matters: set a fresh token first, claim the panel, then remove the
token again. Doing it the other way round leaves the panel claimable by anyone holding the old
token, for as long as it takes you to notice.