NostrComments is a browser extension that adds a comment thread to any web page, stored on public Nostr relays rather than on the site. This box lets the readers of your site read that thread under each article, without installing anything. To write a comment, they install the extension.
Nothing loads until a reader presses Show comments from Nostr. Until then the snippet is a button on your page and contacts nobody.
When a reader presses it, their browser:
nostrcomments.github.io, which is hosted by GitHub. GitHub sees the reader's IP address and that some page shows the box, but not which page: the address is passed after the # in the box's link, and browsers never send that part to a server.nos.lol, relay.damus.io, relay.primal.net, nostr.oxtr.dev, nostr.mom and relay.nostr.net. Each of them sees the reader's IP address and the address of your page, as any server a browser connects to would.There are no cookies, no analytics and no tracking of any kind, and nothing is sent anywhere else. No pictures or avatars are loaded. The box cannot see your page, and your page cannot see into the box.
Please tell your readers. A line you could add to your privacy policy:
Comments under our articles are loaded from public Nostr relays, but only when you press “Show comments from Nostr”. Your browser then connects to those relays and to GitHub, which see your IP address; the relays also see the address of the page. We do not store or control these comments.
Paste this where the comments should appear, in the template of your article pages:
<div data-nostrcomments></div>
<script>
(function (box) {
// NostrComments comment box: https://nostrcomments.github.io/embed/
// Until the button is pressed, nothing is loaded and no server is contacted.
var button = document.createElement('button');
button.type = 'button';
button.textContent = 'Show comments from Nostr';
box.appendChild(button);
button.onclick = function () {
var canonical = document.querySelector('link[rel~="canonical"]');
var params = new URLSearchParams({ url: box.getAttribute('data-url') || location.href });
if (canonical && canonical.getAttribute('href')) params.set('alt', canonical.getAttribute('href'));
if (box.getAttribute('data-theme')) params.set('theme', box.getAttribute('data-theme'));
var frame = document.createElement('iframe');
frame.src = 'https://nostrcomments.github.io/embed/frame.html#' + params;
frame.title = 'Comments from Nostr';
frame.setAttribute('sandbox', 'allow-scripts allow-popups allow-popups-to-escape-sandbox');
frame.setAttribute('referrerpolicy', 'no-referrer');
frame.style.cssText = 'display:block;width:100%;height:180px;border:0';
window.addEventListener('message', function (e) {
if (e.source !== frame.contentWindow || !e.data || e.data.nostrcomments !== 'height') return;
frame.style.height = Math.min(Math.max(Number(e.data.height) || 0, 80), 20000) + 'px';
});
box.replaceChild(frame, button);
};
})(document.currentScript.previousElementSibling);
</script>
utm_source are left out, so every reader lands in the same thread as people using the extension.<div data-nostrcomments data-url="https://example.com/article"></div>data-theme="light" or data-theme="dark" to the same line.'sha256-LfnHEeqEcktaKDJwmO3HOFG4S6oQY88jjOA/P6ZtYx4=' to script-src, and https://nostrcomments.github.io to frame-src. The hash only matches the snippet exactly as above.This is the snippet above, showing the thread of this site's front page.
The box is a static page with no server behind it. Every comment, vote and name is shown only after its signature has been checked in the reader's browser, and the reading rules are copied from the extension, with a check that fails when the two drift apart. The source is here, and the extension's is here.