annotepage

Frequently asked questions

Everything you always wanted to know about annotepage, and a few things you would not have thought to ask.

See a detailed usage example See every way to install it

What if I lose the key?

Key

In secure mode, the notes go with it. There is no recovery and no rotation — the key is what decrypts them, and the server has never had it. In open mode the key is written in the tag, on a page anybody can read, so it cannot be lost.

Can I let someone read the notes without writing any?

KeyWho can do what

No. The key reads and writes; there is no reader-only role.

What exactly is encrypted, and by what?

KeyPrivacy

The remark, its author, the element it points at and the page it is on — everything a reader wrote. Your browser encrypts it with AES-256-GCM, using a key derived from yours by HKDF-SHA-256, and sends the result. The key stays in the browser: it travels in the tag or in a paste, never in a request. What the server stores is a block of bytes it cannot open, a project id, and a blind index saying which page — which is how it can group notes without knowing a single path. The exact scheme is in FORMAT.md.

Where does the key come from?

KeySetting it up

Your browser draws it, on the install page, and sends it nowhere. The project id in the tag is derived from it and does not lead back to it — the server recognises that id, it never issues one.

What does the notes server see?

PrivacyYour own server

Counts, times, sizes, IP addresses and your domain. Never your paths, never your text — in secure mode it cannot read a line it stores.

What statistics does annotepage collect?

PrivacyYour own server

Usage totals, and nothing else. Once a day, each annotepage server tells the project three numbers: how many projects, notes and pages it has carried, along with its version. That is how we know how many servers run annotepage, and how much they are used.

Nothing identifies you. No domain name, no project, no page, not a word of a note. Servers are told apart by an identifier each one draws at random, tied to nothing. Addresses are never stored: only a keyed fingerprint shared by hundreds of networks, kept for the day — the rigour asked of far more sensitive data.

It is on by default, and one line turns it off: 'report_statistics' => false in internal/config-local.php, or the matching option in the installer.

Is there an account, a moderator, a way to delete a note?

Who can do whatPrivacy

None of them, and no notification either. A project is a key and the notes under it. Nobody can point at a remark and make it go — age can, and only age: see the next answer.

How long are the notes kept?

Who can do whatPrivacyYour own server

For as long as the server that holds them says. The installer writes ninety days into every configuration it creates, counted from a thread’s last message: the whole thread goes at once, so a reply is never cut off its remark, and a discussion that is still live is never cut short. Nobody chooses which — age, and nothing else, which is what keeps “no moderation” true. The panel on your pages says the number the server really uses, and it says how many notes have already gone that way. On your own server, one line in the file keeps everything for ever.

Is there a limit on what can be written?

Your own serverWho can do whatSetting it up

Yes, four of them, and every one is a line in your configuration with a default that suits a review team. They are counted per five-minute window: 120 writes per IP address, 300 writes per project — everybody writing to it together — and 90 exports per IP. Past one of those the answer is a 429 saying when to come back. A request body over 64 KB gets a 413. And a project may be capped at a number of rows, which a public relay sets to 6000 — about 2000 remarks, since a discussed thread is three rows — and a server carrying one team’s own notes leaves off.

You do not have to find them afterwards: the installer has a field for every one, folded under “change anything else”, with the number that applies if you leave it empty shown in the field — and the same on the command line, where --help lists each one with its default.

Can I switch from open to secure later?

KeyPrivacy

For new notes, yes: change the tag. The notes already written keep the mode they were written in, and nothing converts them.

Can the page sit behind a login?

PrivacyYour site

Yes. Neither the notes server nor the assistant ever opens it: the MCP talks to the notes API and to nothing else, and the page is opened by the reviewers’ own browsers, which are already signed in.

Can I try it on my own machine?

Setting it upYour site

Yes: localhost is a secure context, so the browser has the crypto it needs. But never declare localhost:3000 in a server’s list of domains — that name belongs to every machine on earth, so declaring it lets any of them write to your project.

Is it free?

Cost and licence

Yes, and it is MIT. The shared notes server at api.annotepage.com is free to use with nothing to install; running your own is free too, and that is the whole list.

Do I have to use your server?

Your own serverCost and licence

No. One PHP file installs your own, and the install page takes you through it. Whichever one you run, its address is what goes in the tag.

What does the daily line change on my server?

UpdatesYour own server

