* Document what actually keeps a cross-domain reader signed in The separate-domain manual tells operators to set ALLOWED_HOSTS and AUTH_SAME_SITE and says authorisation then works anywhere. That stopped being true as browsers began blocking third-party cookies: the server-set auth cookies carry no Partitioned attribute, so a browser enforcing the block drops them whatever their SameSite value. What survives is AUTH_SEND_JWT_HEADER, where the token returns in a header and the widget writes its own partitioned cookie from inside the frame, and the manual never mentioned it. It now does, with the XSS trade-off and a pointer to the parameter page, and it says plainly that this rescues Email, Telegram and anonymous but not oAuth. The parameter page's own mitigation list was left wrong by #2197. It promised SameSite=Strict cookies and a __Host- prefix on HTTPS; authCookieOptions drops the prefix entirely and uses SameSite=None; Secure; Partitioned whenever the widget is embedded on another domain, which is the case the flag exists for. * Say that the JWT header is sent in addition to the cookies, not instead Both the flag's own help and the parameter table said the header replaces the server-set cookie. Service.Set does neither: it writes the header and then falls through to set both cookies, with a comment saying the cookies are needed because headers do not survive the OAuth redirect. An operator reading either description would expect the server to stop setting cookies once the flag is on, and would misjudge what the flag changes about their exposure. * Correct three details in the cross-domain documentation The link to the parameter page used Zola's @/ syntax, which Hugo emits literally as a relative href since there is no render-link hook. It was the only such link under site/content; the other manuals use the relative form and this now does too. The CHIPS description claimed Partitioned makes the cookie unreadable from any other page the browser visits. The partition key is the top-level site, so a different site gets a separate cookie while pages and subdomains under the same site share it. Overstating isolation on the page an operator reads to weigh risk is the wrong direction to be wrong in. And Chrome does not block third-party cookies by default: Google's April 2025 position keeps ordinary Chrome on user choice and names Incognito as the mode that blocks. Naming Safari, Chrome Incognito and browsers configured to block them says the same thing and stays true. * Drop AUTH_SAME_SITE from the recommended cross-domain recipe Measured rather than reasoned, because it reverses guidance this page has carried for years. With only the remark42-https service taken back to the default, both reload cases pass for anonymous and email, under a permissive browser and under one enforcing partitioning. The cookie jar after an anonymous sign-in says why. With the setting there are four cookies: the server's unpartitioned JWT and XSRF-TOKEN, and the widget's own partitioned pair. Without it there are two, the widget's pair alone, and the session behaves identically. So the setting is doing something real, which is what makes the passing run meaningful, and what it does is add an unpartitioned HttpOnly JWT delivered as a third-party cookie to every listed domain wherever the browser still permits that. Nothing needs it. It stays documented for the configuration that does need it, which is one without AUTH_SEND_JWT_HEADER, where the server's cookies are the only ones there are. One prediction the experiment falsified: the attribute case was expected to fail on the default server-set pair. It passes, because a cross-site Set-Cookie lacking SameSite=None is refused outright, so that pair is absent from the jar instead of present with the wrong attribute. The manual now says so.
Remark42 site
Sources for remark42.com, built with Hugo.
Requirements
Hugo, the plain build; nothing here needs the extended one. The version the published image builds with is ARG HUGO_VERSION in Dockerfile, and any release at or above it works locally. Nothing updates that pin automatically: Dependabot's docker ecosystem reads FROM references, and the Hugo version is a bare string in a download URL, so bumping it is a manual edit. brew install hugo, or see the installation guide. Nothing else: no Node, no package manager.
Development
hugo server
Serves the site on http://localhost:1313 and rebuilds on change.
Alternatively, without installing Hugo:
docker compose up
Build
hugo --minify --cleanDestinationDir --destination build
Writes the static site to build/, which is what the Docker image serves. --cleanDestinationDir matters on a rebuild: without it a page you deleted, and the fingerprinted stylesheets of earlier builds, stay behind.
Layout
| Path | Contents |
|---|---|
content/ |
Pages as markdown. content/docs/ is the documentation tree |
layouts/ |
Templates. partials/, shortcodes/ and _markup/ render hooks |
assets/ |
styles.css and the scripts, fingerprinted at build time |
static/ |
Files copied to the site root as-is: favicons, manifest, robots.txt |
data/nav.json |
The documentation sidebar |
hugo.toml |
Site configuration |
Writing docs
A page is a markdown file with a title in its front matter. A directory becomes a section when it holds _index.md, and a page that carries its own images is a directory with index.md and the images beside it.
Adding a page to the sidebar means adding an entry to data/nav.json; the paths there are relative to /docs.
Callouts use the note shortcode, which takes the emoji to show:
{{< note "💡" >}}
Anything markdown here.
{{< /note >}}
Two documentation pages are symlinked into the repository as backend/README.md and frontend/apps/remark42/README.md, so moving or renaming content/docs/contributing/backend/index.md or content/docs/contributing/frontend/index.md means repointing those symlinks.
Styling
assets/styles.css is hand-written, with the palette and light/dark values as custom properties at the top. The dark theme is applied by a dark class on <html>, set before first paint by assets/inline.js and toggled by assets/script.js.
Code highlighting is Hugo's built-in Chroma. The rules at the end of the stylesheet come from hugo gen chromastyles --style=github and --style=github-dark.
Dates
The "Updated" line on a documentation page comes from .Lastmod. hugo.toml resolves it through [frontmatter] lastmod, which tries git, then a lastmod in the page's front matter, then the file's modification time; without that chain it would resolve to .Date and every page would read Jan 01, 0001. A deployed page therefore shows the build date, since a checkout does not preserve modification times.
HUGO_ENABLEGITINFO=true hugo gives real per-page commit dates. It is off by default because the image build context is site/ alone, where there is no .git for Hugo to read and it fails rather than falling back.