nuke/docs/docs/design/how-anubis-works.mdx
Xe Iaso 7c80c23e90
docs: remove JSON examples from policy file docs (#945)
* docs: remove JSON examples from policy file docs

Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(lib): remove mentions of botPolicies.json in the tests

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs: update link to challenge methods

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs: unbreak links to the challenges category

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-08-03 18:09:26 +00:00

107 lines
4 KiB
Text

---
title: How Anubis works
---
Anubis uses a proof-of-work challenge to ensure that clients are using a modern browser and are able to calculate SHA-256 checksums. Anubis has a customizable difficulty for this proof-of-work challenge, but defaults to 5 leading zeroes.
```mermaid
---
title: Challenge generation and validation
---
flowchart TD
Backend("Backend")
Fail("Fail")
style PresentChallenge color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF
style ValidateChallenge color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF
style Backend color:#FFFFFF, stroke:#00C853, fill:#00C853
style Fail color:#FFFFFF, stroke:#FF2962, fill:#FF2962
subgraph Server
PresentChallenge("Present Challenge")
ValidateChallenge("Validate Challenge")
end
subgraph Client
Main("main.mjs")
Worker("Worker")
end
Main -- Request challenge --> PresentChallenge
PresentChallenge -- Return challenge & difficulty --> Main
Main -- Spawn worker --> Worker
Worker -- Successful challenge --> Main
Main -- Validate challenge --> ValidateChallenge
ValidateChallenge -- Return cookie --> Backend
ValidateChallenge -- If anything is wrong --> Fail
```
## Challenge presentation
Anubis decides to present a challenge using this logic:
- User-Agent contains `"Mozilla"`
- Request path is not in `/.well-known`, `/robots.txt`, or `/favicon.ico`
- Request path is not obviously an RSS feed (ends with `.rss`, `.xml`, or `.atom`)
This should ensure that git clients, RSS readers, and other low-harm clients can get through without issue, but high-risk clients such as browsers and AI scraper bots will get blocked.
```mermaid
---
title: Challenge presentation logic
---
flowchart LR
Request("Request")
Backend("Backend")
%%Fail("Fail")
PresentChallenge("Present
challenge")
HasMozilla{"Is browser
or scraper?"}
HasCookie{"Has cookie?"}
HasExpired{"Cookie expired?"}
HasSignature{"Has valid
signature?"}
RandomJitter{"Secondary
screening?"}
POWPass{"Proof of
work valid?"}
style PresentChallenge color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF
style Backend color:#FFFFFF, stroke:#00C853, fill:#00C853
%%style Fail color:#FFFFFF, stroke:#FF2962, fill:#FF2962
Request --> HasMozilla
HasMozilla -- Yes --> HasCookie
HasMozilla -- No --> Backend
HasCookie -- Yes --> HasExpired
HasCookie -- No --> PresentChallenge
HasExpired -- Yes --> PresentChallenge
HasExpired -- No --> HasSignature
HasSignature -- Yes --> RandomJitter
HasSignature -- No --> PresentChallenge
RandomJitter -- Yes --> POWPass
RandomJitter -- No --> Backend
POWPass -- Yes --> Backend
PowPass -- No --> PresentChallenge
PresentChallenge -- Back again for another cycle --> Request
```
## Proof of passing challenges
When a client passes a challenge, Anubis sets an HTTP cookie named `"techaro.lol-anubis-auth"` containing a signed [JWT](https://jwt.io/) (JSON Web Token). This JWT contains the following claims:
- `challenge`: The challenge string derived from user request metadata
- `nonce`: The nonce / iteration number used to generate the passing response
- `response`: The hash that passed Anubis' checks
- `iat`: When the token was issued
- `nbf`: One minute prior to when the token was issued
- `exp`: The token's expiry week after the token was issued
This ensures that the token has enough metadata to prove that the token is valid (due to the token's signature), but also so that the server can independently prove the token is valid. This cookie is allowed to be set without triggering an EU cookie banner notification; but depending on facts and circumstances, you may wish to disclose this to your users.
## JWT signing
Anubis uses an ed25519 keypair to sign the JWTs issued when challenges are passed. Anubis will generate a new ed25519 keypair every time it starts. At this time, there is no way to share this keypair between instance of Anubis, but that will be addressed in future versions.