Three things. The update: it asks which version is published, and when it is newer, downloads only the files that differ, checks each one, and swaps them in. The files it replaced are kept in .update/previous-<version>-<date>, beside the install. The maintenance: it removes the threads older than your configuration keeps them — ninety days after the last reply, unless you changed it — and adjusts the tables when a release needs it. The statistics: it tells the project how much this server carries — three totals, under an identifier drawn at random — unless you turned that off.

It never touches internal/config-local.php, never puts install.php back once it is deleted, and never replaces a store file you have edited yourself.

Where does an update come from, and what if it fails halfway?

UpdatesYour own serverPrivacy

From the project’s repository on GitHub, over HTTPS with the certificate checked. Every file is hashed once it is on your disk and compared with the SHA-256 in the published manifest: one mismatch and nothing is changed. The new files wait beside the old ones and are swapped in together; if the swap fails, the old ones go back. A run that overruns its time swaps nothing and tries again the next day.

What an update takes from your server is a request for the version number, once a day, and the files when there is a release. Nothing about your notes, your sites or your visitors — the one other thing a server sends is its daily totals. The line the install page gives you runs at a minute and an hour drawn for your install, so servers do not all ask at the same second.

Is the address for the scheduler safe if somebody finds it?

UpdatesYour own server

It carries a long secret the installer writes and shows you once. Without it, the server answers as if the action did not exist. With it, somebody can make your server check for a release and run its maintenance — at most one real check a day, or every time with &force=1 — and nothing else: what gets installed is still only a release whose every file matches the published manifest.

If it leaks, into a host’s logs for instance, change update_token in internal/config-local.php and paste the new address into the scheduler.

Can I update by hand instead, or not at all?

UpdatesYour own server

Yes. Run php internal/update.php when you choose; --only-maintenance does the housekeeping and fetches no update, and --help lists both.

With nothing scheduled, the server keeps working on the version it has. Old threads still go, but only when somebody writes a note, and then one time in fifty — so a quiet project keeps them longer than its configuration says.

Can I install the assistant's tool for good?

Setting it upUpdates

Yes. By default nothing is installed: you say npx when you ask, and your assistant fetches the latest version each time. Installed and plugged in, its tools are there in every conversation instead:

npm install -g annotepage-mcp
claude mcp add annotepage -- annotepage-mcp

With npx, nothing stays on the machine: the latest version is fetched each time, so there is never anything to update.

Installed, it stays, and it does not update itself. When it is behind it says so to your assistant, which tells you: updating is npm install -g annotepage-mcp@latest, often with administrator rights, and then restarting the assistant — two things it cannot always do on its own. Once installed, npx annotepage-mcp runs that copy too.

Can I run a version before everybody else?

Setting it upThe tag on your page

Yes, and it is one word in each place.

The tag on your pages — ask for @next instead of @2:

https://cdn.jsdelivr.net/npm/annotepage-client@next/dist/annotepage.js

Your own notes server — one line in internal/config-local.php, pointing it at the candidate branch:

'update_source' => 'https://raw.githubusercontent.com/tuxo83/annotepage/next/server/webroot/',

It stays hashed against the manifest either way: a candidate is checked exactly as a release is. Put main back and the next daily check returns it to the stable one.

The assistant — nothing. The MCP talks to your server whichever code it is running.

Why @2 and not @latest?

The tag on your pageNothing appears

Because it is not the ceiling it looks like. A page carrying @2 still ends up running version 3 — your notes server announces the client it works with, and the client fetches that exact version and replaces itself, major number included. What @2 decides is only which copy loads first.

And that order is the point. A major version is the one release allowed to change the protocol; the client is on a CDN and the server is on somebody's machine, so @latest would deliver the new client to every page the day it is published, in front of servers that have not moved yet. Announced instead, the server is already running it before it says so.

If you would rather have the newest whatever your server is running, ask for it — it is the same one word:

https://cdn.jsdelivr.net/npm/annotepage-client@latest/dist/annotepage.js

A release candidate never arrives through any of the three: ranges exclude pre-releases, latest is never moved to one, and the client refuses to install one on its own.

Where is the source?

Cost and licence

All of it, MIT: github.com/tuxo83/annotepage — the client, the notes server, the MCP and this site. The two published packages are annotepage-client (the tag) and annotepage-mcp (what plugs an assistant onto it). The server is not a package: it is one PHP file that fetches the rest.

