Self-hosting
Admin panel
Every instance setting, moderation queue, and security switch — all at /admin, no config files.
Sign in as an admin or a moderator and open /admin. Everything the setup
wizard set — and more — is editable here, subject to your role.
Instance
- Name and public domain.
- The federation toggle.
- Session-secret rotation.
Domain and federation changes apply on the next restart; the page tells you when a restart is needed.
Rotating the session secret
Rotate the secret that signs login sessions only if you suspect it was exposed. It signs everyone out — including you — on the next restart, and is guarded by a confirmation dialog.
If you pinned the secret yourself via the SESSION_SECRET environment variable,
the UI tells you to rotate it there instead: an env-supplied value wins over the
generated one.
Switch modes, edit connection details, generate DKIM keys, verify DNS records, and send a live test. Passwords are write-only — the UI shows “unchanged” rather than reading a stored secret back to the browser.
Full detail: Email.
Reports
The moderation queue, shared with moderators. Review user-reported posts and actors, remove posts, and dismiss reports. Reports cover both local and remote content.
Users
Suspend or reinstate local accounts. A suspended account cannot sign in, and its content stops being served. Moderators share this tab but can only act on regular accounts — never on admins or fellow moderators.
Deleting an account hides it everywhere but keeps it restorable for 30 days — see Moderation tools. Erasing it from Recently deleted is permanent.
Edit updates an account’s profile — display name, bio, public email, custom section, tags, links — or its login email. A login-email change needs re-verification: the new address gets a confirmation link, the old address gets a security notice, and the account stays unverified until the new address is confirmed.
Federation
Defederate (block) a domain, or re-federate it. Blocking purges already-cached content from that domain — it is not just a forward-looking filter.
Security
AI-scraper protection
A lightweight proof-of-work challenge (Anubis) in front of the interactive half of the app. It is off by default.
When enabled, browser-like traffic to these routes is challenged — a real browser solves it in about a second, most scrapers cannot:
/composeand/search/dashboard,/drafts,/posts/manageand the post editor/settings,/admin,/notifications,/follow-requests,/lists- Sign-in, registration, and password reset
Deliberately never challenged:
- Reading — the home feed, profiles, posts, tag pages and public reading lists, for everyone, with or without JavaScript.
- Federation — ActivityPub delivery keeps working.
- The API — API clients are untouched, and so is everything a page loads for itself: more responses, further pages of a feed, every uploaded image.
- Feeds and sitemaps — RSS readers and search engines keep working.
- OpenGraph crawlers — link-preview cards still render.
- URLs that do not exist — a bad link gets a real 404, not an interstitial, so stale URLs can drop out of search indexes.
A request that does not send a browser-shaped User-Agent is never challenged either, on any route.
What a challenged request gets back
503 Service Unavailable, with Retry-After: 3600 and
<meta name="robots" content="noindex,nofollow"> on the page.
This is deliberate, and worth knowing before your monitoring tells you about it. A challenge is a temporary refusal to serve, and 503 is the code that says so: a search engine keeps the URL and comes back, a link checker does not report a dead link, and an archive does not store an interstitial as the page. Answering 200 — which is what the challenge service does by default — tells every automated client the request succeeded while handing it a page that is not the one asked for, which a search engine reads as a soft 404 and, since every challenged URL returns the same interstitial, as a set of duplicates of one another.
A denial (no rule issues one in the shipped policy) answers 403 Forbidden.
None of this changes anything for a reader: browsers render an error page and run its scripts exactly as they would a 200, so the challenge is solved and the page loads as before.
The toggle applies live: no restart, no config file. The backend re-routes the app through the bundled challenge service via Caddy’s internal admin API. Per-IP rate limiting stays accurate.
The challenge service always runs but only sits in the request path while the
toggle is on. Its signing key is generated once into the secrets volume, so
restarts and upgrades do not re-challenge anyone who has already solved one.
Which routes are challenged is set by botPolicy.yaml at the repository root.
It is read once at startup, so an edit needs docker compose restart anubis.
What a visitor without JavaScript sees
Solving the challenge needs JavaScript, so a visitor browsing without it — off in the browser, or blocked for your domain by an extension — cannot get past the screen. Neither can one whose browser runs JavaScript perfectly well but failed to fetch the challenge script.
The challenge screen carries a footer line for both of them: what the screen is, that reading needs none of it, and a link to your home page. Beside it is an Imprint link to a longer page explaining which of the two failures a reload will fix.
Nothing they are likely to want is behind the wall. Reading is never challenged, so the link out is a real way back into the site rather than an apology.
To add your own support address to that explanation, edit the impressum.page
body in botPolicy.yaml and restart:
docker compose restart anubis
What the challenge screen looks like
The screen is drawn by the challenge service, not by Omicron — it answers before the request reaches the app — so it is not a page the admin panel can change. Left alone it arrives in that project’s own colours, which look nothing like the rest of your site.
Omicron replaces its stylesheet instead. anubis-theme.css at the repository
root carries the app’s palette, typography and dark mode, and Caddy serves it at
the path the challenge page asks for. Edit that file to restyle the screen for
your instance; the change shows up on the next page load, with nothing to
restart.
The mascot, the wording and the “Protected by Anubis” footer are left as the upstream project ships them. Custom images and custom titles are what its authors sell as BotStopper.
The wording is already translated, and by the challenge service rather than by
Omicron: it reads the visitor’s Accept-Language header and renders in whichever
of its 25 languages matches — Polish, Italian, Turkish, Ukrainian, Chinese and
others — falling back to English. Azerbaijani is not among them. The one part
that stays English whatever it picks is the footer line and imprint page above,
which are your instance’s own words; translate them in botPolicy.yaml if your
readers need it.
Media
Optional. Controls where writers can search for a post banner from inside the editor.
Openverse is always available and needs nothing configured — no account, no key. Every instance has working photo search out of the box.
Unsplash is opt-in, and opt-in by necessity: Unsplash issues a key per registered application and has no anonymous mode, so unlike Openverse it cannot work untouched. Create a free app at unsplash.com/oauth/applications and paste its Access Key here to add Unsplash as a second tab in the picker. Their demo tier allows 50 searches an hour across the whole instance, shared by every writer on it.
The key is write-only: the panel reports whether one is set and never reads it back to the browser. To change it, enter the new one; to turn Unsplash off, remove it — Openverse keeps working either way.
Analytics and discoverability
The admin settings also cover instance-level analytics behaviour and everything search engines see. What is and is not measured about readers is documented in full in Writer dashboard.
Discoverability holds the search-engine indexing switch, the sitemap,
robots.txt, per-console verification tokens, and IndexNow — see
Discoverability.
Everything is reversible
No admin setting requires a reinstall to undo. The ones that need a restart say so; the rest apply immediately.
Found a mistake?Edit this page on GitHub.