1. Deploy your own backend
About ten minutes. You need a Cloudflare account, where the free tier is plenty, and a Resend API key for the emails. Clone the repository first.
cd worker
npx wrangler d1 create feedtack # copy the database_id it prints
# paste it into wrangler.toml, then set DESTINO / REMITENTE / ORIGENES_PERMITIDOS
npx wrangler d1 execute feedtack --remote --file=esquema.sql
npx wrangler deploy
npx wrangler secret put RESEND_API_KEY # your Resend key
openssl rand -hex 24 | npx wrangler secret put CLAVE_ADMIN # your team key, save it
| Variable | What for |
|---|---|
DESTINO | Where the comments are emailed to. |
REMITENTE | The sender address, on a domain verified in Resend. |
ORIGENES_PERMITIDOS | Comma-separated allowlist of sites that may send comments. Wildcards like *.pages.dev work. Anything else is blocked by CORS, on purpose. |
GET /salud on your Worker returns {"ok":true} when it is up.
2. Add the widget
<script src="https://cdn.jsdelivr.net/gh/alvaromassana/feedtack@main/widget/feedtack.js"
data-site="client-slug"
data-endpoint="https://your-worker.workers.dev"
data-color="#4f46e5"
defer></script>
| Attribute | What for | Default |
|---|---|---|
data-site | Identifies the client, shows up in the email subject. | sin-identificar |
data-endpoint | Your Worker's base URL. | none, required |
data-color | Accent colour, usually the client's brand. | #4f46e5 |
data-label | Button text. | Comentar / Comment |
data-position | borde-derecho (a tab on the right edge), bottom-right, bottom-left, top-right. | borde-derecho |
data-lang | es or en, overrides the page's lang. | auto |
Pin the URL to a release (@v0.1.0) instead of @main if you don't
want to pick up changes automatically.
3. Hand out the links
- Your team. Open the site once with
?feedtack_admin=YOUR_KEY. It is remembered in that browser. With the key you can resolve, mark your own notes as done, reopen and delete. - Each reviewer. Send them
https://staging.example.com/?feedtack_yo=Their%20Name. From then on everything they write is signed, and they can edit or delete their own comments. Without a link, the widget asks for a name the first time and remembers it.
Without the key you can write, edit and delete your own, and confirm or reopen what has been resolved. Nothing else.
?tack_yo= and ?tack_admin= links and still reads the identity saved
in the browser under the old name.
The WordPress plugin
Download
feedtack.zip and install it like any other plugin. Settings live under
Settings → Feedtack: site slug, Worker endpoint, colour, button text, position and panel
language. Leave the language empty to follow the page's lang, and set it when the
site is written in one language and reviewed in another.
The reason it exists is not compatibility, it is the lock: it will not load if
wp_get_environment_type() returns production, unless you tick a box,
which then shows a permanent red warning. It also stays out of the admin, ajax calls, cron and
feeds. The environment comes from WP_ENVIRONMENT_TYPE in wp-config.php.
What lands in your inbox
Every new comment, edit, reopening and deletion sends an email with the text (previous version struck through on edits), the pointed elements with their selectors and sizes, the attachments, and the context: page, URL, viewport, browser, OS, scroll position and time.
The review workflow
open ──(your team, with the key)──> resolved ──(the client)──> confirmed
^ │
└───────────(the client)────────── reopened
The client writes and can edit their own while it is not closed. Your team resolves. The client confirms or reopens, and you get an email either way. Deleting, by the team or by the author, emails a copy, which is the only trace that is kept.
What is stored, and where
One table in your own D1 database, comentarios: the text, the pointed elements
(selector, text, position), the page, the anonymous author id and name, the state, a history of
state changes and previous texts, and the browser context. Replies live in their own table.
The schema is
worker/esquema.sql.
Attachments are emailed, not stored. The panel shows the count, not the files. Storing them would mean adding R2.
Honest limitations
- Identity is not authentication. Who wrote what is an anonymous id in
localStorage. Enough for a private staging site, not enough for anything sensitive. Switch browsers and you lose the ability to edit what you wrote: a personal link restores the name, not the id. - No rate limiting. An allowed domain can send as much as it likes.
- Editing changes the text and the pointed elements, not the attachments.
- Screenshots use
getDisplayMedia, so the browser asks the reviewer to share their screen. On browsers without it they can attach a screenshot they took themselves.
The full picture is in SECURITY.md.
The guide for the people reviewing
Your client should not read any of this page. Send them the guide instead: Spanish or English, one short clip per action. There is also a narrated version. Same page for every client, no sign-up, no names inside.
It shows the interface, so it stops being true the day the interface changes. Regenerate it
with node qa/guia-clips.mjs and deploy again.