Can I load it as a module?

The tag on your pageNothing appears

No. The client reads document.currentScript to find its own settings, and a type="module" script does not have one: nothing appears on the page. It is no longer silent about it — one line goes to the console — and the settings can be declared on the page instead.

My build combines the scripts, and nothing appears

The tag on your pageNothing appears

The client reads its settings off its own tag, through document.currentScript — which is null for a file concatenated with others by a “combine JS” option or an asset pipeline, inlined, or loaded as a module. There is then no tag to read, so the client stands down having drawn nothing and sent nothing, and writes one line in the console saying why.

The same settings can be declared on the page instead, in one object, before the client is loaded:

<script>
  window.annotepageConfig = {
    server: 'https://example.com/annotepage/api.php',
    key: '<43 characters>'
  };
</script>

The names are the attributes without data-, and server is required there: a tag served by your own site falls back on ../api.php beside its own file, and with no tag there is no file to count from — an address guessed wrong would send the remarks nowhere at all, so it is refused instead. A page may carry both a tag and the object, and they must then say the same thing: anything else is refused, naming the setting they disagree about. The tag stays the standard way, and the install page writes it for you.

What if I pin the script with a wrong integrity digest?

The tag on your pageNothing appears

The browser refuses the file and the tool simply does not appear. Nothing on the page says why. The install page writes the digest for you when you ask for a pinned tag.

My site sets a Content-Security-Policy. What do I need?

The tag on your pageNothing appears

Two directives. script-src must allow wherever the client is served from, or it never loads; connect-src must allow your notes server, or fetch fails and the tool stands down in silence, as if nothing had been configured. style-src usually needs nothing: the stylesheet goes in as a constructed sheet, which is not an inline sheet in the policy’s sense.

Can one page carry two projects?

The tag on your page

No: one tool runs on a page at a time. The first tag loaded wins; another one on the same page is ignored, and says so in the console.

Several projects on one site are fine — one per section, one per client — as long as each page loads only its own tag. Deciding which one a page gets belongs to your site’s own code.

A switch between projects is possible, and it is the site’s to build: a small control that reloads the page with the other project’s tag. The two never run at the same time — the reload is what hands the page from one to the other.

Can reviewers annotate only part of a page?

The tag on your page

Yes. Add data-zone to the tag with a CSS selector, or several separated by commas — the article, not the header, the menu or the footer:

data-zone="article"
data-zone=".post-content, .comments"

In annotation mode those areas are outlined, and a click anywhere else writes nothing and says why. Notes already written outside stay visible. If the selector matches nothing, nothing can be annotated — the tool never falls back to the whole page.

It guides reviewers; it is not a security boundary. The restriction lives in the browser, where anyone determined can lift it.

I send the HTML file instead of a link. Will everyone see the same notes?

The tag on your pageNothing appears

Not on its own. A page is identified by the path in its address, and a file opened from a disk has the path of the folder it was saved in — a different one for every person, and for you as soon as you move or rename it. Same file, same key, and each reader gets an empty page of their own.

Tell the file which page it is, with data-page on the tag. Every copy then shares one set of notes, wherever it is opened from:

data-page="/reviews/budget-2026.html"
data-page="/3f2b8c1e-9a4d-4c7b-8e21-5d6f0a1b2c3d"

The value is yours to choose, written like a path: it starts with one /, has no space and no ... Anything else is refused on screen rather than corrected. Give it the path of a page that is online and the copy shares that page’s notes.

Three things to know. Put it on a single document, never in a template shared by a site — every page would become the same page. Notes written before you added it stay under the old path. And a file opened from a disk has no site address, so a project restricted to a list of sites refuses it; it needs a project without that list.

What language does it speak?

The tag on your pageYour site

Notes: any language. The tool encrypts them and never reads them. Its own buttons and messages ship in English and French.

The panel follows the page. A page declaring <html lang="fr"> (or fr-CA) gets it in French with nothing to add, and a French WordPress already declares it. Any other language stays English. The browser’s language is ignored on purpose: two people looking at one page see the same panel.

To force a language, change a word or add your own, point the tag at a label file. It always wins, and a copy of the client you host yourself needs it; a label it lacks falls back on English:

data-labels="https://cdn.jsdelivr.net/npm/annotepage-client@2/labels/fr.js"