Compare commits

..

3 Commits

Author SHA1 Message Date
Xe Iaso
f2cde94b71 test(decaymap): fix tests
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 18:46:32 -05:00
Xe Iaso
f56c242167 fix(decaymap): scratch that, once every 15 minutes
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 18:31:38 -05:00
Xe Iaso
a75d9066fd fix(decaymap): serialize use of decaymap, make decaying happen once per minute
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-28 18:30:37 -05:00
271 changed files with 2686 additions and 5342 deletions

View File

@@ -2,7 +2,9 @@
// README at: https://github.com/devcontainers/templates/tree/main/src/debian // README at: https://github.com/devcontainers/templates/tree/main/src/debian
{ {
"name": "Dev", "name": "Dev",
"dockerComposeFile": ["./docker-compose.yaml"], "dockerComposeFile": [
"./docker-compose.yaml"
],
"service": "workspace", "service": "workspace",
"workspaceFolder": "/workspace/anubis", "workspaceFolder": "/workspace/anubis",
"postStartCommand": "bash ./.devcontainer/poststart.sh", "postStartCommand": "bash ./.devcontainer/poststart.sh",

View File

@@ -58,3 +58,4 @@ body:
attributes: attributes:
label: Additional context label: Additional context
description: Add any other context about the problem here. description: Add any other context about the problem here.

View File

@@ -1,6 +1,6 @@
name: Feature request name: Feature request
description: Suggest an idea for this project description: Suggest an idea for this project
title: "[Feature request] " title: '[Feature request] '
body: body:
- type: textarea - type: textarea

View File

@@ -1,12 +1,12 @@
<!-- <!--
delete me and describe your change here, give enough context for a maintainer to understand what and why delete me and describe your change here, give enough context for a maintainer to understand what and why
See https://github.com/TecharoHQ/anubis/blob/main/CONTRIBUTING.md for more information See https://anubis.techaro.lol/docs/developer/code-quality for more information
--> -->
Checklist: Checklist:
- [ ] Added a description of the changes to the `[Unreleased]` section of docs/docs/CHANGELOG.md - [ ] Added a description of the changes to the `[Unreleased]` section of docs/docs/CHANGELOG.md
- [ ] Added test cases to [the relevant parts of the codebase](https://github.com/TecharoHQ/anubis/blob/main/CONTRIBUTING.md) - [ ] Added test cases to [the relevant parts of the codebase](https://anubis.techaro.lol/docs/developer/code-quality)
- [ ] Ran integration tests `npm run test:integration` (unsupported on Windows, please use WSL) - [ ] Ran integration tests `npm run test:integration` (unsupported on Windows, please use WSL)
- [ ] All of my commits have [verified signatures](https://anubis.techaro.lol/docs/developer/signed-commits) - [ ] All of my commits have [verified signatures](https://anubis.techaro.lol/docs/developer/signed-commits)

View File

@@ -1,17 +1,17 @@
# check-spelling/check-spelling configuration # check-spelling/check-spelling configuration
| File | Purpose | Format | Info | File | Purpose | Format | Info
| -------------------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -|-|-|-
| [dictionary.txt](dictionary.txt) | Replacement dictionary (creating this file will override the default dictionary) | one word per line | [dictionary](https://github.com/check-spelling/check-spelling/wiki/Configuration#dictionary) | [dictionary.txt](dictionary.txt) | Replacement dictionary (creating this file will override the default dictionary) | one word per line | [dictionary](https://github.com/check-spelling/check-spelling/wiki/Configuration#dictionary)
| [allow.txt](allow.txt) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow) | [allow.txt](allow.txt) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
| [reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject) | [reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
| [excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes) | [excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
| [only.txt](only.txt) | Only check matching files (applied after excludes) | perl regular expression | [only](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-only) | [only.txt](only.txt) | Only check matching files (applied after excludes) | perl regular expression | [only](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-only)
| [patterns.txt](patterns.txt) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns) | [patterns.txt](patterns.txt) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
| [candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns) | [candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
| [line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns) | [line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
| [expect.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect) | [expect.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
| [advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice) | [advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
Note: you can replace any of these files with a directory by the same name (minus the suffix) Note: you can replace any of these files with a directory by the same name (minus the suffix)
and then include multiple files inside that directory (with that suffix) to merge multiple files together. and then include multiple files inside that directory (with that suffix) to merge multiple files together.

View File

@@ -2,27 +2,30 @@
<details><summary>If the flagged items are :exploding_head: false positives</summary> <details><summary>If the flagged items are :exploding_head: false positives</summary>
If items relate to a ... If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).
- binary file (or some other file you wouldn't want to check at all).
Please add a file path to the `excludes.txt` file matching the containing file. Please add a file path to the `excludes.txt` file matching the containing file.
File paths are Perl 5 Regular Expressions - you can [test](https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files. File paths are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](../tree/HEAD/README.md) (on whichever branch you're using). `^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
../tree/HEAD/README.md) (on whichever branch you're using).
- well-formed pattern. * well-formed pattern.
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it, If you can write a [pattern](
https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
) that would match it,
try adding it to the `patterns.txt` file. try adding it to the `patterns.txt` file.
Patterns are Perl 5 Regular Expressions - you can [test](https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines. Patterns are Perl 5 Regular Expressions - you can [test](
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
Note that patterns can't match multiline strings. Note that patterns can't match multiline strings.
</details> </details>
<!-- adoption information--> <!-- adoption information-->
:steam_locomotive: If you're seeing this message and your PR is from a branch that doesn't have check-spelling, :steam_locomotive: If you're seeing this message and your PR is from a branch that doesn't have check-spelling,
please merge to your PR's base branch to get the version configured for your repository. please merge to your PR's base branch to get the version configured for your repository.

View File

@@ -18,20 +18,3 @@ clampip
pseudoprofound pseudoprofound
reimagining reimagining
iocaine iocaine
admins
fout
iplist
NArg
blocklists
rififi
prolocation
Prolocation
Necron
Stargate
FFXIV
uvensys
de
resourced
envoyproxy
unipromos
Samsung

View File

@@ -87,14 +87,10 @@
^docs/docs/user/known-instances.md$ ^docs/docs/user/known-instances.md$
^docs/manifest/.*$ ^docs/manifest/.*$
^docs/static/\.nojekyll$ ^docs/static/\.nojekyll$
^internal/glob/glob_test.go$
^internal/honeypot/naive/affirmations\.txt$
^internal/honeypot/naive/spintext\.txt$
^internal/honeypot/naive/titles\.txt$
^lib/config/testdata/bad/unparseable\.json$
^lib/localization/.*_test.go$
^lib/localization/locales/.*\.json$
^lib/policy/config/testdata/bad/unparseable\.json$ ^lib/policy/config/testdata/bad/unparseable\.json$
^test/.*$ ^internal/glob/glob_test.go$
ignore$ ignore$
robots.txt robots.txt
^lib/localization/locales/.*\.json$
^lib/localization/.*_test.go$
^test/.*$

View File

@@ -2,12 +2,10 @@ acs
Actorified Actorified
actorifiedstore actorifiedstore
actorify actorify
agentic
Aibrew Aibrew
alibaba alibaba
alrest alrest
amazonbot amazonbot
anexia
anthro anthro
anubis anubis
anubistest anubistest
@@ -63,11 +61,10 @@ checkresult
chibi chibi
cidranger cidranger
ckie ckie
CLAUDE
cloudflare cloudflare
cloudsolutions
Codespaces Codespaces
confd confd
connnection
containerbuild containerbuild
containerregistry containerregistry
coreutils coreutils
@@ -76,11 +73,8 @@ Cromite
crt crt
Cscript Cscript
daemonizing daemonizing
databento
dayjob dayjob
dco
DDOS DDOS
ddwrt
Debian Debian
debrpm debrpm
decaymap decaymap
@@ -103,7 +97,6 @@ duckduckbot
eerror eerror
ellenjoe ellenjoe
emacs emacs
embe
enbyware enbyware
etld etld
everyones everyones
@@ -121,7 +114,6 @@ FCr
fcrdns fcrdns
fediverse fediverse
ffprobe ffprobe
fhdr
financials financials
finfos finfos
Firecrawl Firecrawl
@@ -140,9 +132,7 @@ GHSA
Ghz Ghz
gipc gipc
gitea gitea
GLM
godotenv godotenv
goimports
goland goland
gomod gomod
goodbot goodbot
@@ -159,7 +149,6 @@ grw
gzw gzw
Hashcash Hashcash
hashrate hashrate
hdr
headermap headermap
healthcheck healthcheck
healthz healthz
@@ -169,7 +158,6 @@ Hetzner
hmc hmc
homelab homelab
hostable hostable
HSTS
htmlc htmlc
htmx htmx
httpdebug httpdebug
@@ -222,6 +210,7 @@ LLU
loadbalancer loadbalancer
lol lol
lominsa lominsa
maintainership
malware malware
mcr mcr
memes memes
@@ -243,7 +232,6 @@ nepeat
netsurf netsurf
nginx nginx
nicksnyder nicksnyder
nikandfor
nobots nobots
NONINFRINGEMENT NONINFRINGEMENT
nosleep nosleep
@@ -260,13 +248,11 @@ opengraph
openrc openrc
oswald oswald
pag pag
pagegen
palemoon palemoon
Pangu Pangu
parseable parseable
passthrough passthrough
Patreon Patreon
perplexitybot
pgrep pgrep
phrik phrik
pidfile pidfile
@@ -298,7 +284,6 @@ redirectscheme
refactors refactors
remoteip remoteip
reputational reputational
Rhul
risc risc
ruleset ruleset
runlevels runlevels
@@ -318,7 +303,6 @@ Seo
setsebool setsebool
shellcheck shellcheck
shirou shirou
shoneypot
shopt shopt
Sidetrade Sidetrade
simprint simprint
@@ -327,16 +311,13 @@ sls
sni sni
snipster snipster
Spambot Spambot
spammer
sparkline sparkline
spyderbot spyderbot
srcip
srv srv
stackoverflow stackoverflow
startprecmd startprecmd
stoppostcmd stoppostcmd
storetest storetest
strcmp
subgrid subgrid
subr subr
subrequest subrequest
@@ -361,27 +342,25 @@ Timpibot
TLog TLog
traefik traefik
trunc trunc
txn
uberspace uberspace
Unbreak Unbreak
unbreakdocker unbreakdocker
unifiedjs unifiedjs
unmarshal unmarshal
unparseable unparseable
updown
uvx uvx
UXP UXP
valkey valkey
Varis Varis
Velen Velen
vendored vendored
verify
vhosts vhosts
vkbot vkbot
VKE VKE
vnd vnd
VPS VPS
Vultr Vultr
WAIFU
weblate weblate
webmaster webmaster
webpage webpage
@@ -407,7 +386,6 @@ XNG
XOB XOB
XOriginal XOriginal
XReal XReal
Y'shtola
yae yae
YAMLTo YAMLTo
Yda Yda
@@ -419,4 +397,13 @@ Zenos
zizmor zizmor
zombocom zombocom
zos zos
zst GLM
iocaine
nikandfor
pagegen
pseudoprofound
reimagining
Rhul
shoneypot
spammer
Y'shtola

View File

@@ -13,7 +13,7 @@ jobs:
asset_verification: asset_verification:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
@@ -22,12 +22,12 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential sudo apt-get install -y build-essential
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: '24.11.0'
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "1.25.7" go-version: '1.25.4'
- name: install node deps - name: install node deps
run: | run: |

View File

@@ -1,9 +0,0 @@
name: DCO Check
on: [pull_request]
jobs:
dco_check:
runs-on: ubuntu-latest
steps:
- uses: tisonkun/actions-dco@f1024cd563550b5632e754df11b7d30b73be54a5 # v1.1

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
fetch-tags: true fetch-tags: true
fetch-depth: 0 fetch-depth: 0
@@ -26,18 +26,18 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential sudo apt-get install -y build-essential
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: '24.11.0'
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with: with:
images: ghcr.io/${{ github.repository }} images: ghcr.io/${{ github.repository }}

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
fetch-tags: true fetch-tags: true
fetch-depth: 0 fetch-depth: 0
@@ -36,17 +36,17 @@ jobs:
run: | run: |
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: '24.11.0'
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
- name: Log into registry - name: Log into registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
@@ -54,7 +54,7 @@ jobs:
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with: with:
images: ${{ env.IMAGE }} images: ${{ env.IMAGE }}
@@ -68,7 +68,7 @@ jobs:
SLOG_LEVEL: debug SLOG_LEVEL: debug
- name: Generate artifact attestation - name: Generate artifact attestation
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with: with:
subject-name: ${{ env.IMAGE }} subject-name: ${{ env.IMAGE }}
subject-digest: ${{ steps.build.outputs.digest }} subject-digest: ${{ steps.build.outputs.digest }}

View File

@@ -17,15 +17,15 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Log into registry - name: Log into registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with: with:
registry: ghcr.io registry: ghcr.io
username: techarohq username: techarohq
@@ -33,7 +33,7 @@ jobs:
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with: with:
images: ghcr.io/techarohq/anubis/docs images: ghcr.io/techarohq/anubis/docs
tags: | tags: |
@@ -42,7 +42,7 @@ jobs:
- name: Build and push - name: Build and push
id: build id: build
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
context: ./docs context: ./docs
cache-to: type=gha cache-to: type=gha
@@ -53,14 +53,14 @@ jobs:
push: true push: true
- name: Apply k8s manifests to limsa lominsa - name: Apply k8s manifests to limsa lominsa
uses: actions-hub/kubectl@934aaa4354bbbc3d2176ae8d7cae92d515032dff # v1.35.3 uses: actions-hub/kubectl@f6d776bd78f4523e36d6c74d34f9941c242b2213 # v1.35.0
env: env:
KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }} KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }}
with: with:
args: apply -k docs/manifest args: apply -k docs/manifest
- name: Apply k8s manifests to limsa lominsa - name: Apply k8s manifests to limsa lominsa
uses: actions-hub/kubectl@934aaa4354bbbc3d2176ae8d7cae92d515032dff # v1.35.3 uses: actions-hub/kubectl@f6d776bd78f4523e36d6c74d34f9941c242b2213 # v1.35.0
env: env:
KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }} KUBE_CONFIG: ${{ secrets.LIMSA_LOMINSA_KUBECONFIG }}
with: with:

View File

@@ -13,16 +13,16 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with: with:
images: ghcr.io/techarohq/anubis/docs images: ghcr.io/techarohq/anubis/docs
tags: | tags: |
@@ -31,7 +31,7 @@ jobs:
- name: Build and push - name: Build and push
id: build id: build
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
context: ./docs context: ./docs
cache-to: type=gha cache-to: type=gha

View File

@@ -13,13 +13,13 @@ jobs:
go_mod_tidy_check: go_mod_tidy_check:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- name: Check go.mod and go.sum in main directory - name: Check go.mod and go.sum in main directory
run: | run: |

View File

@@ -15,7 +15,7 @@ jobs:
#runs-on: alrest-techarohq #runs-on: alrest-techarohq
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
@@ -24,15 +24,15 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential sudo apt-get install -y build-essential
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: '24.11.0'
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- name: Cache playwright binaries - name: Cache playwright binaries
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
id: playwright-cache id: playwright-cache
with: with:
path: | path: |
@@ -55,10 +55,10 @@ jobs:
run: npm run test run: npm run test
- name: Lint with staticcheck - name: Lint with staticcheck
uses: dominikh/staticcheck-action@9716614d4101e79b4340dd97b10e54d68234e431 # v1.4.1 uses: dominikh/staticcheck-action@024238d2898c874f26d723e7d0ff4308c35589a2 # v1.4.0
with: with:
version: "latest" version: "latest"
- name: Govulncheck - name: Govulncheck
run: | run: |
go tool govulncheck ./... ||: go tool govulncheck ./...

View File

@@ -1,19 +0,0 @@
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
lint_pr_title:
name: Validate PR title
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -14,7 +14,7 @@ jobs:
#runs-on: alrest-techarohq #runs-on: alrest-techarohq
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
fetch-tags: true fetch-tags: true
@@ -25,12 +25,12 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential sudo apt-get install -y build-essential
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: '24.11.0'
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- name: install node deps - name: install node deps
run: | run: |

View File

@@ -15,7 +15,7 @@ jobs:
#runs-on: alrest-techarohq #runs-on: alrest-techarohq
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
fetch-tags: true fetch-tags: true
@@ -26,12 +26,12 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential sudo apt-get install -y build-essential
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: '24.11.0'
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- name: install node deps - name: install node deps
run: | run: |
@@ -41,7 +41,7 @@ jobs:
run: | run: |
go tool yeet go tool yeet
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with: with:
name: packages name: packages
path: var/* path: var/*

View File

@@ -30,16 +30,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with: with:
node-version: "24.11.0" node-version: "24.11.0"
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: "1.25.4"
- uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9
@@ -57,7 +57,7 @@ jobs:
run: echo "ARTIFACT_NAME=${{ matrix.test }}" | sed 's|/|-|g' >> $GITHUB_ENV run: echo "ARTIFACT_NAME=${{ matrix.test }}" | sed 's|/|-|g' >> $GITHUB_ENV
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
if: always() if: always()
with: with:
name: ${{ env.ARTIFACT_NAME }} name: ${{ env.ARTIFACT_NAME }}

View File

@@ -59,16 +59,16 @@ name: Check Spelling
on: on:
push: push:
branches: branches:
- "**" - '**'
tags-ignore: tags-ignore:
- "**" - '**'
pull_request: pull_request:
branches: branches:
- "**" - '**'
types: types:
- "opened" - 'opened'
- "reopened" - 'reopened'
- "synchronize" - 'synchronize'
jobs: jobs:
spelling: spelling:

View File

@@ -18,19 +18,19 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
fetch-tags: true fetch-tags: true
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Log into registry - name: Log into registry
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Build and push - name: Build and push
run: | run: |
cd ./test/ssh-ci cd ./test/ssh-ci

View File

@@ -12,33 +12,32 @@ permissions:
jobs: jobs:
ssh: ssh:
if: github.repository == 'TecharoHQ/anubis' if: github.repository == 'TecharoHQ/anubis'
#runs-on: alrest-techarohq runs-on: alrest-techarohq
runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
host: host:
- riscv64 - riscv64
- ppc64le - ppc64le
#- aarch64-4k - aarch64-4k
#- aarch64-16k - aarch64-16k
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
fetch-tags: true fetch-tags: true
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Install CI target SSH key - name: Install CI target SSH key
uses: shimataro/ssh-key-action@6b84f2e793b32fa0b03a379cadadec75cc539391 # v2.8.0 uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4 # v2.7.0
with: with:
key: ${{ secrets.CI_SSH_KEY }} key: ${{ secrets.CI_SSH_KEY }}
name: id_rsa name: id_rsa
known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }} known_hosts: ${{ secrets.CI_SSH_KNOWN_HOSTS }}
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with: with:
go-version: "stable" go-version: '1.25.4'
- name: Run CI - name: Run CI
run: go run ./utils/cmd/backoff-retry bash test/ssh-ci/rigging.sh ${{ matrix.host }} run: go run ./utils/cmd/backoff-retry bash test/ssh-ci/rigging.sh ${{ matrix.host }}

View File

@@ -1,12 +1,12 @@
name: zizmor name: zizmor
on: on:
push: push:
paths: paths:
- ".github/workflows/*.ya?ml" - '.github/workflows/*.ya?ml'
pull_request: pull_request:
paths: paths:
- ".github/workflows/*.ya?ml" - '.github/workflows/*.ya?ml'
jobs: jobs:
zizmor: zizmor:
@@ -16,12 +16,12 @@ jobs:
security-events: write security-events: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with: with:
persist-credentials: false persist-credentials: false
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0 uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
- name: Run zizmor 🌈 - name: Run zizmor 🌈
run: uvx zizmor --format sarif . > results.sarif run: uvx zizmor --format sarif . > results.sarif
@@ -29,7 +29,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file - name: Upload SARIF file
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
with: with:
sarif_file: results.sarif sarif_file: results.sarif
category: zizmor category: zizmor

View File

@@ -1,8 +0,0 @@
npx --no-install commitlint --edit "$1"
# Check if commit message contains Signed-off-by line
if ! grep -q "^Signed-off-by:" "$1"; then
echo "Commit message must contain a 'Signed-off-by:' line."
echo "Please use 'git commit --signoff' or add a Signed-off-by line to your commit message."
exit 1
fi

View File

@@ -1,2 +0,0 @@
npm run lint
npm run test

View File

@@ -1,13 +1,13 @@
defaultBaseImage: cgr.dev/chainguard/static defaultBaseImage: cgr.dev/chainguard/static
defaultPlatforms: defaultPlatforms:
- linux/arm64 - linux/arm64
- linux/amd64 - linux/amd64
- linux/arm/v7 - linux/arm/v7
builds: builds:
- id: anubis - id: anubis
main: ./cmd/anubis main: ./cmd/anubis
ldflags: ldflags:
- -s -w - -s -w
- -extldflags "-static" - -extldflags "-static"
- -X github.com/TecharoHQ/anubis.Version={{.Env.VERSION}} - -X github.com/TecharoHQ/anubis.Version={{.Env.VERSION}}

View File

@@ -1,4 +0,0 @@
lib/config/testdata/bad/*
*.inc
AGENTS.md
CLAUDE.md

View File

@@ -1,75 +0,0 @@
# Agent instructions
Primary agent documentation is in `CONTRIBUTING.md`. You MUST read this file before proceeding.
## Useful Commands
```shell
npm ci # install node dependencies
npm run assets # build JS/CSS (required before any Go build/test)
npm run build # assets + go build -> ./var/anubis
npm run dev # assets + run locally with --use-remote-address
```
## Testing
```shell
npm run test
```
## Linting
```shell
go vet ./...
go tool staticcheck ./...
go tool govulncheck ./...
```
## Commit Messages
Commit messages follow the [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/) format:
```text
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
- Add `!` after type/scope for breaking changes or include `BREAKING CHANGE:` in the footer.
- Keep descriptions concise, imperative, lowercase, and without a trailing period.
- Reference issues/PRs in the footer when applicable.
- **ALL git commits MUST be made with `--signoff`.** This is mandatory.
### Attribution Requirements
AI agents must disclose what tool and model they are using in the "Assisted-by" commit footer:
```text
Assisted-by: [Model Name] via [Tool Name]
```
Example:
```text
Assisted-by: GLM 4.6 via Claude Code
```
## PR Checklist
- Add description of changes to `[Unreleased]` in `docs/docs/CHANGELOG.md`.
- Add test cases for bug fixes and behavior changes.
- Run integration tests: `npm run test:integration`.
- All commits must have verified (signed) signatures.
## Key Conventions
- **Security-first**: This is security software. Code reviews are strict. Always add tests for bug fixes. Consider adversarial inputs.
- **Configuration**: YAML-based policy files. Config structs validate via `Valid() error` methods returning sentinel errors.
- **Store interface**: `lib/store.Interface` abstracts key-value storage.
- **Environment variables**: Parsed from flags via `flagenv`. Use `.env` files locally (loaded by `godotenv/autoload`). Never commit `.env` files.
- **Assets must be built first**: JS/CSS assets are embedded into the Go binary. Always run `npm run assets` before `go test` or `go build`.
- **CEL expressions**: Policy rules support CEL (Common Expression Language) expressions for advanced matching. See `lib/policy/expressions/`.

View File

@@ -1,2 +0,0 @@
@AGENTS.md
@CONTRIBUTING.md

View File

@@ -1,144 +0,0 @@
# Contributing to Anubis
Anubis is a Web AI Firewall Utility (WAIFU) written in Go. It uses sha256 proof-of-work challenges to protect upstream HTTP resources from scraper bots. This is security software -- correctness matters.
## Build & Run
Prerequisites: Go 1.24+, Node.js (any supported version), esbuild, gzip, zstd, brotli. Install all with `brew bundle` if you are using Homebrew.
```shell
npm ci # install node dependencies
npm run assets # build JS/CSS (required before any Go build/test)
npm run build # assets + go build -> ./var/anubis
npm run dev # assets + run locally with --use-remote-address
```
## Testing
```shell
# Run all unit tests (assets must be built first)
npm run test # or: make test
# Run a single test by name
go test -run TestClampIP ./internal/
# Run a single test file's package
go test ./lib/config/
# Run tests with verbose output
go test -v -run TestBotValid ./lib/config/
```
### Smoke tests
The `tests` folder contains "smoke tests" that are intended to set up Anubis in production-adjacent settings and testing it against real infrastructure tools. A smoke test is a folder with `test.sh` that sets up infrastructure, validates the behaviour, and then tears it down. Smoke tests are run in GitHub actions with `.github/workflows/smoke-tests.yaml`.
## Linting
```shell
go vet ./...
go tool staticcheck ./...
go tool govulncheck ./...
```
## Code Generation
The project uses `go generate` for templ templates and stringer. Always run `npm run generate` (or `make assets`) before building or testing. Generated files include:
- `web/*.templ` -> templ-generated Go code
- `web/static/` -> bundled/minified JS and CSS (with .gz, .zst, .br variants)
## Project Layout
Important folders:
- `cmd/anubis`: Main entrypoint for the project. This is the program that runs on servers.
- `lib/*`: The core library for Anubis and all of its features. This is internal code that is made public for ease of downstream consumption. No API stability is guaranteed. Use at your own risk.
- `internal/*`: Actual internal code that is private to the implementation of Anubis. If you need to use a package in this, please copy it out and manually vendor it in your own project.
- `test/*` Smoke tests (see dedicated section for details).
- `web`: Frontend HTML templates.
- `xess`: Frontend CSS framework and build logic.
## Code Style
### Go
This project follows the idioms of the Go standard library. Generally follow the patterns that upstream Go uses, including:
- Prefer packages from the standard library unless there is no other option.
- Use package import aliases only when package names collide.
- Use `goimports` to format code. Run with `npm run format`.
- Use sentinel errors as package-level variables prefixed with `Err` (such as `ErrBotMustHaveName`). Wrap with `fmt.Errorf("package: small message giving context: %w", err)`.
- Use `log/slog` for structured logging. Pass loggers as arguments to functions. Use `lg.With` to preload with context. Prefer using `slog.Debug` unless you absolutely need to report messages to users, some users have magical thinking about log verbosity.
- Name PublicFunctionsAndTypes in PascalCase. Name privateFunctionsAndTypes in camelCase.
- Acronyms stay uppercase (`URL`, `HTTP`, `IP`, `DNS`, etc.)
- Enumerations should use strong types with validation logic for parsing remote input.
- Be conservative in what you send but liberal in what you accept.
- Anything reading configuration values should use both `json` and `yaml` struct tags. Use pointer values for optional configuration values.
- Use [table-driven tests](https://go.dev/wiki/TableDrivenTests) when writing test code.
- Use [`t.Helper()`](https://pkg.go.dev/testing#T.Helper) in helper code (setup/teardown scaffolding).
- Use [`t.Cleanup()`](https://pkg.go.dev/testing#T.Cleanup) to tear down per-test or per-suite scaffolding.
- Use [`errors.Is`](https://pkg.go.dev/errors#Is) for validating function results against sentinel errors.
- Prefer same-package tests over black-box tests (`_test` packages).
### JavaScript / TypeScript
- Source lives in `web/js/`. Built with esbuild, bundled and minified.
- Uses Preact (not React).
- No linter config. Keep functions small. Use `const` by default.
### Templ Templates
Anubis uses [Templ](https://templ.guide) for generating HTML on the server.
- `.templ` files in `web/` generate Go code. Run `go generate ./...` (or `npm run assets`) after modifying them.
- Templates receive typed Go parameters. Keep logic in Go, not templates.
## Commit Messages
Commit messages follow the [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/) format:
```text
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
**Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
- Add `!` after type/scope for breaking changes or include `BREAKING CHANGE:` in the footer.
- Keep descriptions concise, imperative, lowercase, and without a trailing period.
- Reference issues/PRs in the footer when applicable.
- **ALL git commits MUST be made with `--signoff`.** This is mandatory.
### Attribution Requirements
AI agents must disclose what tool and model they are using in the "Assisted-by" commit footer:
```text
Assisted-by: [Model Name] via [Tool Name]
```
Example:
```text
Assisted-by: GLM 4.6 via Claude Code
```
## PR Checklist
- Add description of changes to `[Unreleased]` in `docs/docs/CHANGELOG.md`.
- Add test cases for bug fixes and behavior changes.
- Run integration tests: `npm run test:integration`.
- All commits must have verified (signed) signatures.
## Key Conventions
- **Security-first**: This is security software. Code reviews are strict. Always add tests for bug fixes. Consider adversarial inputs.
- **Configuration**: YAML-based policy files. Config structs validate via `Valid() error` methods returning sentinel errors.
- **Store interface**: `lib/store.Interface` abstracts key-value storage.
- **Environment variables**: Parsed from flags via `flagenv`. Use `.env` files locally (loaded by `godotenv/autoload`). Never commit `.env` files.
- **Assets must be built first**: JS/CSS assets are embedded into the Go binary. Always run `npm run assets` before `go test` or `go build`.
- **CEL expressions**: Policy rules support CEL (Common Expression Language) expressions for advanced matching. See `lib/policy/expressions/`.

View File

@@ -24,6 +24,7 @@ build: assets
lint: assets lint: assets
$(GO) vet ./... $(GO) vet ./...
$(GO) tool staticcheck ./... $(GO) tool staticcheck ./...
$(GO) tool govulncheck ./...
prebaked-build: prebaked-build:
$(GO) build -o ./var/anubis -ldflags "-X 'github.com/TecharoHQ/anubis.Version=$(VERSION)'" ./cmd/anubis $(GO) build -o ./var/anubis -ldflags "-X 'github.com/TecharoHQ/anubis.Version=$(VERSION)'" ./cmd/anubis

View File

@@ -20,27 +20,12 @@ Anubis is brought to you by sponsors and donors like:
<a href="https://www.raptorcs.com/content/base/products.html"> <a href="https://www.raptorcs.com/content/base/products.html">
<img src="./docs/static/img/sponsors/raptor-computing-logo.webp" alt="Raptor Computing Systems" height=64 /> <img src="./docs/static/img/sponsors/raptor-computing-logo.webp" alt="Raptor Computing Systems" height=64 />
</a> </a>
<a href="https://databento.com/?utm_source=anubis&utm_medium=sponsor&utm_campaign=anubis">
<img src="./docs/static/img/sponsors/databento-logo.webp" alt="Databento" height="64" />
</a>
### Gold Tier ### Gold Tier
<a href="https://www.unipromos.com/?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/unipromos.webp" alt="Unipromos" height="64" />
</a>
<a href="https://uvensys.de/?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/uvensys.webp" alt="Uvensys" height="64">
</a>
<a href="https://distrust.co?utm_campaign=github&utm_medium=referral&utm_content=anubis"> <a href="https://distrust.co?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/distrust-logo.webp" alt="Distrust" height="64"> <img src="./docs/static/img/sponsors/distrust-logo.webp" alt="Distrust" height="64">
</a> </a>
<a href="https://about.gitea.com?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/gitea-logo.webp" alt="Gitea" height="64">
</a>
<a href="https://prolocation.net?utm_campaign=github&utm_medium=referral&utm_content=anubis">
<img src="./docs/static/img/sponsors/prolocation-logo.svg" alt="Prolocation" height="64">
</a>
<a href="https://terminaltrove.com/?utm_campaign=github&utm_medium=referral&utm_content=anubis&utm_source=abgh"> <a href="https://terminaltrove.com/?utm_campaign=github&utm_medium=referral&utm_content=anubis&utm_source=abgh">
<img src="./docs/static/img/sponsors/terminal-trove.webp" alt="Terminal Trove" height="64"> <img src="./docs/static/img/sponsors/terminal-trove.webp" alt="Terminal Trove" height="64">
</a> </a>
@@ -70,12 +55,6 @@ Anubis is brought to you by sponsors and donors like:
height="64" height="64"
/> />
</a> </a>
<a href="https://www.anexia.com/">
<img src="./docs/static/img/sponsors/anexia-cloudsolutions-logo.webp" alt="ANEXIA Cloud Solutions" height="64">
</a>
<a href="https://dd-wrt.com/">
<img src="./docs/static/img/sponsors/ddwrt-logo.webp" alt="embeDD GmbH" height="64">
</a>
## Overview ## Overview

View File

@@ -1 +1 @@
1.25.0 1.24.0

View File

@@ -17,7 +17,6 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/http/pprof"
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
@@ -419,8 +418,8 @@ func main() {
var redirectDomainsList []string var redirectDomainsList []string
if *redirectDomains != "" { if *redirectDomains != "" {
domains := strings.SplitSeq(*redirectDomains, ",") domains := strings.Split(*redirectDomains, ",")
for domain := range domains { for _, domain := range domains {
_, err = url.Parse(domain) _, err = url.Parse(domain)
if err != nil { if err != nil {
log.Fatalf("cannot parse redirect-domain %q: %s", domain, err.Error()) log.Fatalf("cannot parse redirect-domain %q: %s", domain, err.Error())
@@ -428,7 +427,7 @@ func main() {
redirectDomainsList = append(redirectDomainsList, strings.TrimSpace(domain)) redirectDomainsList = append(redirectDomainsList, strings.TrimSpace(domain))
} }
} else { } else {
lg.Warn("REDIRECT_DOMAINS is not set, Anubis will redirect to any domain, see https://anubis.techaro.lol/docs/admin/configuration/redirect-domains") lg.Warn("REDIRECT_DOMAINS is not set, Anubis will only redirect to the same domain a request is coming from, see https://anubis.techaro.lol/docs/admin/configuration/redirect-domains")
} }
anubis.CookieName = *cookiePrefix + "-auth" anubis.CookieName = *cookiePrefix + "-auth"
@@ -523,11 +522,6 @@ func metricsServer(ctx context.Context, lg slog.Logger, done func()) {
defer done() defer done()
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("GET /debug/pprof/", pprof.Index)
mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("GET /debug/pprof/profile", pprof.Profile)
mux.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace)
mux.Handle("/metrics", promhttp.Handler()) mux.Handle("/metrics", promhttp.Handler())
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
st, ok := internal.GetHealth("anubis") st, ok := internal.GetHealth("anubis")

View File

@@ -159,8 +159,5 @@ func run(command string) (string, error) {
} }
func setOutput(key, val string) { func setOutput(key, val string) {
github_output := os.Getenv("GITHUB_OUTPUT") fmt.Printf("::set-output name=%s::%s\n", key, val)
f, _ := os.OpenFile(github_output, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
fmt.Fprintf(f, "%s=%s\n", key, val)
f.Close()
} }

View File

@@ -10,7 +10,6 @@ import (
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
"slices"
"strings" "strings"
"github.com/TecharoHQ/anubis/lib/config" "github.com/TecharoHQ/anubis/lib/config"
@@ -211,8 +210,11 @@ func parseRobotsTxt(input io.Reader) ([]RobotsRule, error) {
// Mark blacklisted user agents (those with "Disallow: /") // Mark blacklisted user agents (those with "Disallow: /")
for i := range rules { for i := range rules {
if slices.Contains(rules[i].Disallows, "/") { for _, disallow := range rules[i].Disallows {
rules[i].IsBlacklist = true if disallow == "/" {
rules[i].IsBlacklist = true
break
}
} }
} }

View File

@@ -158,8 +158,8 @@ func TestDataFileConversion(t *testing.T) {
} }
if strings.ToLower(*outputFormat) == "yaml" { if strings.ToLower(*outputFormat) == "yaml" {
var actualData []any var actualData []interface{}
var expectedData []any var expectedData []interface{}
err = yaml.Unmarshal(actualOutput, &actualData) err = yaml.Unmarshal(actualOutput, &actualData)
if err != nil { if err != nil {
@@ -178,8 +178,8 @@ func TestDataFileConversion(t *testing.T) {
t.Errorf("Output mismatch for %s\nExpected:\n%s\n\nActual:\n%s", tc.name, expectedStr, actualStr) t.Errorf("Output mismatch for %s\nExpected:\n%s\n\nActual:\n%s", tc.name, expectedStr, actualStr)
} }
} else { } else {
var actualData []any var actualData []interface{}
var expectedData []any var expectedData []interface{}
err = json.Unmarshal(actualOutput, &actualData) err = json.Unmarshal(actualOutput, &actualData)
if err != nil { if err != nil {
@@ -419,6 +419,6 @@ Disallow: /`
// compareData performs a deep comparison of two data structures, // compareData performs a deep comparison of two data structures,
// ignoring differences that are semantically equivalent in YAML/JSON // ignoring differences that are semantically equivalent in YAML/JSON
func compareData(actual, expected any) bool { func compareData(actual, expected interface{}) bool {
return reflect.DeepEqual(actual, expected) return reflect.DeepEqual(actual, expected)
} }

View File

@@ -25,6 +25,6 @@
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("Googlebot") - userAgent.contains("Googlebot")
- path.startsWith("/search") - path.startsWith("/search")
name: robots-txt-policy-disallow-7 name: robots-txt-policy-disallow-7

View File

@@ -20,8 +20,8 @@
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("Googlebot") - userAgent.contains("Googlebot")
- path.startsWith("/search/") - path.startsWith("/search/")
name: robots-txt-policy-disallow-6 name: robots-txt-policy-disallow-6
- action: WEIGH - action: WEIGH
expression: userAgent.contains("Bingbot") expression: userAgent.contains("Bingbot")
@@ -31,14 +31,14 @@
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("Bingbot") - userAgent.contains("Bingbot")
- path.startsWith("/search/") - path.startsWith("/search/")
name: robots-txt-policy-disallow-8 name: robots-txt-policy-disallow-8
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("Bingbot") - userAgent.contains("Bingbot")
- path.startsWith("/admin/") - path.startsWith("/admin/")
name: robots-txt-policy-disallow-9 name: robots-txt-policy-disallow-9
- action: DENY - action: DENY
expression: userAgent.contains("BadBot") expression: userAgent.contains("BadBot")
@@ -54,18 +54,18 @@
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("TestBot") - userAgent.contains("TestBot")
- path.matches("^/.*/admin") - path.matches("^/.*/admin")
name: robots-txt-policy-disallow-13 name: robots-txt-policy-disallow-13
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("TestBot") - userAgent.contains("TestBot")
- path.matches("^/temp.*\\.html") - path.matches("^/temp.*\\.html")
name: robots-txt-policy-disallow-14 name: robots-txt-policy-disallow-14
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("TestBot") - userAgent.contains("TestBot")
- path.matches("^/file.\\.log") - path.matches("^/file.\\.log")
name: robots-txt-policy-disallow-15 name: robots-txt-policy-disallow-15

View File

@@ -9,39 +9,39 @@
- action: DENY - action: DENY
expression: expression:
any: any:
- userAgent.contains("BadBot") - userAgent.contains("BadBot")
- userAgent.contains("SpamBot") - userAgent.contains("SpamBot")
- userAgent.contains("EvilBot") - userAgent.contains("EvilBot")
name: robots-txt-policy-blacklist-3 name: robots-txt-policy-blacklist-3
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("GoodBot") - userAgent.contains("GoodBot")
- path.startsWith("/private") - path.startsWith("/private")
name: robots-txt-policy-disallow-4 name: robots-txt-policy-disallow-4
- action: WEIGH - action: WEIGH
expression: expression:
any: any:
- userAgent.contains("SlowBot1") - userAgent.contains("SlowBot1")
- userAgent.contains("SlowBot2") - userAgent.contains("SlowBot2")
name: robots-txt-policy-crawl-delay-5 name: robots-txt-policy-crawl-delay-5
weight: weight:
adjust: 3 adjust: 3
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("SearchBot1") - userAgent.contains("SearchBot1")
- path.startsWith("/search") - path.startsWith("/search")
name: robots-txt-policy-disallow-7 name: robots-txt-policy-disallow-7
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("SearchBot2") - userAgent.contains("SearchBot2")
- path.startsWith("/search") - path.startsWith("/search")
name: robots-txt-policy-disallow-8 name: robots-txt-policy-disallow-8
- action: CHALLENGE - action: CHALLENGE
expression: expression:
all: all:
- userAgent.contains("SearchBot3") - userAgent.contains("SearchBot3")
- path.startsWith("/search") - path.startsWith("/search")
name: robots-txt-policy-disallow-9 name: robots-txt-policy-disallow-9

View File

@@ -2,5 +2,5 @@
action: ALLOW action: ALLOW
expression: expression:
all: all:
- '!(method == "HEAD" || method == "GET")' - '!(method == "HEAD" || method == "GET")'
- path.startsWith("/api/") - path.startsWith("/api/")

View File

@@ -3,6 +3,5 @@
- name: qualys-ssl-labs - name: qualys-ssl-labs
action: ALLOW action: ALLOW
remote_addresses: remote_addresses:
- 69.67.183.0/24 - 64.41.200.0/24
- 2600:C02:1020:4202::/64 - 2600:C02:1020:4202::/64
- 2602:fdaa:c6:2::/64

View File

@@ -5,5 +5,5 @@
- name: searx-checker - name: searx-checker
action: ALLOW action: ALLOW
remote_addresses: remote_addresses:
- 167.235.158.251/32 - 167.235.158.251/32
- 2a01:4f8:1c1c:8fc2::1/128 - 2a01:4f8:1c1c:8fc2::1/128

View File

@@ -3,7 +3,7 @@
action: ALLOW action: ALLOW
expression: expression:
all: all:
- remoteAddress == "159.69.213.214" || remoteAddress == "2a01:4f8:c2c:7bf4::1" - remoteAddress == "159.69.213.214" || remoteAddress == "2a01:4f8:c2c:7bf4::1"
- userAgent == "Mozilla/5.0 (compatible; utils.web Limnoria module)" - userAgent == "Mozilla/5.0 (compatible; utils.web Limnoria module)"
- '"X-Http-Version" in headers' - '"X-Http-Version" in headers'
- headers["X-Http-Version"] == "HTTP/1.1" - headers["X-Http-Version"] == "HTTP/1.1"

View File

@@ -3,7 +3,7 @@
action: ALLOW action: ALLOW
expression: expression:
all: all:
- remoteAddress == "45.76.166.57" - remoteAddress == "45.76.166.57"
- userAgent == "Mozilla/5.0 (Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0" - userAgent == "Mozilla/5.0 (Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0"
- '"X-Http-Version" in headers' - '"X-Http-Version" in headers'
- headers["X-Http-Version"] == "HTTP/1.1" - headers["X-Http-Version"] == "HTTP/1.1"

View File

@@ -4,5 +4,5 @@
# - Claude-User: No published IP allowlist # - Claude-User: No published IP allowlist
- name: "ai-clients" - name: "ai-clients"
user_agent_regex: >- user_agent_regex: >-
ChatGPT-User|Claude-User|MistralAI-User|Perplexity-User ChatGPT-User|Claude-User|MistralAI-User
action: DENY action: DENY

View File

@@ -2,6 +2,6 @@
action: ALLOW action: ALLOW
expression: expression:
all: all:
- userAgent.startsWith("Go-http-client/") - userAgent.startsWith("Go-http-client/")
- '"go-get" in query' - '"go-get" in query'
- query["go-get"] == "1" - query["go-get"] == "1"

View File

@@ -4,4 +4,7 @@
user_agent_regex: MistralAI-User/.+; \+https\://docs\.mistral\.ai/robots user_agent_regex: MistralAI-User/.+; \+https\://docs\.mistral\.ai/robots
action: ALLOW action: ALLOW
# https://mistral.ai/mistralai-user-ips.json # https://mistral.ai/mistralai-user-ips.json
remote_addresses: ["20.240.160.161/32", "20.240.160.1/32"] remote_addresses: [
"20.240.160.161/32",
"20.240.160.1/32",
]

View File

@@ -5,90 +5,89 @@
action: ALLOW action: ALLOW
# https://openai.com/chatgpt-user.json # https://openai.com/chatgpt-user.json
# curl 'https://openai.com/chatgpt-user.json' | jq '.prefixes.[].ipv4Prefix' | sed 's/$/,/' # curl 'https://openai.com/chatgpt-user.json' | jq '.prefixes.[].ipv4Prefix' | sed 's/$/,/'
remote_addresses: remote_addresses: [
[ "13.65.138.112/28",
"13.65.138.112/28", "23.98.179.16/28",
"23.98.179.16/28", "13.65.138.96/28",
"13.65.138.96/28", "172.183.222.128/28",
"172.183.222.128/28", "20.102.212.144/28",
"20.102.212.144/28", "40.116.73.208/28",
"40.116.73.208/28", "172.183.143.224/28",
"172.183.143.224/28", "52.190.190.16/28",
"52.190.190.16/28", "13.83.237.176/28",
"13.83.237.176/28", "51.8.155.64/28",
"51.8.155.64/28", "74.249.86.176/28",
"74.249.86.176/28", "51.8.155.48/28",
"51.8.155.48/28", "20.55.229.144/28",
"20.55.229.144/28", "135.237.131.208/28",
"135.237.131.208/28", "135.237.133.48/28",
"135.237.133.48/28", "51.8.155.112/28",
"51.8.155.112/28", "135.237.133.112/28",
"135.237.133.112/28", "52.159.249.96/28",
"52.159.249.96/28", "52.190.137.16/28",
"52.190.137.16/28", "52.255.111.112/28",
"52.255.111.112/28", "40.84.181.32/28",
"40.84.181.32/28", "172.178.141.112/28",
"172.178.141.112/28", "52.190.142.64/28",
"52.190.142.64/28", "172.178.140.144/28",
"172.178.140.144/28", "52.190.137.144/28",
"52.190.137.144/28", "172.178.141.128/28",
"172.178.141.128/28", "57.154.187.32/28",
"57.154.187.32/28", "4.196.118.112/28",
"4.196.118.112/28", "20.193.50.32/28",
"20.193.50.32/28", "20.215.188.192/28",
"20.215.188.192/28", "20.215.214.16/28",
"20.215.214.16/28", "4.197.22.112/28",
"4.197.22.112/28", "4.197.115.112/28",
"4.197.115.112/28", "172.213.21.16/28",
"172.213.21.16/28", "172.213.11.144/28",
"172.213.11.144/28", "172.213.12.112/28",
"172.213.12.112/28", "172.213.21.144/28",
"172.213.21.144/28", "20.90.7.144/28",
"20.90.7.144/28", "57.154.175.0/28",
"57.154.175.0/28", "57.154.174.112/28",
"57.154.174.112/28", "52.236.94.144/28",
"52.236.94.144/28", "137.135.191.176/28",
"137.135.191.176/28", "23.98.186.192/28",
"23.98.186.192/28", "23.98.186.96/28",
"23.98.186.96/28", "23.98.186.176/28",
"23.98.186.176/28", "23.98.186.64/28",
"23.98.186.64/28", "68.221.67.192/28",
"68.221.67.192/28", "68.221.67.160/28",
"68.221.67.160/28", "13.83.167.128/28",
"13.83.167.128/28", "20.228.106.176/28",
"20.228.106.176/28", "52.159.227.32/28",
"52.159.227.32/28", "68.220.57.64/28",
"68.220.57.64/28", "172.213.21.112/28",
"172.213.21.112/28", "68.221.67.224/28",
"68.221.67.224/28", "68.221.75.16/28",
"68.221.75.16/28", "20.97.189.96/28",
"20.97.189.96/28", "52.252.113.240/28",
"52.252.113.240/28", "52.230.163.32/28",
"52.230.163.32/28", "172.212.159.64/28",
"172.212.159.64/28", "52.255.111.80/28",
"52.255.111.80/28", "52.255.111.0/28",
"52.255.111.0/28", "4.151.241.240/28",
"4.151.241.240/28", "52.255.111.32/28",
"52.255.111.32/28", "52.255.111.48/28",
"52.255.111.48/28", "52.255.111.16/28",
"52.255.111.16/28", "52.230.164.176/28",
"52.230.164.176/28", "52.176.139.176/28",
"52.176.139.176/28", "52.173.234.16/28",
"52.173.234.16/28", "4.151.71.176/28",
"4.151.71.176/28", "4.151.119.48/28",
"4.151.119.48/28", "52.255.109.112/28",
"52.255.109.112/28", "52.255.109.80/28",
"52.255.109.80/28", "20.161.75.208/28",
"20.161.75.208/28", "68.154.28.96/28",
"68.154.28.96/28", "52.255.109.128/28",
"52.255.109.128/28", "52.225.75.208/28",
"52.225.75.208/28", "52.190.139.48/28",
"52.190.139.48/28", "68.221.67.240/28",
"68.221.67.240/28", "52.156.77.144/28",
"52.156.77.144/28", "52.148.129.32/28",
"52.148.129.32/28", "40.84.221.208/28",
"40.84.221.208/28", "104.210.139.224/28",
"104.210.139.224/28", "40.84.221.224/28",
"40.84.221.224/28", "104.210.139.192/28",
"104.210.139.192/28", ]
]

View File

@@ -1,8 +0,0 @@
# Acts on behalf of user requests
# https://docs.perplexity.ai/guides/bots
- name: perplexity-user
user_agent_regex: Perplexity-User/.+; \+https\://perplexity\.ai/perplexity-user
action: ALLOW
# https://www.perplexity.com/perplexity-user.json
remote_addresses:
["44.208.221.197/32", "34.193.163.52/32", "18.97.21.0/30", "18.97.43.80/29"]

View File

@@ -1,55 +0,0 @@
# Assert behaviour that only genuine browsers display. This ensures that modern Chrome
# or Firefox versions will get through without a challenge.
#
# These rules have been known to be bypassed by some of the worst automated scrapers.
# Use at your own risk.
- name: realistic-browser-catchall
expression:
all:
- '"User-Agent" in headers'
- '( userAgent.contains("Firefox") ) || ( userAgent.contains("Chrome") ) || ( userAgent.contains("Safari") )'
- '"Accept" in headers'
- '"Sec-Fetch-Dest" in headers'
- '"Sec-Fetch-Mode" in headers'
- '"Sec-Fetch-Site" in headers'
- '"Accept-Encoding" in headers'
- '( headers["Accept-Encoding"].contains("zstd") || headers["Accept-Encoding"].contains("br") )'
- '"Accept-Language" in headers'
action: WEIGH
weight:
adjust: -10
# The Upgrade-Insecure-Requests header is typically sent by browsers, but not always
- name: upgrade-insecure-requests
expression: '"Upgrade-Insecure-Requests" in headers'
action: WEIGH
weight:
adjust: -2
# Chrome should behave like Chrome
- name: chrome-is-proper
expression:
all:
- userAgent.contains("Chrome")
- '"Sec-Ch-Ua" in headers'
- 'headers["Sec-Ch-Ua"].contains("Chromium")'
- '"Sec-Ch-Ua-Mobile" in headers'
- '"Sec-Ch-Ua-Platform" in headers'
action: WEIGH
weight:
adjust: -5
- name: should-have-accept
expression: '!("Accept" in headers)'
action: WEIGH
weight:
adjust: 5
# Generic catchall rule
- name: generic-browser
user_agent_regex: >-
Mozilla|Opera
action: WEIGH
weight:
adjust: 10

View File

@@ -2,5 +2,5 @@
action: ALLOW action: ALLOW
expression: expression:
all: all:
- '!(method == "HEAD" || method == "GET")' - '!(method == "HEAD" || method == "GET")'
- path.startsWith("/api/") - path.startsWith("/api/")

View File

@@ -8,5 +8,4 @@
- import: (data)/crawlers/marginalia.yaml - import: (data)/crawlers/marginalia.yaml
- import: (data)/crawlers/mojeekbot.yaml - import: (data)/crawlers/mojeekbot.yaml
- import: (data)/crawlers/commoncrawl.yaml - import: (data)/crawlers/commoncrawl.yaml
- import: (data)/crawlers/wikimedia-citoid.yaml
- import: (data)/crawlers/yandexbot.yaml - import: (data)/crawlers/yandexbot.yaml

View File

@@ -4,5 +4,5 @@
# - Claude-SearchBot: No published IP allowlist # - Claude-SearchBot: No published IP allowlist
- name: "ai-crawlers-search" - name: "ai-crawlers-search"
user_agent_regex: >- user_agent_regex: >-
OAI-SearchBot|Claude-SearchBot|PerplexityBot OAI-SearchBot|Claude-SearchBot
action: DENY action: DENY

View File

@@ -4,18 +4,17 @@
user_agent_regex: Applebot user_agent_regex: Applebot
action: ALLOW action: ALLOW
# https://search.developer.apple.com/applebot.json # https://search.developer.apple.com/applebot.json
remote_addresses: remote_addresses: [
[ "17.241.208.160/27",
"17.241.208.160/27", "17.241.193.160/27",
"17.241.193.160/27", "17.241.200.160/27",
"17.241.200.160/27", "17.22.237.0/24",
"17.22.237.0/24", "17.22.245.0/24",
"17.22.245.0/24", "17.22.253.0/24",
"17.22.253.0/24", "17.241.75.0/24",
"17.241.75.0/24", "17.241.219.0/24",
"17.241.219.0/24", "17.241.227.0/24",
"17.241.227.0/24", "17.246.15.0/24",
"17.246.15.0/24", "17.246.19.0/24",
"17.246.19.0/24", "17.246.23.0/24",
"17.246.23.0/24", ]
]

View File

@@ -2,34 +2,33 @@
user_agent_regex: \+http\://www\.bing\.com/bingbot\.htm user_agent_regex: \+http\://www\.bing\.com/bingbot\.htm
action: ALLOW action: ALLOW
# https://www.bing.com/toolbox/bingbot.json # https://www.bing.com/toolbox/bingbot.json
remote_addresses: remote_addresses: [
[ "157.55.39.0/24",
"157.55.39.0/24", "207.46.13.0/24",
"207.46.13.0/24", "40.77.167.0/24",
"40.77.167.0/24", "13.66.139.0/24",
"13.66.139.0/24", "13.66.144.0/24",
"13.66.144.0/24", "52.167.144.0/24",
"52.167.144.0/24", "13.67.10.16/28",
"13.67.10.16/28", "13.69.66.240/28",
"13.69.66.240/28", "13.71.172.224/28",
"13.71.172.224/28", "139.217.52.0/28",
"139.217.52.0/28", "191.233.204.224/28",
"191.233.204.224/28", "20.36.108.32/28",
"20.36.108.32/28", "20.43.120.16/28",
"20.43.120.16/28", "40.79.131.208/28",
"40.79.131.208/28", "40.79.186.176/28",
"40.79.186.176/28", "52.231.148.0/28",
"52.231.148.0/28", "20.79.107.240/28",
"20.79.107.240/28", "51.105.67.0/28",
"51.105.67.0/28", "20.125.163.80/28",
"20.125.163.80/28", "40.77.188.0/22",
"40.77.188.0/22", "65.55.210.0/24",
"65.55.210.0/24", "199.30.24.0/23",
"199.30.24.0/23", "40.77.202.0/24",
"40.77.202.0/24", "40.77.139.0/25",
"40.77.139.0/25", "20.74.197.0/28",
"20.74.197.0/28", "20.15.133.160/27",
"20.15.133.160/27", "40.77.177.0/24",
"40.77.177.0/24", "40.77.178.0/23"
"40.77.178.0/23", ]
]

View File

@@ -2,275 +2,274 @@
user_agent_regex: DuckDuckBot/1\.1; \(\+http\://duckduckgo\.com/duckduckbot\.html\) user_agent_regex: DuckDuckBot/1\.1; \(\+http\://duckduckgo\.com/duckduckbot\.html\)
action: ALLOW action: ALLOW
# https://duckduckgo.com/duckduckgo-help-pages/results/duckduckbot # https://duckduckgo.com/duckduckgo-help-pages/results/duckduckbot
remote_addresses: remote_addresses: [
[ "57.152.72.128/32",
"57.152.72.128/32", "51.8.253.152/32",
"51.8.253.152/32", "40.80.242.63/32",
"40.80.242.63/32", "20.12.141.99/32",
"20.12.141.99/32", "20.49.136.28/32",
"20.49.136.28/32", "51.116.131.221/32",
"51.116.131.221/32", "51.107.40.209/32",
"51.107.40.209/32", "20.40.133.240/32",
"20.40.133.240/32", "20.50.168.91/32",
"20.50.168.91/32", "51.120.48.122/32",
"51.120.48.122/32", "20.193.45.113/32",
"20.193.45.113/32", "40.76.173.151/32",
"40.76.173.151/32", "40.76.163.7/32",
"40.76.163.7/32", "20.185.79.47/32",
"20.185.79.47/32", "52.142.26.175/32",
"52.142.26.175/32", "20.185.79.15/32",
"20.185.79.15/32", "52.142.24.149/32",
"52.142.24.149/32", "40.76.162.208/32",
"40.76.162.208/32", "40.76.163.23/32",
"40.76.163.23/32", "40.76.162.191/32",
"40.76.162.191/32", "40.76.162.247/32",
"40.76.162.247/32", "40.88.21.235/32",
"40.88.21.235/32", "20.191.45.212/32",
"20.191.45.212/32", "52.146.59.12/32",
"52.146.59.12/32", "52.146.59.156/32",
"52.146.59.156/32", "52.146.59.154/32",
"52.146.59.154/32", "52.146.58.236/32",
"52.146.58.236/32", "20.62.224.44/32",
"20.62.224.44/32", "51.104.180.53/32",
"51.104.180.53/32", "51.104.180.47/32",
"51.104.180.47/32", "51.104.180.26/32",
"51.104.180.26/32", "51.104.146.225/32",
"51.104.146.225/32", "51.104.146.235/32",
"51.104.146.235/32", "20.73.202.147/32",
"20.73.202.147/32", "20.73.132.240/32",
"20.73.132.240/32", "20.71.12.143/32",
"20.71.12.143/32", "20.56.197.58/32",
"20.56.197.58/32", "20.56.197.63/32",
"20.56.197.63/32", "20.43.150.93/32",
"20.43.150.93/32", "20.43.150.85/32",
"20.43.150.85/32", "20.44.222.1/32",
"20.44.222.1/32", "40.89.243.175/32",
"40.89.243.175/32", "13.89.106.77/32",
"13.89.106.77/32", "52.143.242.6/32",
"52.143.242.6/32", "52.143.241.111/32",
"52.143.241.111/32", "52.154.60.82/32",
"52.154.60.82/32", "20.197.209.11/32",
"20.197.209.11/32", "20.197.209.27/32",
"20.197.209.27/32", "20.226.133.105/32",
"20.226.133.105/32", "191.234.216.4/32",
"191.234.216.4/32", "191.234.216.178/32",
"191.234.216.178/32", "20.53.92.211/32",
"20.53.92.211/32", "20.53.91.2/32",
"20.53.91.2/32", "20.207.99.197/32",
"20.207.99.197/32", "20.207.97.190/32",
"20.207.97.190/32", "40.81.250.205/32",
"40.81.250.205/32", "40.64.106.11/32",
"40.64.106.11/32", "40.64.105.247/32",
"40.64.105.247/32", "20.72.242.93/32",
"20.72.242.93/32", "20.99.255.235/32",
"20.99.255.235/32", "20.113.3.121/32",
"20.113.3.121/32", "52.224.16.221/32",
"52.224.16.221/32", "52.224.21.53/32",
"52.224.21.53/32", "52.224.20.204/32",
"52.224.20.204/32", "52.224.21.19/32",
"52.224.21.19/32", "52.224.20.249/32",
"52.224.20.249/32", "52.224.20.203/32",
"52.224.20.203/32", "52.224.20.190/32",
"52.224.20.190/32", "52.224.16.229/32",
"52.224.16.229/32", "52.224.21.20/32",
"52.224.21.20/32", "52.146.63.80/32",
"52.146.63.80/32", "52.224.20.227/32",
"52.224.20.227/32", "52.224.20.193/32",
"52.224.20.193/32", "52.190.37.160/32",
"52.190.37.160/32", "52.224.21.23/32",
"52.224.21.23/32", "52.224.20.223/32",
"52.224.20.223/32", "52.224.20.181/32",
"52.224.20.181/32", "52.224.21.49/32",
"52.224.21.49/32", "52.224.21.55/32",
"52.224.21.55/32", "52.224.21.61/32",
"52.224.21.61/32", "52.224.19.152/32",
"52.224.19.152/32", "52.224.20.186/32",
"52.224.20.186/32", "52.224.21.27/32",
"52.224.21.27/32", "52.224.21.51/32",
"52.224.21.51/32", "52.224.20.174/32",
"52.224.20.174/32", "52.224.21.4/32",
"52.224.21.4/32", "51.104.164.109/32",
"51.104.164.109/32", "51.104.167.71/32",
"51.104.167.71/32", "51.104.160.177/32",
"51.104.160.177/32", "51.104.162.149/32",
"51.104.162.149/32", "51.104.167.95/32",
"51.104.167.95/32", "51.104.167.54/32",
"51.104.167.54/32", "51.104.166.111/32",
"51.104.166.111/32", "51.104.167.88/32",
"51.104.167.88/32", "51.104.161.32/32",
"51.104.161.32/32", "51.104.163.250/32",
"51.104.163.250/32", "51.104.164.189/32",
"51.104.164.189/32", "51.104.167.19/32",
"51.104.167.19/32", "51.104.160.167/32",
"51.104.160.167/32", "51.104.167.110/32",
"51.104.167.110/32", "20.191.44.119/32",
"20.191.44.119/32", "51.104.167.104/32",
"51.104.167.104/32", "20.191.44.234/32",
"20.191.44.234/32", "51.104.164.215/32",
"51.104.164.215/32", "51.104.167.52/32",
"51.104.167.52/32", "20.191.44.22/32",
"20.191.44.22/32", "51.104.167.87/32",
"51.104.167.87/32", "51.104.167.96/32",
"51.104.167.96/32", "20.191.44.16/32",
"20.191.44.16/32", "51.104.167.61/32",
"51.104.167.61/32", "51.104.164.147/32",
"51.104.164.147/32", "20.50.48.159/32",
"20.50.48.159/32", "40.114.182.172/32",
"40.114.182.172/32", "20.50.50.130/32",
"20.50.50.130/32", "20.50.50.163/32",
"20.50.50.163/32", "20.50.50.46/32",
"20.50.50.46/32", "40.114.182.153/32",
"40.114.182.153/32", "20.50.50.118/32",
"20.50.50.118/32", "20.50.49.55/32",
"20.50.49.55/32", "20.50.49.25/32",
"20.50.49.25/32", "40.114.183.251/32",
"40.114.183.251/32", "20.50.50.123/32",
"20.50.50.123/32", "20.50.49.237/32",
"20.50.49.237/32", "20.50.48.192/32",
"20.50.48.192/32", "20.50.50.134/32",
"20.50.50.134/32", "51.138.90.233/32",
"51.138.90.233/32", "40.114.183.196/32",
"40.114.183.196/32", "20.50.50.146/32",
"20.50.50.146/32", "40.114.183.88/32",
"40.114.183.88/32", "20.50.50.145/32",
"20.50.50.145/32", "20.50.50.121/32",
"20.50.50.121/32", "20.50.49.40/32",
"20.50.49.40/32", "51.138.90.206/32",
"51.138.90.206/32", "40.114.182.45/32",
"40.114.182.45/32", "51.138.90.161/32",
"51.138.90.161/32", "20.50.49.0/32",
"20.50.49.0/32", "40.119.232.215/32",
"40.119.232.215/32", "104.43.55.167/32",
"104.43.55.167/32", "40.119.232.251/32",
"40.119.232.251/32", "40.119.232.50/32",
"40.119.232.50/32", "40.119.232.146/32",
"40.119.232.146/32", "40.119.232.218/32",
"40.119.232.218/32", "104.43.54.127/32",
"104.43.54.127/32", "104.43.55.117/32",
"104.43.55.117/32", "104.43.55.116/32",
"104.43.55.116/32", "104.43.55.166/32",
"104.43.55.166/32", "52.154.169.50/32",
"52.154.169.50/32", "52.154.171.70/32",
"52.154.171.70/32", "52.154.170.229/32",
"52.154.170.229/32", "52.154.170.113/32",
"52.154.170.113/32", "52.154.171.44/32",
"52.154.171.44/32", "52.154.172.2/32",
"52.154.172.2/32", "52.143.244.81/32",
"52.143.244.81/32", "52.154.171.87/32",
"52.154.171.87/32", "52.154.171.250/32",
"52.154.171.250/32", "52.154.170.28/32",
"52.154.170.28/32", "52.154.170.122/32",
"52.154.170.122/32", "52.143.243.117/32",
"52.143.243.117/32", "52.143.247.235/32",
"52.143.247.235/32", "52.154.171.235/32",
"52.154.171.235/32", "52.154.171.196/32",
"52.154.171.196/32", "52.154.171.0/32",
"52.154.171.0/32", "52.154.170.243/32",
"52.154.170.243/32", "52.154.170.26/32",
"52.154.170.26/32", "52.154.169.200/32",
"52.154.169.200/32", "52.154.170.96/32",
"52.154.170.96/32", "52.154.170.88/32",
"52.154.170.88/32", "52.154.171.150/32",
"52.154.171.150/32", "52.154.171.205/32",
"52.154.171.205/32", "52.154.170.117/32",
"52.154.170.117/32", "52.154.170.209/32",
"52.154.170.209/32", "191.235.202.48/32",
"191.235.202.48/32", "191.233.3.202/32",
"191.233.3.202/32", "191.235.201.214/32",
"191.235.201.214/32", "191.233.3.197/32",
"191.233.3.197/32", "191.235.202.38/32",
"191.235.202.38/32", "20.53.78.144/32",
"20.53.78.144/32", "20.193.24.10/32",
"20.193.24.10/32", "20.53.78.236/32",
"20.53.78.236/32", "20.53.78.138/32",
"20.53.78.138/32", "20.53.78.123/32",
"20.53.78.123/32", "20.53.78.106/32",
"20.53.78.106/32", "20.193.27.215/32",
"20.193.27.215/32", "20.193.25.197/32",
"20.193.25.197/32", "20.193.12.126/32",
"20.193.12.126/32", "20.193.24.251/32",
"20.193.24.251/32", "20.204.242.101/32",
"20.204.242.101/32", "20.207.72.113/32",
"20.207.72.113/32", "20.204.242.19/32",
"20.204.242.19/32", "20.219.45.67/32",
"20.219.45.67/32", "20.207.72.11/32",
"20.207.72.11/32", "20.219.45.190/32",
"20.219.45.190/32", "20.204.243.55/32",
"20.204.243.55/32", "20.204.241.148/32",
"20.204.241.148/32", "20.207.72.110/32",
"20.207.72.110/32", "20.204.240.172/32",
"20.204.240.172/32", "20.207.72.21/32",
"20.207.72.21/32", "20.204.246.81/32",
"20.204.246.81/32", "20.207.107.181/32",
"20.207.107.181/32", "20.204.246.254/32",
"20.204.246.254/32", "20.219.43.246/32",
"20.219.43.246/32", "52.149.25.43/32",
"52.149.25.43/32", "52.149.61.51/32",
"52.149.61.51/32", "52.149.58.139/32",
"52.149.58.139/32", "52.149.60.38/32",
"52.149.60.38/32", "52.148.165.38/32",
"52.148.165.38/32", "52.143.95.162/32",
"52.143.95.162/32", "52.149.56.151/32",
"52.149.56.151/32", "52.149.30.45/32",
"52.149.30.45/32", "52.149.58.173/32",
"52.149.58.173/32", "52.143.95.204/32",
"52.143.95.204/32", "52.149.28.83/32",
"52.149.28.83/32", "52.149.58.69/32",
"52.149.58.69/32", "52.148.161.87/32",
"52.148.161.87/32", "52.149.58.27/32",
"52.149.58.27/32", "52.149.28.18/32",
"52.149.28.18/32", "20.79.226.26/32",
"20.79.226.26/32", "20.79.239.66/32",
"20.79.239.66/32", "20.79.238.198/32",
"20.79.238.198/32", "20.113.14.159/32",
"20.113.14.159/32", "20.75.144.152/32",
"20.75.144.152/32", "20.43.172.120/32",
"20.43.172.120/32", "20.53.134.160/32",
"20.53.134.160/32", "20.201.15.208/32",
"20.201.15.208/32", "20.93.28.24/32",
"20.93.28.24/32", "20.61.34.40/32",
"20.61.34.40/32", "52.242.224.168/32",
"52.242.224.168/32", "20.80.129.80/32",
"20.80.129.80/32", "20.195.108.47/32",
"20.195.108.47/32", "4.195.133.120/32",
"4.195.133.120/32", "4.228.76.163/32",
"4.228.76.163/32", "4.182.131.108/32",
"4.182.131.108/32", "4.209.224.56/32",
"4.209.224.56/32", "108.141.83.74/32",
"108.141.83.74/32", "4.213.46.14/32",
"4.213.46.14/32", "172.169.17.165/32",
"172.169.17.165/32", "51.8.71.117/32",
"51.8.71.117/32", "20.3.1.178/32",
"20.3.1.178/32", "52.149.56.151/32",
"52.149.56.151/32", "52.149.30.45/32",
"52.149.30.45/32", "52.149.58.173/32",
"52.149.58.173/32", "52.143.95.204/32",
"52.143.95.204/32", "52.149.28.83/32",
"52.149.28.83/32", "52.149.58.69/32",
"52.149.58.69/32", "52.148.161.87/32",
"52.148.161.87/32", "52.149.58.27/32",
"52.149.58.27/32", "52.149.28.18/32",
"52.149.28.18/32", "20.79.226.26/32",
"20.79.226.26/32", "20.79.239.66/32",
"20.79.239.66/32", "20.79.238.198/32",
"20.79.238.198/32", "20.113.14.159/32",
"20.113.14.159/32", "20.75.144.152/32",
"20.75.144.152/32", "20.43.172.120/32",
"20.43.172.120/32", "20.53.134.160/32",
"20.53.134.160/32", "20.201.15.208/32",
"20.201.15.208/32", "20.93.28.24/32",
"20.93.28.24/32", "20.61.34.40/32",
"20.61.34.40/32", "52.242.224.168/32",
"52.242.224.168/32", "20.80.129.80/32",
"20.80.129.80/32", "20.195.108.47/32",
"20.195.108.47/32", "4.195.133.120/32",
"4.195.133.120/32", "4.228.76.163/32",
"4.228.76.163/32", "4.182.131.108/32",
"4.182.131.108/32", "4.209.224.56/32",
"4.209.224.56/32", "108.141.83.74/32",
"108.141.83.74/32", "4.213.46.14/32",
"4.213.46.14/32", "172.169.17.165/32",
"172.169.17.165/32", "51.8.71.117/32",
"51.8.71.117/32", "20.3.1.178/32"
"20.3.1.178/32", ]
]

View File

@@ -2,263 +2,262 @@
user_agent_regex: \+http\://www\.google\.com/bot\.html user_agent_regex: \+http\://www\.google\.com/bot\.html
action: ALLOW action: ALLOW
# https://developers.google.com/static/search/apis/ipranges/googlebot.json # https://developers.google.com/static/search/apis/ipranges/googlebot.json
remote_addresses: remote_addresses: [
[ "2001:4860:4801:10::/64",
"2001:4860:4801:10::/64", "2001:4860:4801:11::/64",
"2001:4860:4801:11::/64", "2001:4860:4801:12::/64",
"2001:4860:4801:12::/64", "2001:4860:4801:13::/64",
"2001:4860:4801:13::/64", "2001:4860:4801:14::/64",
"2001:4860:4801:14::/64", "2001:4860:4801:15::/64",
"2001:4860:4801:15::/64", "2001:4860:4801:16::/64",
"2001:4860:4801:16::/64", "2001:4860:4801:17::/64",
"2001:4860:4801:17::/64", "2001:4860:4801:18::/64",
"2001:4860:4801:18::/64", "2001:4860:4801:19::/64",
"2001:4860:4801:19::/64", "2001:4860:4801:1a::/64",
"2001:4860:4801:1a::/64", "2001:4860:4801:1b::/64",
"2001:4860:4801:1b::/64", "2001:4860:4801:1c::/64",
"2001:4860:4801:1c::/64", "2001:4860:4801:1d::/64",
"2001:4860:4801:1d::/64", "2001:4860:4801:1e::/64",
"2001:4860:4801:1e::/64", "2001:4860:4801:1f::/64",
"2001:4860:4801:1f::/64", "2001:4860:4801:20::/64",
"2001:4860:4801:20::/64", "2001:4860:4801:21::/64",
"2001:4860:4801:21::/64", "2001:4860:4801:22::/64",
"2001:4860:4801:22::/64", "2001:4860:4801:23::/64",
"2001:4860:4801:23::/64", "2001:4860:4801:24::/64",
"2001:4860:4801:24::/64", "2001:4860:4801:25::/64",
"2001:4860:4801:25::/64", "2001:4860:4801:26::/64",
"2001:4860:4801:26::/64", "2001:4860:4801:27::/64",
"2001:4860:4801:27::/64", "2001:4860:4801:28::/64",
"2001:4860:4801:28::/64", "2001:4860:4801:29::/64",
"2001:4860:4801:29::/64", "2001:4860:4801:2::/64",
"2001:4860:4801:2::/64", "2001:4860:4801:2a::/64",
"2001:4860:4801:2a::/64", "2001:4860:4801:2b::/64",
"2001:4860:4801:2b::/64", "2001:4860:4801:2c::/64",
"2001:4860:4801:2c::/64", "2001:4860:4801:2d::/64",
"2001:4860:4801:2d::/64", "2001:4860:4801:2e::/64",
"2001:4860:4801:2e::/64", "2001:4860:4801:2f::/64",
"2001:4860:4801:2f::/64", "2001:4860:4801:31::/64",
"2001:4860:4801:31::/64", "2001:4860:4801:32::/64",
"2001:4860:4801:32::/64", "2001:4860:4801:33::/64",
"2001:4860:4801:33::/64", "2001:4860:4801:34::/64",
"2001:4860:4801:34::/64", "2001:4860:4801:35::/64",
"2001:4860:4801:35::/64", "2001:4860:4801:36::/64",
"2001:4860:4801:36::/64", "2001:4860:4801:37::/64",
"2001:4860:4801:37::/64", "2001:4860:4801:38::/64",
"2001:4860:4801:38::/64", "2001:4860:4801:39::/64",
"2001:4860:4801:39::/64", "2001:4860:4801:3a::/64",
"2001:4860:4801:3a::/64", "2001:4860:4801:3b::/64",
"2001:4860:4801:3b::/64", "2001:4860:4801:3c::/64",
"2001:4860:4801:3c::/64", "2001:4860:4801:3d::/64",
"2001:4860:4801:3d::/64", "2001:4860:4801:3e::/64",
"2001:4860:4801:3e::/64", "2001:4860:4801:40::/64",
"2001:4860:4801:40::/64", "2001:4860:4801:41::/64",
"2001:4860:4801:41::/64", "2001:4860:4801:42::/64",
"2001:4860:4801:42::/64", "2001:4860:4801:43::/64",
"2001:4860:4801:43::/64", "2001:4860:4801:44::/64",
"2001:4860:4801:44::/64", "2001:4860:4801:45::/64",
"2001:4860:4801:45::/64", "2001:4860:4801:46::/64",
"2001:4860:4801:46::/64", "2001:4860:4801:47::/64",
"2001:4860:4801:47::/64", "2001:4860:4801:48::/64",
"2001:4860:4801:48::/64", "2001:4860:4801:49::/64",
"2001:4860:4801:49::/64", "2001:4860:4801:4a::/64",
"2001:4860:4801:4a::/64", "2001:4860:4801:4b::/64",
"2001:4860:4801:4b::/64", "2001:4860:4801:4c::/64",
"2001:4860:4801:4c::/64", "2001:4860:4801:50::/64",
"2001:4860:4801:50::/64", "2001:4860:4801:51::/64",
"2001:4860:4801:51::/64", "2001:4860:4801:52::/64",
"2001:4860:4801:52::/64", "2001:4860:4801:53::/64",
"2001:4860:4801:53::/64", "2001:4860:4801:54::/64",
"2001:4860:4801:54::/64", "2001:4860:4801:55::/64",
"2001:4860:4801:55::/64", "2001:4860:4801:56::/64",
"2001:4860:4801:56::/64", "2001:4860:4801:60::/64",
"2001:4860:4801:60::/64", "2001:4860:4801:61::/64",
"2001:4860:4801:61::/64", "2001:4860:4801:62::/64",
"2001:4860:4801:62::/64", "2001:4860:4801:63::/64",
"2001:4860:4801:63::/64", "2001:4860:4801:64::/64",
"2001:4860:4801:64::/64", "2001:4860:4801:65::/64",
"2001:4860:4801:65::/64", "2001:4860:4801:66::/64",
"2001:4860:4801:66::/64", "2001:4860:4801:67::/64",
"2001:4860:4801:67::/64", "2001:4860:4801:68::/64",
"2001:4860:4801:68::/64", "2001:4860:4801:69::/64",
"2001:4860:4801:69::/64", "2001:4860:4801:6a::/64",
"2001:4860:4801:6a::/64", "2001:4860:4801:6b::/64",
"2001:4860:4801:6b::/64", "2001:4860:4801:6c::/64",
"2001:4860:4801:6c::/64", "2001:4860:4801:6d::/64",
"2001:4860:4801:6d::/64", "2001:4860:4801:6e::/64",
"2001:4860:4801:6e::/64", "2001:4860:4801:6f::/64",
"2001:4860:4801:6f::/64", "2001:4860:4801:70::/64",
"2001:4860:4801:70::/64", "2001:4860:4801:71::/64",
"2001:4860:4801:71::/64", "2001:4860:4801:72::/64",
"2001:4860:4801:72::/64", "2001:4860:4801:73::/64",
"2001:4860:4801:73::/64", "2001:4860:4801:74::/64",
"2001:4860:4801:74::/64", "2001:4860:4801:75::/64",
"2001:4860:4801:75::/64", "2001:4860:4801:76::/64",
"2001:4860:4801:76::/64", "2001:4860:4801:77::/64",
"2001:4860:4801:77::/64", "2001:4860:4801:78::/64",
"2001:4860:4801:78::/64", "2001:4860:4801:79::/64",
"2001:4860:4801:79::/64", "2001:4860:4801:80::/64",
"2001:4860:4801:80::/64", "2001:4860:4801:81::/64",
"2001:4860:4801:81::/64", "2001:4860:4801:82::/64",
"2001:4860:4801:82::/64", "2001:4860:4801:83::/64",
"2001:4860:4801:83::/64", "2001:4860:4801:84::/64",
"2001:4860:4801:84::/64", "2001:4860:4801:85::/64",
"2001:4860:4801:85::/64", "2001:4860:4801:86::/64",
"2001:4860:4801:86::/64", "2001:4860:4801:87::/64",
"2001:4860:4801:87::/64", "2001:4860:4801:88::/64",
"2001:4860:4801:88::/64", "2001:4860:4801:90::/64",
"2001:4860:4801:90::/64", "2001:4860:4801:91::/64",
"2001:4860:4801:91::/64", "2001:4860:4801:92::/64",
"2001:4860:4801:92::/64", "2001:4860:4801:93::/64",
"2001:4860:4801:93::/64", "2001:4860:4801:94::/64",
"2001:4860:4801:94::/64", "2001:4860:4801:95::/64",
"2001:4860:4801:95::/64", "2001:4860:4801:96::/64",
"2001:4860:4801:96::/64", "2001:4860:4801:a0::/64",
"2001:4860:4801:a0::/64", "2001:4860:4801:a1::/64",
"2001:4860:4801:a1::/64", "2001:4860:4801:a2::/64",
"2001:4860:4801:a2::/64", "2001:4860:4801:a3::/64",
"2001:4860:4801:a3::/64", "2001:4860:4801:a4::/64",
"2001:4860:4801:a4::/64", "2001:4860:4801:a5::/64",
"2001:4860:4801:a5::/64", "2001:4860:4801:c::/64",
"2001:4860:4801:c::/64", "2001:4860:4801:f::/64",
"2001:4860:4801:f::/64", "192.178.5.0/27",
"192.178.5.0/27", "192.178.6.0/27",
"192.178.6.0/27", "192.178.6.128/27",
"192.178.6.128/27", "192.178.6.160/27",
"192.178.6.160/27", "192.178.6.192/27",
"192.178.6.192/27", "192.178.6.32/27",
"192.178.6.32/27", "192.178.6.64/27",
"192.178.6.64/27", "192.178.6.96/27",
"192.178.6.96/27", "34.100.182.96/28",
"34.100.182.96/28", "34.101.50.144/28",
"34.101.50.144/28", "34.118.254.0/28",
"34.118.254.0/28", "34.118.66.0/28",
"34.118.66.0/28", "34.126.178.96/28",
"34.126.178.96/28", "34.146.150.144/28",
"34.146.150.144/28", "34.147.110.144/28",
"34.147.110.144/28", "34.151.74.144/28",
"34.151.74.144/28", "34.152.50.64/28",
"34.152.50.64/28", "34.154.114.144/28",
"34.154.114.144/28", "34.155.98.32/28",
"34.155.98.32/28", "34.165.18.176/28",
"34.165.18.176/28", "34.175.160.64/28",
"34.175.160.64/28", "34.176.130.16/28",
"34.176.130.16/28", "34.22.85.0/27",
"34.22.85.0/27", "34.64.82.64/28",
"34.64.82.64/28", "34.65.242.112/28",
"34.65.242.112/28", "34.80.50.80/28",
"34.80.50.80/28", "34.88.194.0/28",
"34.88.194.0/28", "34.89.10.80/28",
"34.89.10.80/28", "34.89.198.80/28",
"34.89.198.80/28", "34.96.162.48/28",
"34.96.162.48/28", "35.247.243.240/28",
"35.247.243.240/28", "66.249.64.0/27",
"66.249.64.0/27", "66.249.64.128/27",
"66.249.64.128/27", "66.249.64.160/27",
"66.249.64.160/27", "66.249.64.224/27",
"66.249.64.224/27", "66.249.64.32/27",
"66.249.64.32/27", "66.249.64.64/27",
"66.249.64.64/27", "66.249.64.96/27",
"66.249.64.96/27", "66.249.65.0/27",
"66.249.65.0/27", "66.249.65.128/27",
"66.249.65.128/27", "66.249.65.160/27",
"66.249.65.160/27", "66.249.65.192/27",
"66.249.65.192/27", "66.249.65.224/27",
"66.249.65.224/27", "66.249.65.32/27",
"66.249.65.32/27", "66.249.65.64/27",
"66.249.65.64/27", "66.249.65.96/27",
"66.249.65.96/27", "66.249.66.0/27",
"66.249.66.0/27", "66.249.66.128/27",
"66.249.66.128/27", "66.249.66.160/27",
"66.249.66.160/27", "66.249.66.192/27",
"66.249.66.192/27", "66.249.66.224/27",
"66.249.66.224/27", "66.249.66.32/27",
"66.249.66.32/27", "66.249.66.64/27",
"66.249.66.64/27", "66.249.66.96/27",
"66.249.66.96/27", "66.249.68.0/27",
"66.249.68.0/27", "66.249.68.128/27",
"66.249.68.128/27", "66.249.68.32/27",
"66.249.68.32/27", "66.249.68.64/27",
"66.249.68.64/27", "66.249.68.96/27",
"66.249.68.96/27", "66.249.69.0/27",
"66.249.69.0/27", "66.249.69.128/27",
"66.249.69.128/27", "66.249.69.160/27",
"66.249.69.160/27", "66.249.69.192/27",
"66.249.69.192/27", "66.249.69.224/27",
"66.249.69.224/27", "66.249.69.32/27",
"66.249.69.32/27", "66.249.69.64/27",
"66.249.69.64/27", "66.249.69.96/27",
"66.249.69.96/27", "66.249.70.0/27",
"66.249.70.0/27", "66.249.70.128/27",
"66.249.70.128/27", "66.249.70.160/27",
"66.249.70.160/27", "66.249.70.192/27",
"66.249.70.192/27", "66.249.70.224/27",
"66.249.70.224/27", "66.249.70.32/27",
"66.249.70.32/27", "66.249.70.64/27",
"66.249.70.64/27", "66.249.70.96/27",
"66.249.70.96/27", "66.249.71.0/27",
"66.249.71.0/27", "66.249.71.128/27",
"66.249.71.128/27", "66.249.71.160/27",
"66.249.71.160/27", "66.249.71.192/27",
"66.249.71.192/27", "66.249.71.224/27",
"66.249.71.224/27", "66.249.71.32/27",
"66.249.71.32/27", "66.249.71.64/27",
"66.249.71.64/27", "66.249.71.96/27",
"66.249.71.96/27", "66.249.72.0/27",
"66.249.72.0/27", "66.249.72.128/27",
"66.249.72.128/27", "66.249.72.160/27",
"66.249.72.160/27", "66.249.72.192/27",
"66.249.72.192/27", "66.249.72.224/27",
"66.249.72.224/27", "66.249.72.32/27",
"66.249.72.32/27", "66.249.72.64/27",
"66.249.72.64/27", "66.249.72.96/27",
"66.249.72.96/27", "66.249.73.0/27",
"66.249.73.0/27", "66.249.73.128/27",
"66.249.73.128/27", "66.249.73.160/27",
"66.249.73.160/27", "66.249.73.192/27",
"66.249.73.192/27", "66.249.73.224/27",
"66.249.73.224/27", "66.249.73.32/27",
"66.249.73.32/27", "66.249.73.64/27",
"66.249.73.64/27", "66.249.73.96/27",
"66.249.73.96/27", "66.249.74.0/27",
"66.249.74.0/27", "66.249.74.128/27",
"66.249.74.128/27", "66.249.74.160/27",
"66.249.74.160/27", "66.249.74.192/27",
"66.249.74.192/27", "66.249.74.32/27",
"66.249.74.32/27", "66.249.74.64/27",
"66.249.74.64/27", "66.249.74.96/27",
"66.249.74.96/27", "66.249.75.0/27",
"66.249.75.0/27", "66.249.75.128/27",
"66.249.75.128/27", "66.249.75.160/27",
"66.249.75.160/27", "66.249.75.192/27",
"66.249.75.192/27", "66.249.75.224/27",
"66.249.75.224/27", "66.249.75.32/27",
"66.249.75.32/27", "66.249.75.64/27",
"66.249.75.64/27", "66.249.75.96/27",
"66.249.75.96/27", "66.249.76.0/27",
"66.249.76.0/27", "66.249.76.128/27",
"66.249.76.128/27", "66.249.76.160/27",
"66.249.76.160/27", "66.249.76.192/27",
"66.249.76.192/27", "66.249.76.224/27",
"66.249.76.224/27", "66.249.76.32/27",
"66.249.76.32/27", "66.249.76.64/27",
"66.249.76.64/27", "66.249.76.96/27",
"66.249.76.96/27", "66.249.77.0/27",
"66.249.77.0/27", "66.249.77.128/27",
"66.249.77.128/27", "66.249.77.160/27",
"66.249.77.160/27", "66.249.77.192/27",
"66.249.77.192/27", "66.249.77.224/27",
"66.249.77.224/27", "66.249.77.32/27",
"66.249.77.32/27", "66.249.77.64/27",
"66.249.77.64/27", "66.249.77.96/27",
"66.249.77.96/27", "66.249.78.0/27",
"66.249.78.0/27", "66.249.78.32/27",
"66.249.78.32/27", "66.249.79.0/27",
"66.249.79.0/27", "66.249.79.128/27",
"66.249.79.128/27", "66.249.79.160/27",
"66.249.79.160/27", "66.249.79.192/27",
"66.249.79.192/27", "66.249.79.224/27",
"66.249.79.224/27", "66.249.79.32/27",
"66.249.79.32/27", "66.249.79.64/27",
"66.249.79.64/27", "66.249.79.96/27"
"66.249.79.96/27", ]
]

View File

@@ -1,4 +1,8 @@
- name: internet-archive - name: internet-archive
action: ALLOW action: ALLOW
# https://ipinfo.io/AS7941 # https://ipinfo.io/AS7941
remote_addresses: ["207.241.224.0/20", "208.70.24.0/21", "2620:0:9c0::/48"] remote_addresses: [
"207.241.224.0/20",
"208.70.24.0/21",
"2620:0:9c0::/48"
]

View File

@@ -2,10 +2,9 @@
user_agent_regex: \+https\://kagi\.com/bot user_agent_regex: \+https\://kagi\.com/bot
action: ALLOW action: ALLOW
# https://kagi.com/bot # https://kagi.com/bot
remote_addresses: remote_addresses: [
[ "216.18.205.234/32",
"216.18.205.234/32", "35.212.27.76/32",
"35.212.27.76/32", "104.254.65.50/32",
"104.254.65.50/32", "209.151.156.194/32"
"209.151.156.194/32", ]
]

View File

@@ -2,11 +2,10 @@
user_agent_regex: search\.marginalia\.nu user_agent_regex: search\.marginalia\.nu
action: ALLOW action: ALLOW
# Received directly over email # Received directly over email
remote_addresses: remote_addresses: [
[ "193.183.0.162/31",
"193.183.0.162/31", "193.183.0.164/30",
"193.183.0.164/30", "193.183.0.168/30",
"193.183.0.168/30", "193.183.0.172/31",
"193.183.0.172/31", "193.183.0.174/32"
"193.183.0.174/32", ]
]

View File

@@ -2,4 +2,4 @@
user_agent_regex: \+https\://www\.mojeek\.com/bot\.html user_agent_regex: \+https\://www\.mojeek\.com/bot\.html
action: ALLOW action: ALLOW
# https://www.mojeek.com/bot.html # https://www.mojeek.com/bot.html
remote_addresses: ["5.102.173.71/32"] remote_addresses: [ "5.102.173.71/32" ]

View File

@@ -4,14 +4,13 @@
user_agent_regex: GPTBot/1\.1; \+https\://openai\.com/gptbot user_agent_regex: GPTBot/1\.1; \+https\://openai\.com/gptbot
action: ALLOW action: ALLOW
# https://openai.com/gptbot.json # https://openai.com/gptbot.json
remote_addresses: remote_addresses: [
[ "52.230.152.0/24",
"52.230.152.0/24", "20.171.206.0/24",
"20.171.206.0/24", "20.171.207.0/24",
"20.171.207.0/24", "4.227.36.0/25",
"4.227.36.0/25", "20.125.66.80/28",
"20.125.66.80/28", "172.182.204.0/24",
"172.182.204.0/24", "172.182.214.0/24",
"172.182.214.0/24", "172.182.215.0/24",
"172.182.215.0/24", ]
]

View File

@@ -4,11 +4,10 @@
user_agent_regex: OAI-SearchBot/1\.0; \+https\://openai\.com/searchbot user_agent_regex: OAI-SearchBot/1\.0; \+https\://openai\.com/searchbot
action: ALLOW action: ALLOW
# https://openai.com/searchbot.json # https://openai.com/searchbot.json
remote_addresses: remote_addresses: [
[ "20.42.10.176/28",
"20.42.10.176/28", "172.203.190.128/28",
"172.203.190.128/28", "104.210.140.128/28",
"104.210.140.128/28", "51.8.102.0/24",
"51.8.102.0/24", "135.234.64.0/24"
"135.234.64.0/24", ]
]

View File

@@ -1,17 +0,0 @@
# Indexing for search, does not collect training data
# https://docs.perplexity.ai/guides/bots
- name: perplexitybot
user_agent_regex: PerplexityBot/.+; \+https\://perplexity\.ai/perplexitybot
action: ALLOW
# https://www.perplexity.com/perplexitybot.json
remote_addresses:
[
"107.20.236.150/32",
"3.224.62.45/32",
"18.210.92.235/32",
"3.222.232.239/32",
"3.211.124.183/32",
"3.231.139.107/32",
"18.97.1.228/30",
"18.97.9.96/29",
]

View File

@@ -2,4 +2,4 @@
user_agent_regex: \+https\://help\.qwant\.com/bot/ user_agent_regex: \+https\://help\.qwant\.com/bot/
action: ALLOW action: ALLOW
# https://help.qwant.com/wp-content/uploads/sites/2/2025/01/qwantbot.json # https://help.qwant.com/wp-content/uploads/sites/2/2025/01/qwantbot.json
remote_addresses: ["91.242.162.0/24"] remote_addresses: [ "91.242.162.0/24" ]

View File

@@ -1,18 +0,0 @@
# Wikimedia Foundation citation services
# https://www.mediawiki.org/wiki/Citoid
- name: wikimedia-citoid
user_agent_regex: "Citoid/WMF"
action: ALLOW
remote_addresses: [
"208.80.152.0/22",
"2620:0:860::/46",
]
- name: wikimedia-zotero-translation-server
user_agent_regex: "ZoteroTranslationServer/WMF"
action: ALLOW
remote_addresses: [
"208.80.152.0/22",
"2620:0:860::/46",
]

View File

@@ -3,7 +3,5 @@
- import: (data)/bots/ai-catchall.yaml - import: (data)/bots/ai-catchall.yaml
- import: (data)/crawlers/ai-training.yaml - import: (data)/crawlers/ai-training.yaml
- import: (data)/crawlers/openai-searchbot.yaml - import: (data)/crawlers/openai-searchbot.yaml
- import: (data)/crawlers/perplexitybot.yaml
- import: (data)/clients/openai-chatgpt-user.yaml - import: (data)/clients/openai-chatgpt-user.yaml
- import: (data)/clients/mistral-mistralai-user.yaml - import: (data)/clients/mistral-mistralai-user.yaml
- import: (data)/clients/perplexity-user.yaml

View File

@@ -2,7 +2,5 @@
- import: (data)/bots/ai-catchall.yaml - import: (data)/bots/ai-catchall.yaml
- import: (data)/crawlers/openai-searchbot.yaml - import: (data)/crawlers/openai-searchbot.yaml
- import: (data)/crawlers/openai-gptbot.yaml - import: (data)/crawlers/openai-gptbot.yaml
- import: (data)/crawlers/perplexitybot.yaml
- import: (data)/clients/openai-chatgpt-user.yaml - import: (data)/clients/openai-chatgpt-user.yaml
- import: (data)/clients/mistral-mistralai-user.yaml - import: (data)/clients/mistral-mistralai-user.yaml
- import: (data)/clients/perplexity-user.yaml

View File

@@ -1,26 +0,0 @@
# https://updown.io/about
- name: updown
user_agent_regex: updown.io
action: ALLOW
remote_addresses: [
"45.32.74.41/32",
"104.238.136.194/32",
"192.99.37.47/32",
"91.121.222.175/32",
"104.238.159.87/32",
"102.212.60.78/32",
"135.181.102.135/32",
"45.32.107.181/32",
"45.76.104.117/32",
"45.63.29.207/32",
"2001:19f0:6001:2c6::1/128",
"2001:19f0:9002:11a::1/128",
"2607:5300:60:4c2f::1/128",
"2001:41d0:2:85af::1/128",
"2001:19f0:6c01:145::1/128",
"2c0f:c40:4003:4::2/128",
"2a01:4f9:c010:d5f9::1/128",
"2001:19f0:4400:402e::1/128",
"2001:19f0:7001:45a::1/128",
"2001:19f0:5801:1d8::1/128"
]

View File

@@ -2,223 +2,222 @@
user_agent_regex: UptimeRobot user_agent_regex: UptimeRobot
action: ALLOW action: ALLOW
# https://api.uptimerobot.com/meta/ips # https://api.uptimerobot.com/meta/ips
remote_addresses: remote_addresses: [
[ "3.12.251.153/32",
"3.12.251.153/32", "3.20.63.178/32",
"3.20.63.178/32", "3.77.67.4/32",
"3.77.67.4/32", "3.79.134.69/32",
"3.79.134.69/32", "3.105.133.239/32",
"3.105.133.239/32", "3.105.190.221/32",
"3.105.190.221/32", "3.133.226.214/32",
"3.133.226.214/32", "3.149.57.90/32",
"3.149.57.90/32", "3.212.128.62/32",
"3.212.128.62/32", "5.161.61.238/32",
"5.161.61.238/32", "5.161.73.160/32",
"5.161.73.160/32", "5.161.75.7/32",
"5.161.75.7/32", "5.161.113.195/32",
"5.161.113.195/32", "5.161.117.52/32",
"5.161.117.52/32", "5.161.177.47/32",
"5.161.177.47/32", "5.161.194.92/32",
"5.161.194.92/32", "5.161.215.244/32",
"5.161.215.244/32", "5.223.43.32/32",
"5.223.43.32/32", "5.223.53.147/32",
"5.223.53.147/32", "5.223.57.22/32",
"5.223.57.22/32", "18.116.205.62/32",
"18.116.205.62/32", "18.180.208.214/32",
"18.180.208.214/32", "18.192.166.72/32",
"18.192.166.72/32", "18.193.252.127/32",
"18.193.252.127/32", "24.144.78.39/32",
"24.144.78.39/32", "24.144.78.185/32",
"24.144.78.185/32", "34.198.201.66/32",
"34.198.201.66/32", "45.55.123.175/32",
"45.55.123.175/32", "45.55.127.146/32",
"45.55.127.146/32", "49.13.24.81/32",
"49.13.24.81/32", "49.13.130.29/32",
"49.13.130.29/32", "49.13.134.145/32",
"49.13.134.145/32", "49.13.164.148/32",
"49.13.164.148/32", "49.13.167.123/32",
"49.13.167.123/32", "52.15.147.27/32",
"52.15.147.27/32", "52.22.236.30/32",
"52.22.236.30/32", "52.28.162.93/32",
"52.28.162.93/32", "52.59.43.236/32",
"52.59.43.236/32", "52.87.72.16/32",
"52.87.72.16/32", "54.64.67.106/32",
"54.64.67.106/32", "54.79.28.129/32",
"54.79.28.129/32", "54.87.112.51/32",
"54.87.112.51/32", "54.167.223.174/32",
"54.167.223.174/32", "54.249.170.27/32",
"54.249.170.27/32", "63.178.84.147/32",
"63.178.84.147/32", "64.225.81.248/32",
"64.225.81.248/32", "64.225.82.147/32",
"64.225.82.147/32", "69.162.124.227/32",
"69.162.124.227/32", "69.162.124.235/32",
"69.162.124.235/32", "69.162.124.238/32",
"69.162.124.238/32", "78.46.190.63/32",
"78.46.190.63/32", "78.46.215.1/32",
"78.46.215.1/32", "78.47.98.55/32",
"78.47.98.55/32", "78.47.173.76/32",
"78.47.173.76/32", "88.99.80.227/32",
"88.99.80.227/32", "91.99.101.207/32",
"91.99.101.207/32", "128.140.41.193/32",
"128.140.41.193/32", "128.140.106.114/32",
"128.140.106.114/32", "129.212.132.140/32",
"129.212.132.140/32", "134.199.240.137/32",
"134.199.240.137/32", "138.197.53.117/32",
"138.197.53.117/32", "138.197.53.138/32",
"138.197.53.138/32", "138.197.54.143/32",
"138.197.54.143/32", "138.197.54.247/32",
"138.197.54.247/32", "138.197.63.92/32",
"138.197.63.92/32", "139.59.50.44/32",
"139.59.50.44/32", "142.132.180.39/32",
"142.132.180.39/32", "143.198.249.237/32",
"143.198.249.237/32", "143.198.250.89/32",
"143.198.250.89/32", "143.244.196.21/32",
"143.244.196.21/32", "143.244.196.211/32",
"143.244.196.211/32", "143.244.221.177/32",
"143.244.221.177/32", "144.126.251.21/32",
"144.126.251.21/32", "146.190.9.187/32",
"146.190.9.187/32", "152.42.149.135/32",
"152.42.149.135/32", "157.90.155.240/32",
"157.90.155.240/32", "157.90.156.63/32",
"157.90.156.63/32", "159.69.158.189/32",
"159.69.158.189/32", "159.223.243.219/32",
"159.223.243.219/32", "161.35.247.201/32",
"161.35.247.201/32", "167.99.18.52/32",
"167.99.18.52/32", "167.235.143.113/32",
"167.235.143.113/32", "168.119.53.160/32",
"168.119.53.160/32", "168.119.96.239/32",
"168.119.96.239/32", "168.119.123.75/32",
"168.119.123.75/32", "170.64.250.64/32",
"170.64.250.64/32", "170.64.250.132/32",
"170.64.250.132/32", "170.64.250.235/32",
"170.64.250.235/32", "178.156.181.172/32",
"178.156.181.172/32", "178.156.184.20/32",
"178.156.184.20/32", "178.156.185.127/32",
"178.156.185.127/32", "178.156.185.231/32",
"178.156.185.231/32", "178.156.187.238/32",
"178.156.187.238/32", "178.156.189.113/32",
"178.156.189.113/32", "178.156.189.249/32",
"178.156.189.249/32", "188.166.201.79/32",
"188.166.201.79/32", "206.189.241.133/32",
"206.189.241.133/32", "209.38.49.1/32",
"209.38.49.1/32", "209.38.49.206/32",
"209.38.49.206/32", "209.38.49.226/32",
"209.38.49.226/32", "209.38.51.43/32",
"209.38.51.43/32", "209.38.53.7/32",
"209.38.53.7/32", "209.38.124.252/32",
"209.38.124.252/32", "216.144.248.18/31",
"216.144.248.18/31", "216.144.248.21/32",
"216.144.248.21/32", "216.144.248.22/31",
"216.144.248.22/31", "216.144.248.24/30",
"216.144.248.24/30", "216.144.248.28/31",
"216.144.248.28/31", "216.144.248.30/32",
"216.144.248.30/32", "216.245.221.83/32",
"216.245.221.83/32", "2400:6180:10:200::56a0:b000/128",
"2400:6180:10:200::56a0:b000/128", "2400:6180:10:200::56a0:c000/128",
"2400:6180:10:200::56a0:c000/128", "2400:6180:10:200::56a0:e000/128",
"2400:6180:10:200::56a0:e000/128", "2400:6180:100:d0::94b6:4001/128",
"2400:6180:100:d0::94b6:4001/128", "2400:6180:100:d0::94b6:5001/128",
"2400:6180:100:d0::94b6:5001/128", "2400:6180:100:d0::94b6:7001/128",
"2400:6180:100:d0::94b6:7001/128", "2406:da14:94d:8601:9d0d:7754:bedf:e4f5/128",
"2406:da14:94d:8601:9d0d:7754:bedf:e4f5/128", "2406:da14:94d:8601:b325:ff58:2bba:7934/128",
"2406:da14:94d:8601:b325:ff58:2bba:7934/128", "2406:da14:94d:8601:db4b:c5ac:2cbe:9a79/128",
"2406:da14:94d:8601:db4b:c5ac:2cbe:9a79/128", "2406:da1c:9c8:dc02:7ae1:f2ea:ab91:2fde/128",
"2406:da1c:9c8:dc02:7ae1:f2ea:ab91:2fde/128", "2406:da1c:9c8:dc02:7db9:f38b:7b9f:402e/128",
"2406:da1c:9c8:dc02:7db9:f38b:7b9f:402e/128", "2406:da1c:9c8:dc02:82b2:f0fd:ee96:579/128",
"2406:da1c:9c8:dc02:82b2:f0fd:ee96:579/128", "2600:1f16:775:3a00:ac3:c5eb:7081:942e/128",
"2600:1f16:775:3a00:ac3:c5eb:7081:942e/128", "2600:1f16:775:3a00:37bf:6026:e54a:f03a/128",
"2600:1f16:775:3a00:37bf:6026:e54a:f03a/128", "2600:1f16:775:3a00:3f24:5bb0:95d7:5a6b/128",
"2600:1f16:775:3a00:3f24:5bb0:95d7:5a6b/128", "2600:1f16:775:3a00:8c2c:2ba6:778f:5be5/128",
"2600:1f16:775:3a00:8c2c:2ba6:778f:5be5/128", "2600:1f16:775:3a00:91ac:3120:ff38:92b5/128",
"2600:1f16:775:3a00:91ac:3120:ff38:92b5/128", "2600:1f16:775:3a00:dbbe:36b0:3c45:da32/128",
"2600:1f16:775:3a00:dbbe:36b0:3c45:da32/128", "2600:1f18:179:f900:71:af9a:ade7:d772/128",
"2600:1f18:179:f900:71:af9a:ade7:d772/128", "2600:1f18:179:f900:2406:9399:4ae6:c5d3/128",
"2600:1f18:179:f900:2406:9399:4ae6:c5d3/128", "2600:1f18:179:f900:4696:7729:7bb3:f52f/128",
"2600:1f18:179:f900:4696:7729:7bb3:f52f/128", "2600:1f18:179:f900:4b7d:d1cc:2d10:211/128",
"2600:1f18:179:f900:4b7d:d1cc:2d10:211/128", "2600:1f18:179:f900:5c68:91b6:5d75:5d7/128",
"2600:1f18:179:f900:5c68:91b6:5d75:5d7/128", "2600:1f18:179:f900:e8dd:eed1:a6c:183b/128",
"2600:1f18:179:f900:e8dd:eed1:a6c:183b/128", "2604:a880:800:14:0:1:68ba:d000/128",
"2604:a880:800:14:0:1:68ba:d000/128", "2604:a880:800:14:0:1:68ba:e000/128",
"2604:a880:800:14:0:1:68ba:e000/128", "2604:a880:800:14:0:1:68bb:0/128",
"2604:a880:800:14:0:1:68bb:0/128", "2604:a880:800:14:0:1:68bb:1000/128",
"2604:a880:800:14:0:1:68bb:1000/128", "2604:a880:800:14:0:1:68bb:3000/128",
"2604:a880:800:14:0:1:68bb:3000/128", "2604:a880:800:14:0:1:68bb:4000/128",
"2604:a880:800:14:0:1:68bb:4000/128", "2604:a880:800:14:0:1:68bb:5000/128",
"2604:a880:800:14:0:1:68bb:5000/128", "2604:a880:800:14:0:1:68bb:6000/128",
"2604:a880:800:14:0:1:68bb:6000/128", "2604:a880:800:14:0:1:68bb:7000/128",
"2604:a880:800:14:0:1:68bb:7000/128", "2604:a880:800:14:0:1:68bb:a000/128",
"2604:a880:800:14:0:1:68bb:a000/128", "2604:a880:800:14:0:1:68bb:b000/128",
"2604:a880:800:14:0:1:68bb:b000/128", "2604:a880:800:14:0:1:68bb:c000/128",
"2604:a880:800:14:0:1:68bb:c000/128", "2604:a880:800:14:0:1:68bb:d000/128",
"2604:a880:800:14:0:1:68bb:d000/128", "2604:a880:800:14:0:1:68bb:e000/128",
"2604:a880:800:14:0:1:68bb:e000/128", "2604:a880:800:14:0:1:68bb:f000/128",
"2604:a880:800:14:0:1:68bb:f000/128", "2607:ff68:107::4/128",
"2607:ff68:107::4/128", "2607:ff68:107::14/128",
"2607:ff68:107::14/128", "2607:ff68:107::33/128",
"2607:ff68:107::33/128", "2607:ff68:107::48/127",
"2607:ff68:107::48/127", "2607:ff68:107::50/125",
"2607:ff68:107::50/125", "2607:ff68:107::58/127",
"2607:ff68:107::58/127", "2607:ff68:107::60/128",
"2607:ff68:107::60/128", "2a01:4f8:c0c:83fa::1/128",
"2a01:4f8:c0c:83fa::1/128", "2a01:4f8:c17:42e4::1/128",
"2a01:4f8:c17:42e4::1/128", "2a01:4f8:c2c:9fc6::1/128",
"2a01:4f8:c2c:9fc6::1/128", "2a01:4f8:c2c:beae::1/128",
"2a01:4f8:c2c:beae::1/128", "2a01:4f8:1c1a:3d53::1/128",
"2a01:4f8:1c1a:3d53::1/128", "2a01:4f8:1c1b:4ef4::1/128",
"2a01:4f8:1c1b:4ef4::1/128", "2a01:4f8:1c1b:5b5a::1/128",
"2a01:4f8:1c1b:5b5a::1/128", "2a01:4f8:1c1b:7ecc::1/128",
"2a01:4f8:1c1b:7ecc::1/128", "2a01:4f8:1c1c:11aa::1/128",
"2a01:4f8:1c1c:11aa::1/128", "2a01:4f8:1c1c:5353::1/128",
"2a01:4f8:1c1c:5353::1/128", "2a01:4f8:1c1c:7240::1/128",
"2a01:4f8:1c1c:7240::1/128", "2a01:4f8:1c1c:a98a::1/128",
"2a01:4f8:1c1c:a98a::1/128", "2a01:4f8:c012:c60e::1/128",
"2a01:4f8:c012:c60e::1/128", "2a01:4f8:c013:c18::1/128",
"2a01:4f8:c013:c18::1/128", "2a01:4f8:c013:34c0::1/128",
"2a01:4f8:c013:34c0::1/128", "2a01:4f8:c013:3b0f::1/128",
"2a01:4f8:c013:3b0f::1/128", "2a01:4f8:c013:3c52::1/128",
"2a01:4f8:c013:3c52::1/128", "2a01:4f8:c013:3c53::1/128",
"2a01:4f8:c013:3c53::1/128", "2a01:4f8:c013:3c54::1/128",
"2a01:4f8:c013:3c54::1/128", "2a01:4f8:c013:3c55::1/128",
"2a01:4f8:c013:3c55::1/128", "2a01:4f8:c013:3c56::1/128",
"2a01:4f8:c013:3c56::1/128", "2a01:4ff:f0:bfd::1/128",
"2a01:4ff:f0:bfd::1/128", "2a01:4ff:f0:2219::1/128",
"2a01:4ff:f0:2219::1/128", "2a01:4ff:f0:3e03::1/128",
"2a01:4ff:f0:3e03::1/128", "2a01:4ff:f0:5f80::1/128",
"2a01:4ff:f0:5f80::1/128", "2a01:4ff:f0:7fad::1/128",
"2a01:4ff:f0:7fad::1/128", "2a01:4ff:f0:9c5f::1/128",
"2a01:4ff:f0:9c5f::1/128", "2a01:4ff:f0:b2f2::1/128",
"2a01:4ff:f0:b2f2::1/128", "2a01:4ff:f0:b6f1::1/128",
"2a01:4ff:f0:b6f1::1/128", "2a01:4ff:f0:d283::1/128",
"2a01:4ff:f0:d283::1/128", "2a01:4ff:f0:d3cd::1/128",
"2a01:4ff:f0:d3cd::1/128", "2a01:4ff:f0:e516::1/128",
"2a01:4ff:f0:e516::1/128", "2a01:4ff:f0:e9cf::1/128",
"2a01:4ff:f0:e9cf::1/128", "2a01:4ff:f0:eccb::1/128",
"2a01:4ff:f0:eccb::1/128", "2a01:4ff:f0:efd1::1/128",
"2a01:4ff:f0:efd1::1/128", "2a01:4ff:f0:fdc7::1/128",
"2a01:4ff:f0:fdc7::1/128", "2a01:4ff:2f0:193c::1/128",
"2a01:4ff:2f0:193c::1/128", "2a01:4ff:2f0:27de::1/128",
"2a01:4ff:2f0:27de::1/128", "2a01:4ff:2f0:3b3a::1/128",
"2a01:4ff:2f0:3b3a::1/128", "2a03:b0c0:2:f0::bd91:f001/128",
"2a03:b0c0:2:f0::bd91:f001/128", "2a03:b0c0:2:f0::bd92:1/128",
"2a03:b0c0:2:f0::bd92:1/128", "2a03:b0c0:2:f0::bd92:1001/128",
"2a03:b0c0:2:f0::bd92:1001/128", "2a03:b0c0:2:f0::bd92:2001/128",
"2a03:b0c0:2:f0::bd92:2001/128", "2a03:b0c0:2:f0::bd92:4001/128",
"2a03:b0c0:2:f0::bd92:4001/128", "2a03:b0c0:2:f0::bd92:5001/128",
"2a03:b0c0:2:f0::bd92:5001/128", "2a03:b0c0:2:f0::bd92:6001/128",
"2a03:b0c0:2:f0::bd92:6001/128", "2a03:b0c0:2:f0::bd92:7001/128",
"2a03:b0c0:2:f0::bd92:7001/128", "2a03:b0c0:2:f0::bd92:8001/128",
"2a03:b0c0:2:f0::bd92:8001/128", "2a03:b0c0:2:f0::bd92:9001/128",
"2a03:b0c0:2:f0::bd92:9001/128", "2a03:b0c0:2:f0::bd92:a001/128",
"2a03:b0c0:2:f0::bd92:a001/128", "2a03:b0c0:2:f0::bd92:b001/128",
"2a03:b0c0:2:f0::bd92:b001/128", "2a03:b0c0:2:f0::bd92:c001/128",
"2a03:b0c0:2:f0::bd92:c001/128", "2a03:b0c0:2:f0::bd92:e001/128",
"2a03:b0c0:2:f0::bd92:e001/128", "2a03:b0c0:2:f0::bd92:f001/128",
"2a03:b0c0:2:f0::bd92:f001/128", "2a05:d014:1815:3400:6d:9235:c1c0:96ad/128",
"2a05:d014:1815:3400:6d:9235:c1c0:96ad/128", "2a05:d014:1815:3400:654f:bd37:724c:212b/128",
"2a05:d014:1815:3400:654f:bd37:724c:212b/128", "2a05:d014:1815:3400:90b4:4ef9:5631:b170/128",
"2a05:d014:1815:3400:90b4:4ef9:5631:b170/128", "2a05:d014:1815:3400:9779:d8e9:100a:9642/128",
"2a05:d014:1815:3400:9779:d8e9:100a:9642/128", "2a05:d014:1815:3400:af29:e95e:64ff:df81/128",
"2a05:d014:1815:3400:af29:e95e:64ff:df81/128", "2a05:d014:1815:3400:c7d6:f7f3:6cc1:30d1/128",
"2a05:d014:1815:3400:c7d6:f7f3:6cc1:30d1/128", "2a05:d014:1815:3400:d784:e5dd:8e0:67cb/128",
"2a05:d014:1815:3400:d784:e5dd:8e0:67cb/128", ]
]

View File

@@ -19,7 +19,7 @@ type Impl[K comparable, V any] struct {
// stopCh stops the background cleanup worker. // stopCh stops the background cleanup worker.
stopCh chan struct{} stopCh chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
lock sync.RWMutex lock sync.Mutex
} }
type decayMapEntry[V any] struct { type decayMapEntry[V any] struct {
@@ -64,26 +64,28 @@ func (m *Impl[K, V]) expire(key K) bool {
// Delete a value from the DecayMap by key. // Delete a value from the DecayMap by key.
// //
// This defers deletions to a background thread for performance reasons.
//
// If the value does not exist, return false. Return true after // If the value does not exist, return false. Return true after
// deletion. // deletion.
func (m *Impl[K, V]) Delete(key K) bool { func (m *Impl[K, V]) Delete(key K) bool {
// Use a single write lock to avoid RUnlock->Lock convoy. select {
m.lock.Lock() // Defer decay deletion to the background worker to avoid convoy.
defer m.lock.Unlock() case m.deleteCh <- deleteReq[K]{key: key, expiry: time.Now().Add(-1 * time.Second)}:
_, ok := m.data[key] return m.expire(key)
if ok { default:
delete(m.data, key) // Channel full: drop request; a future Cleanup() or Get will retry.
return true
} }
return ok
} }
// Get gets a value from the DecayMap by key. // Get gets a value from the DecayMap by key.
// //
// If a value has expired, forcibly delete it if it was not updated. // If a value has expired, forcibly delete it if it was not updated.
func (m *Impl[K, V]) Get(key K) (V, bool) { func (m *Impl[K, V]) Get(key K) (V, bool) {
m.lock.RLock() m.lock.Lock()
defer m.lock.Unlock()
value, ok := m.data[key] value, ok := m.data[key]
m.lock.RUnlock()
if !ok { if !ok {
return Zilch[V](), false return Zilch[V](), false
@@ -129,8 +131,8 @@ func (m *Impl[K, V]) Cleanup() {
// Len returns the number of entries in the DecayMap. // Len returns the number of entries in the DecayMap.
func (m *Impl[K, V]) Len() int { func (m *Impl[K, V]) Len() int {
m.lock.RLock() m.lock.Lock()
defer m.lock.RUnlock() defer m.lock.Unlock()
return len(m.data) return len(m.data)
} }
@@ -146,7 +148,7 @@ func (m *Impl[K, V]) Close() {
func (m *Impl[K, V]) cleanupWorker() { func (m *Impl[K, V]) cleanupWorker() {
defer m.wg.Done() defer m.wg.Done()
batch := make([]deleteReq[K], 0, 64) batch := make([]deleteReq[K], 0, 64)
ticker := time.NewTicker(500 * time.Millisecond) ticker := time.NewTicker(15 * time.Minute)
defer ticker.Stop() defer ticker.Stop()
flush := func() { flush := func() {

View File

@@ -30,15 +30,7 @@ func TestImpl(t *testing.T) {
t.Error("got value even though it was supposed to be expired") t.Error("got value even though it was supposed to be expired")
} }
// Deletion of expired entries after Get is deferred to a background worker. dm.Cleanup()
// Assert it eventually disappears from the map.
deadline := time.Now().Add(700 * time.Millisecond)
for time.Now().Before(deadline) {
if dm.Len() == 0 {
break
}
time.Sleep(5 * time.Millisecond)
}
if dm.Len() != 0 { if dm.Len() != 0 {
t.Fatalf("expected background cleanup to remove expired key; len=%d", dm.Len()) t.Fatalf("expected background cleanup to remove expired key; len=%d", dm.Len())
} }

View File

@@ -226,7 +226,7 @@ So far Anubis supports the following languages:
- English (Simplified and Traditional) - English (Simplified and Traditional)
- French - French
- Portuguese (Brazil) - Portugese (Brazil)
- Spanish - Spanish
If you want to contribute translations, please [file an issue](https://github.com/TecharoHQ/anubis/issues/new) with your language of choice or submit a pull request to [the `lib/localization/locales` folder](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). We are about to introduce features to the translation stack, so you may want to hold off a hot minute, but we welcome any and all contributions to making Anubis useful to a global audience. If you want to contribute translations, please [file an issue](https://github.com/TecharoHQ/anubis/issues/new) with your language of choice or submit a pull request to [the `lib/localization/locales` folder](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). We are about to introduce features to the translation stack, so you may want to hold off a hot minute, but we welcome any and all contributions to making Anubis useful to a global audience.

View File

@@ -69,7 +69,7 @@ I am waiting to hear back from NLNet on if Anubis was selected for funding or no
Anubis now supports localized responses. Locales can be added in [lib/localization/locales/](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). This release includes support for the following languages: Anubis now supports localized responses. Locales can be added in [lib/localization/locales/](https://github.com/TecharoHQ/anubis/tree/main/lib/localization/locales). This release includes support for the following languages:
- [Brazilian Portuguese](https://github.com/TecharoHQ/anubis/pull/726) - [Brazilian Portugese](https://github.com/TecharoHQ/anubis/pull/726)
- [Chinese (Simplified)](https://github.com/TecharoHQ/anubis/pull/774) - [Chinese (Simplified)](https://github.com/TecharoHQ/anubis/pull/774)
- [Chinese (Traditional)](https://github.com/TecharoHQ/anubis/pull/759) - [Chinese (Traditional)](https://github.com/TecharoHQ/anubis/pull/759)
- [Czech](https://github.com/TecharoHQ/anubis/pull/849) - [Czech](https://github.com/TecharoHQ/anubis/pull/849)

View File

@@ -1,16 +1,14 @@
import React, { useState, useEffect, useMemo } from "react"; import React, { useState, useEffect, useMemo } from 'react';
import styles from "./styles.module.css"; import styles from './styles.module.css';
// A helper function to perform SHA-256 hashing. // A helper function to perform SHA-256 hashing.
// It takes a string, encodes it, hashes it, and returns a hex string. // It takes a string, encodes it, hashes it, and returns a hex string.
async function sha256(message) { async function sha256(message) {
try { try {
const msgBuffer = new TextEncoder().encode(message); const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return hashHex; return hashHex;
} catch (error) { } catch (error) {
console.error("Hashing failed:", error); console.error("Hashing failed:", error);
@@ -23,42 +21,21 @@ const generateRandomHex = (bytes = 16) => {
const buffer = new Uint8Array(bytes); const buffer = new Uint8Array(bytes);
crypto.getRandomValues(buffer); crypto.getRandomValues(buffer);
return Array.from(buffer) return Array.from(buffer)
.map((byte) => byte.toString(16).padStart(2, "0")) .map(byte => byte.toString(16).padStart(2, '0'))
.join(""); .join('');
}; };
// Icon components for better visual feedback // Icon components for better visual feedback
const CheckIcon = () => ( const CheckIcon = () => (
<svg <svg xmlns="http://www.w3.org/2000/svg" className={styles.iconGreen} fill="none" viewBox="0 0 24 24" stroke="currentColor">
xmlns="http://www.w3.org/2000/svg" <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
className={styles.iconGreen}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg> </svg>
); );
const XCircleIcon = () => ( const XCircleIcon = () => (
<svg <svg xmlns="http://www.w3.org/2000/svg" className={styles.iconRed} fill="none" viewBox="0 0 24 24" stroke="currentColor">
xmlns="http://www.w3.org/2000/svg" <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
className={styles.iconRed}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg> </svg>
); );
@@ -69,7 +46,7 @@ export default function App() {
// State for the nonce, which is the variable we can change // State for the nonce, which is the variable we can change
const [nonce, setNonce] = useState(0); const [nonce, setNonce] = useState(0);
// State to store the resulting hash // State to store the resulting hash
const [hash, setHash] = useState(""); const [hash, setHash] = useState('');
// A flag to indicate if the current hash is the "winning" one // A flag to indicate if the current hash is the "winning" one
const [isMining, setIsMining] = useState(false); const [isMining, setIsMining] = useState(false);
const [isFound, setIsFound] = useState(false); const [isFound, setIsFound] = useState(false);
@@ -78,10 +55,7 @@ export default function App() {
const difficulty = "00"; const difficulty = "00";
// Memoize the combined data to avoid recalculating on every render // Memoize the combined data to avoid recalculating on every render
const combinedData = useMemo( const combinedData = useMemo(() => `${challenge}${nonce}`, [challenge, nonce]);
() => `${challenge}${nonce}`,
[challenge, nonce],
);
// This effect hook recalculates the hash whenever the combinedData changes. // This effect hook recalculates the hash whenever the combinedData changes.
useEffect(() => { useEffect(() => {
@@ -94,9 +68,7 @@ export default function App() {
} }
}; };
calculateHash(); calculateHash();
return () => { return () => { isMounted = false; };
isMounted = false;
};
}, [combinedData, difficulty]); }, [combinedData, difficulty]);
// This effect handles the automatic mining process // This effect handles the automatic mining process
@@ -121,7 +93,7 @@ export default function App() {
// Update the UI periodically to avoid freezing the browser // Update the UI periodically to avoid freezing the browser
if (miningNonce % 100 === 0) { if (miningNonce % 100 === 0) {
setNonce(miningNonce); setNonce(miningNonce);
await new Promise((resolve) => setTimeout(resolve, 0)); // Yield to the browser await new Promise(resolve => setTimeout(resolve, 0)); // Yield to the browser
} }
} }
}; };
@@ -130,27 +102,28 @@ export default function App() {
return () => { return () => {
continueMining = false; continueMining = false;
}; }
}, [isMining, challenge, nonce, difficulty]); }, [isMining, challenge, nonce, difficulty]);
const handleMineClick = () => { const handleMineClick = () => {
setIsMining(true); setIsMining(true);
}; }
const handleStopClick = () => { const handleStopClick = () => {
setIsMining(false); setIsMining(false);
}; }
const handleResetClick = () => { const handleResetClick = () => {
setIsMining(false); setIsMining(false);
setNonce(0); setNonce(0);
}; }
const handleNewChallengeClick = () => { const handleNewChallengeClick = () => {
setIsMining(false); setIsMining(false);
setChallenge(generateRandomHex(16)); setChallenge(generateRandomHex(16));
setNonce(0); setNonce(0);
}; }
// Helper to render the hash with colored leading characters // Helper to render the hash with colored leading characters
const renderHash = () => { const renderHash = () => {
@@ -180,46 +153,12 @@ export default function App() {
<div className={styles.block}> <div className={styles.block}>
<h2 className={styles.blockTitle}>2. Nonce</h2> <h2 className={styles.blockTitle}>2. Nonce</h2>
<div className={styles.nonceControls}> <div className={styles.nonceControls}>
<button <button onClick={() => setNonce(n => n - 1)} disabled={isMining} className={styles.nonceButton}>
onClick={() => setNonce((n) => n - 1)} <svg xmlns="http://www.w3.org/2000/svg" className={styles.iconSmall} fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 12H4" /></svg>
disabled={isMining}
className={styles.nonceButton}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className={styles.iconSmall}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20 12H4"
/>
</svg>
</button> </button>
<span className={styles.nonceValue}>{nonce}</span> <span className={styles.nonceValue}>{nonce}</span>
<button <button onClick={() => setNonce(n => n + 1)} disabled={isMining} className={styles.nonceButton}>
onClick={() => setNonce((n) => n + 1)} <svg xmlns="http://www.w3.org/2000/svg" className={styles.iconSmall} fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
disabled={isMining}
className={styles.nonceButton}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className={styles.iconSmall}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4v16m8-8H4"
/>
</svg>
</button> </button>
</div> </div>
</div> </div>
@@ -233,26 +172,13 @@ export default function App() {
{/* Arrow pointing down */} {/* Arrow pointing down */}
<div className={styles.arrowContainer}> <div className={styles.arrowContainer}>
<svg <svg xmlns="http://www.w3.org/2000/svg" className={styles.iconGray} fill="none" viewBox="0 0 24 24" stroke="currentColor">
xmlns="http://www.w3.org/2000/svg" <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
className={styles.iconGray}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg> </svg>
</div> </div>
{/* Hash Output Block */} {/* Hash Output Block */}
<div <div className={`${styles.hashContainer} ${isFound ? styles.hashContainerSuccess : styles.hashContainerError}`}>
className={`${styles.hashContainer} ${isFound ? styles.hashContainerSuccess : styles.hashContainerError}`}
>
<div className={styles.hashContent}> <div className={styles.hashContent}>
<div className={styles.hashText}> <div className={styles.hashText}>
<h2 className={styles.blockTitle}>4. Resulting Hash (SHA-256)</h2> <h2 className={styles.blockTitle}>4. Resulting Hash (SHA-256)</h2>
@@ -267,30 +193,18 @@ export default function App() {
{/* Mining Controls */} {/* Mining Controls */}
<div className={styles.buttonContainer}> <div className={styles.buttonContainer}>
{!isMining ? ( {!isMining ? (
<button <button onClick={handleMineClick} className={`${styles.button} ${styles.buttonCyan}`}>
onClick={handleMineClick}
className={`${styles.button} ${styles.buttonCyan}`}
>
Auto-Mine Auto-Mine
</button> </button>
) : ( ) : (
<button <button onClick={handleStopClick} className={`${styles.button} ${styles.buttonYellow}`}>
onClick={handleStopClick}
className={`${styles.button} ${styles.buttonYellow}`}
>
Stop Mining Stop Mining
</button> </button>
)} )}
<button <button onClick={handleNewChallengeClick} className={`${styles.button} ${styles.buttonIndigo}`}>
onClick={handleNewChallengeClick}
className={`${styles.button} ${styles.buttonIndigo}`}
>
New Challenge New Challenge
</button> </button>
<button <button onClick={handleResetClick} className={`${styles.button} ${styles.buttonGray}`}>
onClick={handleResetClick}
className={`${styles.button} ${styles.buttonGray}`}
>
Reset Nonce Reset Nonce
</button> </button>
</div> </div>

View File

@@ -48,9 +48,7 @@
background-color: rgb(31 41 55); background-color: rgb(31 41 55);
padding: 1.5rem; padding: 1.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
box-shadow: box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
0 10px 15px -3px rgb(0 0 0 / 0.1),
0 4px 6px -4px rgb(0 0 0 / 0.1);
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -160,9 +158,7 @@
.hashContainer { .hashContainer {
padding: 1.5rem; padding: 1.5rem;
border-radius: 0.5rem; border-radius: 0.5rem;
box-shadow: box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
0 10px 15px -3px rgb(0 0 0 / 0.1),
0 4px 6px -4px rgb(0 0 0 / 0.1);
transition: all 300ms; transition: all 300ms;
border: 2px solid; border: 2px solid;
} }

View File

@@ -11,46 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
- Expose [pprof endpoints](https://pkg.go.dev/net/http/pprof) on the metrics listener to enable profiling Anubis in production. - The memory store now decays values every 15 minutes instead of every 10 milliseconds.
- fix: prevent nil pointer panic in challenge validation when threshold rules match during PassChallenge (#1463) - Add Polish locale ([#1292](https://github.com/TecharoHQ/anubis/pull/1309))
- Instruct reverse proxies to not cache error pages.
- Fixed mixed tab/space indentation in Caddy documentation code block
- Improve error messages and fix broken REDIRECT_DOMAINS link in docs ([#1193](https://github.com/TecharoHQ/anubis/issues/1193))
- Add Bulgarian locale ([#1394](https://github.com/TecharoHQ/anubis/pull/1394))
<!-- This changes the project to: --> <!-- This changes the project to: -->
- Fix CEL internal errors when iterating `headers`/`query` map wrappers by implementing map iterators for `HTTPHeaders` and `URLValues` ([#1465](https://github.com/TecharoHQ/anubis/pull/1465)).
## v1.25.0: Necron
Hey all,
I'm sure you've all been aware that things have been slowing down a little with Anubis development, and I want to apologize for that. A lot has been going on in my life lately (my blog will have a post out on Friday with more information), and as a result I haven't really had the energy to work on Anubis in publicly visible ways. There are things going on behind the scenes, but nothing is really shippable yet, sorry!
I've also been feeling some burnout in the wake of perennial waves of anger directed towards me. I'm handling it, I'll be fine, I've just had a lot going on in my life and it's been rough.
I've been missing the sense of wanderlust and discovery that comes with the artistic way I playfully develop software. I suspect that some of the stresses I've been through (setting up a complicated surgery in a country whose language you aren't fluent in is kind of an experience) have been sapping my energy. I'd gonna try to mess with things on my break, but realistically I'm probably just gonna be either watching Stargate SG-1 or doing unreasonable amounts of ocean fishing in Final Fantasy 14. Normally I'd love to keep the details about my medical state fairly private, but I'm more of a public figure now than I was this time last year so I don't really get the invisibility I'm used to for this.
I've also had a fair amount of negativity directed at me for simply being much more visible than the anonymous threat actors running the scrapers that are ruining everything, which though understandable has not helped.
Anyways, it all worked out and I'm about to be in the hospital for a week, so if things go really badly with this release please downgrade to the last version and/or upgrade to the main branch when the fix PR is inevitably merged. I hoped to have time to tame GPG and set up full release automation in the Anubis repo, but that didn't work out this time and that's okay.
If I can challenge you all to do something, go out there and try to actually create something new somehow. Combine ideas you've never mixed before. Be creative, be human, make something purely for yourself to scratch an itch that you've always had yet never gotten around to actually mending.
At the very least, try to be an example of how you want other people to act, even when you're in a situation where software written by someone else is configured to require a user agent to execute javascript to access a webpage.
Be well,
Xe
PS: if you're well-versed in FFXIV lore, the release title should give you an idea of the kind of stuff I've been going through mentally.
- Add iplist2rule tool that lets admins turn an IP address blocklist into an Anubis ruleset.
- Add Polish locale ([#1292](https://github.com/TecharoHQ/anubis/pull/1309))
- Fix honeypot and imprint links missing `BASE_PREFIX` when deployed behind a path prefix ([#1402](https://github.com/TecharoHQ/anubis/issues/1402))
- Add ANEXIA Sponsor logo to docs ([#1409](https://github.com/TecharoHQ/anubis/pull/1409))
- Improve idle performance in memory storage
- Add HAProxy Configurations to Docs ([#1424](https://github.com/TecharoHQ/anubis/pull/1424))
## v1.24.0: Y'shtola Rhul ## v1.24.0: Y'shtola Rhul

View File

@@ -51,8 +51,9 @@ If you are using Kubernetes, you will need to create an image pull secret:
kubectl create secret docker-registry \ kubectl create secret docker-registry \
techarohq-botstopper \ techarohq-botstopper \
--docker-server ghcr.io \ --docker-server ghcr.io \
--docker-username any-username \ --docker-username your-username \
--docker-password <your-access-token> \ --docker-password your-access-token \
--docker-email your@email.address
``` ```
Then attach it to your Deployment: Then attach it to your Deployment:
@@ -84,7 +85,7 @@ Follow [the upstream Docker compose directions](https://anubis.techaro.lol/docs/
OG_EXPIRY_TIME: "24h" OG_EXPIRY_TIME: "24h"
+ # botstopper config here + # botstopper config here
+ CHALLENGE_TITLE: "Doing math for your connection!" + CHALLENGE_TITLE: "Doing math for your connnection!"
+ ERROR_TITLE: "Something went wrong!" + ERROR_TITLE: "Something went wrong!"
+ OVERLAY_FOLDER: /assets + OVERLAY_FOLDER: /assets
+ volumes: + volumes:

View File

@@ -243,16 +243,16 @@ function regexSafe(input: string): string;
`regexSafe` takes a string and escapes it for safe use inside of a regular expression. This is useful when you are creating regular expressions from headers or variables such as `remoteAddress`. `regexSafe` takes a string and escapes it for safe use inside of a regular expression. This is useful when you are creating regular expressions from headers or variables such as `remoteAddress`.
| Input | Output | | Input | Output |
| :------------------------- | :-------------- | | :------------------------ | :------------------------------ |
| `regexSafe("1.2.3.4")` | `1\\.2\\.3\\.4` | | `regexSafe("1.2.3.4")` | `1\\.2\\.3\\.4` |
| `regexSafe("techaro.lol")` | `techaro\\.lol` | | `regexSafe("techaro.lol")` | `techaro\\.lol` |
| `regexSafe("star*")` | `star\\*` | | `regexSafe("star*")` | `star\\*` |
| `regexSafe("plus+")` | `plus\\+` | | `regexSafe("plus+")` | `plus\\+` |
| `regexSafe("{braces}")` | `\\{braces\\}` | | `regexSafe("{braces}")` | `\\{braces\\}` |
| `regexSafe("start^")` | `start\\^` | | `regexSafe("start^")` | `start\\^` |
| `regexSafe("back\\slash")` | `back\\\\slash` | | `regexSafe("back\\slash")` | `back\\\\slash` |
| `regexSafe("dash-dash")` | `dash\\-dash` | | `regexSafe("dash-dash")` | `dash\\-dash` |
### `segments` ### `segments`
@@ -301,9 +301,9 @@ function arpaReverseIP(ip: string): string;
`arpaReverseIP` takes an IP address and returns its value in [ARPA notation](https://www.ietf.org/rfc/rfc2317.html). This can be useful when matching PTR record patterns. `arpaReverseIP` takes an IP address and returns its value in [ARPA notation](https://www.ietf.org/rfc/rfc2317.html). This can be useful when matching PTR record patterns.
| Input | Output | | Input | Output |
| :----------------------------- | :---------------------------------------------------------------- | | :----------------------------- | :------------------------------------------------------------------- |
| `arpaReverseIP("1.2.3.4")` | `4.3.2.1` | | `arpaReverseIP("1.2.3.4")` | `4.3.2.1` |
| `arpaReverseIP("2001:db8::1")` | `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2` | | `arpaReverseIP("2001:db8::1")` | `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2` |
#### `lookupHost` #### `lookupHost`

View File

@@ -13,8 +13,6 @@ bots:
- # This correlates to data/bots/ai-catchall.yaml in the source tree - # This correlates to data/bots/ai-catchall.yaml in the source tree
import: (data)/bots/ai-catchall.yaml import: (data)/bots/ai-catchall.yaml
- import: (data)/bots/cloudflare-workers.yaml - import: (data)/bots/cloudflare-workers.yaml
# Import all the rules in the default configuration
- import: (data)/meta/default-config.yaml
``` ```
Of note, a bot rule can either have inline bot configuration or import a bot config snippet. You cannot do both in a single bot rule. Of note, a bot rule can either have inline bot configuration or import a bot config snippet. You cannot do both in a single bot rule.
@@ -37,33 +35,6 @@ config.BotOrImport: rule definition is invalid, you must set either bot rules or
Paths can either be prefixed with `(data)` to import from the [the data folder in the Anubis source tree](https://github.com/TecharoHQ/anubis/tree/main/data) or anywhere on the filesystem. If you don't have access to the Anubis source tree, check /usr/share/docs/anubis/data or in the tarball you extracted Anubis from. Paths can either be prefixed with `(data)` to import from the [the data folder in the Anubis source tree](https://github.com/TecharoHQ/anubis/tree/main/data) or anywhere on the filesystem. If you don't have access to the Anubis source tree, check /usr/share/docs/anubis/data or in the tarball you extracted Anubis from.
## Importing the default configuration
If you want to base your configuration off of the default configuration, import `(data)/meta/default-config.yaml`:
```yaml
bots:
- import: (data)/meta/default-config.yaml
# Write your rules here
```
This will keep your configuration up to date as Anubis adapts to emerging threats.
## How do I exempt most modern browsers from Anubis challenges?
If you want to exempt most modern browsers from Anubis challenges, import `(data)/common/acts-like-browser.yaml`:
```yaml
bots:
- import: (data)/meta/default-config.yaml
- import: (data)/common/acts-like-browser.yaml
# Write your rules here
```
These rules will allow traffic that "looks like" it's from a modern copy of Edge, Safari, Chrome, or Firefox. These rules used to be enabled by default, however user reports have suggested that AI scraper bots have adapted to conform to these rules to scrape without regard for the infrastructure they are attacking.
Use these rules at your own risk.
## Importing from imports ## Importing from imports
You can also import from an imported file in case you want to import an entire folder of rules at once. You can also import from an imported file in case you want to import an entire folder of rules at once.

View File

@@ -62,9 +62,9 @@ yourdomain.example.com {
tls your@email.address tls your@email.address
reverse_proxy http://anubis:3000 { reverse_proxy http://anubis:3000 {
header_up X-Real-Ip {remote_host} header_up X-Real-Ip {remote_host}
header_up X-Http-Version {http.request.proto} header_up X-Http-Version {http.request.proto}
} }
} }
``` ```

View File

@@ -1,101 +0,0 @@
# HAProxy
import CodeBlock from "@theme/CodeBlock";
To use Anubis with HAProxy, you have two variants:
- simple - stick Anubis between HAProxy and your application backend (simple)
- perfect if you only have a single application in general
- advanced - force Anubis challenge by default and route to the application backend by HAProxy if the challenge is correct
- useful for complex setups
- routing can be done in HAProxy
- define ACLs in HAProxy for domains, paths etc which are required/excluded regarding Anubis
- HAProxy 3.0 recommended
## Simple Variant
```mermaid
---
title: HAProxy with simple config
---
flowchart LR
T(User Traffic)
HAProxy(HAProxy Port 80/443)
Anubis
Application
T --> HAProxy
HAProxy --> Anubis
Anubis --> |Happy Traffic| Application
```
Your Anubis env file configuration may look like this:
import simpleAnubis from "!!raw-loader!./haproxy/simple-config.env";
<CodeBlock language="bash">{simpleAnubis}</CodeBlock>
The important part is that `TARGET` points to your actual application and if Anubis and HAProxy are on the same machine, a UNIX socket can be used.
Your frontend and backend configuration of HAProxy may look like the following:
import simpleHAProxy from "!!raw-loader!./haproxy/simple-haproxy.cfg";
<CodeBlock language="bash">{simpleHAProxy}</CodeBlock>
This simply enables SSL offloading, sets some useful and required headers and routes to Anubis directly.
## Advanced Variant
Due to the fact that HAProxy can decode JWT, we are able to verify the Anubis token directly in HAProxy and route the traffic to the specific backends ourselves.
Mind that rule logic to allow Git HTTP and other legit bot traffic to bypass is delegated from Anubis to HAProxy then. If required, you should implement any whitelisting in HAProxy using `acl_anubis_ignore` yourself.
In this example are three applications behind one HAProxy frontend. Only App1 and App2 are secured via Anubis; App3 is open for everyone. The path `/excluded/path` can also be accessed by anyone.
```mermaid
---
title: HAProxy with advanced config
---
flowchart LR
T(User Traffic)
HAProxy(HAProxy Port 80/443)
B1(App1)
B2(App2)
B3(App3)
Anubis
T --> HAProxy
HAProxy --> |Traffic for App1 and App2 without valid challenge| Anubis
HAProxy --> |app1.example.com | B1
HAProxy --> |app2.example.com| B2
HAProxy --> |app3.example.com| B3
```
:::note
For an improved JWT decoding performance, it's recommended to use HAProxy version 3.0 or above.
:::
Your Anubis env file configuration may look like this:
import advancedAnubis from "!!raw-loader!./haproxy/advanced-config.env";
<CodeBlock language="bash">{advancedAnubis}</CodeBlock>
It's important to use `HS512_SECRET` which HAProxy understands. Please replace `<SECRET-HERE>` with your own secret string (alphanumerical string with 128 characters recommended).
You can set Anubis to force a challenge for every request using the following policy file:
import advancedAnubisPolicy from "!!raw-loader!./haproxy/advanced-config-policy.yml";
<CodeBlock language="yaml">{advancedAnubisPolicy}</CodeBlock>
The HAProxy config file may look like this:
import advancedHAProxy from "!!raw-loader!./haproxy/advanced-haproxy.cfg";
<CodeBlock language="haproxy">{advancedHAProxy}</CodeBlock>
Please replace `<SECRET-HERE>` with the same secret from the Anubis config.

View File

@@ -1,15 +0,0 @@
# /etc/anubis/challenge-any.yml
bots:
- name: any
action: CHALLENGE
user_agent_regex: .*
status_codes:
CHALLENGE: 403
DENY: 403
thresholds: []
dnsbl: false

View File

@@ -1,11 +0,0 @@
# /etc/anubis/default.env
BIND=/run/anubis/default.sock
BIND_NETWORK=unix
DIFFICULTY=4
METRICS_BIND=:9090
# target is irrelevant here, backend routing happens in HAProxy
TARGET=http://0.0.0.0
HS512_SECRET=<SECRET-HERE>
COOKIE_DYNAMIC_DOMAIN=True
POLICY_FNAME=/etc/anubis/challenge-any.yml

View File

@@ -1,59 +0,0 @@
# /etc/haproxy/haproxy.cfg
frontend FE-multiple-applications
mode http
bind :80
# ssl offloading on port 443 using a certificate from /etc/haproxy/ssl/ directory
bind :443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 ssl-min-ver TLSv1.2 no-tls-tickets
# set X-Real-IP header required for Anubis
http-request set-header X-Real-IP "%[src]"
# redirect HTTP to HTTPS
http-request redirect scheme https code 301 unless { ssl_fc }
# add HSTS header
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# only force Anubis challenge for app1 and app2
acl acl_anubis_required hdr(host) -i "app1.example.com"
acl acl_anubis_required hdr(host) -i "app2.example.com"
# exclude Anubis for a specific path
acl acl_anubis_ignore path /excluded/path
# use Anubis if auth cookie not found
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ req.cook(techaro.lol-anubis-auth) -m found }
# get payload of the JWT such as algorithm, expire time, restrictions
http-request set-var(txn.anubis_jwt_alg) req.cook(techaro.lol-anubis-auth),jwt_header_query('$.alg') if acl_anubis_required !acl_anubis_ignore
http-request set-var(txn.anubis_jwt_exp) cook(techaro.lol-anubis-auth),jwt_payload_query('$.exp','int') if acl_anubis_required !acl_anubis_ignore
http-request set-var(txn.anubis_jwt_res) cook(techaro.lol-anubis-auth),jwt_payload_query('$.restriction') if acl_anubis_required !acl_anubis_ignore
http-request set-var(txn.srcip) req.fhdr(X-Real-IP) if acl_anubis_required !acl_anubis_ignore
http-request set-var(txn.now) date() if acl_anubis_required !acl_anubis_ignore
# use Anubis if JWT has wrong algorithm, is expired, restrictions don't match or isn't signed with the correct key
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ var(txn.anubis_jwt_alg) -m str HS512 }
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore { var(txn.anubis_jwt_exp),sub(txn.now) -m int lt 0 }
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ var(txn.srcip),digest(sha256),hex,lower,strcmp(txn.anubis_jwt_res) eq 0 }
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ cook(techaro.lol-anubis-auth),jwt_verify(txn.anubis_jwt_alg,"<SECRET-HERE>") -m int 1 }
# custom routing in HAProxy
use_backend BE-app1 if { hdr(host) -i "app1.example.com" }
use_backend BE-app2 if { hdr(host) -i "app2.example.com" }
use_backend BE-app3 if { hdr(host) -i "app3.example.com" }
backend BE-app1
mode http
server app1-server 127.0.0.1:3000
backend BE-app2
mode http
server app2-server 127.0.0.1:4000
backend BE-app3
mode http
server app3-server 127.0.0.1:5000
BE-anubis
mode http
server anubis /run/anubis/default.sock

View File

@@ -1,10 +0,0 @@
# /etc/anubis/default.env
BIND=/run/anubis/default.sock
BIND_NETWORK=unix
SOCKET_MODE=0666
DIFFICULTY=4
METRICS_BIND=:9090
COOKIE_DYNAMIC_DOMAIN=true
# address and port of the actual application
TARGET=http://localhost:3000

View File

@@ -1,22 +0,0 @@
# /etc/haproxy/haproxy.cfg
frontend FE-application
mode http
bind :80
# ssl offloading on port 443 using a certificate from /etc/haproxy/ssl/ directory
bind :443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 ssl-min-ver TLSv1.2 no-tls-tickets
# set X-Real-IP header required for Anubis
http-request set-header X-Real-IP "%[src]"
# redirect HTTP to HTTPS
http-request redirect scheme https code 301 unless { ssl_fc }
# add HSTS header
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# route to Anubis backend by default
default_backend BE-anubis-application
BE-anubis-application
mode http
server anubis /run/anubis/default.sock

View File

@@ -94,8 +94,10 @@ containers:
- ALL - ALL
seccompProfile: seccompProfile:
type: RuntimeDefault type: RuntimeDefault
``` ```
Then add a Service entry for Anubis: Then add a Service entry for Anubis:
```yaml ```yaml
@@ -130,52 +132,3 @@ Then point your Ingress to the Anubis port:
# diff-add # diff-add
name: anubis name: anubis
``` ```
## Envoy Gateway
If you are using envoy-gateway, the `X-Real-Ip` header is not set by default, but Anubis does require it. You can resolve this by adding the header, either on the specific `HTTPRoute` where Anubis is listening, or on the `ClientTrafficPolicy` to apply it to any number of Gateways:
HTTPRoute:
```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
hostnames: ["app.domain.tld"]
parentRefs:
- name: envoy-external
namespace: network
sectionName: https
rules:
- backendRefs:
- identifier: *app
port: anubis
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
set:
- name: X-Real-Ip
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
```
Applying to any number of Gateways:
```yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: envoy
spec:
headers:
earlyRequestHeaders:
set:
- name: X-Real-Ip
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
clientIPDetection:
xForwardedFor:
trustedCIDRs:
- 10.96.0.0/16 # Cluster pod CIDR
targetSelectors: # These will apply to all Gateways
- group: gateway.networking.k8s.io
kind: Gateway
```

View File

@@ -1,2 +1,8 @@
# /etc/nginx/conf-anubis.inc # Forward to anubis location / { proxy_set_header # /etc/nginx/conf-anubis.inc
Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://anubis; }
# Forward to anubis
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://anubis;
}

View File

@@ -75,7 +75,7 @@ services:
# Telling Anubis, where to listen for Traefik # Telling Anubis, where to listen for Traefik
- BIND=:8080 - BIND=:8080
# Telling Anubis to do redirect — ensure there is a space after '=' # Telling Anubis to do redirect — ensure there is a space after '='
- "TARGET= " - 'TARGET= '
# Specifies which domains Anubis is allowed to redirect to. # Specifies which domains Anubis is allowed to redirect to.
- REDIRECT_DOMAINS=example.com - REDIRECT_DOMAINS=example.com
# Should be the full external URL for Anubis (including scheme) # Should be the full external URL for Anubis (including scheme)

View File

@@ -67,7 +67,7 @@ Currently the following settings are configurable via the policy file:
Anubis uses these environment variables for configuration: Anubis uses these environment variables for configuration:
| Environment Variable | Default value | Explanation | | Environment Variable | Default value | Explanation |
| :----------------------------- | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |:-------------------------------|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ASSET_LOOKUP_HEADER` | unset | <EO /> If set, use the contents of this header in requests when looking up custom assets in `OVERLAY_FOLDER`. See [Header-based overlay dispatch](./botstopper.mdx#header-based-overlay-dispatch) for more details. | | `ASSET_LOOKUP_HEADER` | unset | <EO /> If set, use the contents of this header in requests when looking up custom assets in `OVERLAY_FOLDER`. See [Header-based overlay dispatch](./botstopper.mdx#header-based-overlay-dispatch) for more details. |
| `BASE_PREFIX` | unset | If set, adds a global prefix to all Anubis endpoints (everything starting with `/.within.website/x/anubis/`). For example, setting this to `/myapp` would make Anubis accessible at `/myapp/` instead of `/`. This is useful when running Anubis behind a reverse proxy that routes based on path prefixes. | | `BASE_PREFIX` | unset | If set, adds a global prefix to all Anubis endpoints (everything starting with `/.within.website/x/anubis/`). For example, setting this to `/myapp` would make Anubis accessible at `/myapp/` instead of `/`. This is useful when running Anubis behind a reverse proxy that routes based on path prefixes. |
| `BIND` | `:8923` | The network address that Anubis listens on. For `unix`, set this to a path: `/run/anubis/instance.sock` | | `BIND` | `:8923` | The network address that Anubis listens on. For `unix`, set this to a path: `/run/anubis/instance.sock` |
@@ -95,7 +95,7 @@ Anubis uses these environment variables for configuration:
| `OVERLAY_FOLDER` | unset | <EO /> If set, treat the given path as an [overlay folder](./botstopper.mdx#custom-images-and-css), allowing you to customize CSS, fonts, images, and add other assets to BotStopper deployments. | | `OVERLAY_FOLDER` | unset | <EO /> If set, treat the given path as an [overlay folder](./botstopper.mdx#custom-images-and-css), allowing you to customize CSS, fonts, images, and add other assets to BotStopper deployments. |
| `POLICY_FNAME` | unset | The file containing [bot policy configuration](./policies.mdx). See the bot policy documentation for more details. If unset, the default bot policy configuration is used. | | `POLICY_FNAME` | unset | The file containing [bot policy configuration](./policies.mdx). See the bot policy documentation for more details. If unset, the default bot policy configuration is used. |
| `PUBLIC_URL` | unset | The externally accessible URL for this Anubis instance, used for constructing redirect URLs (e.g., for Traefik forwardAuth). Leave it unset when Anubis terminates traffic directly (sidecar/standalone deployments) or redirect building will fail with `redir=null`. | | `PUBLIC_URL` | unset | The externally accessible URL for this Anubis instance, used for constructing redirect URLs (e.g., for Traefik forwardAuth). Leave it unset when Anubis terminates traffic directly (sidecar/standalone deployments) or redirect building will fail with `redir=null`. |
| `REDIRECT_DOMAINS` | unset | Comma-separated list of domain names that Anubis should allow redirects to when passing a challenge. See [Redirect Domain Configuration](./configuration/redirect-domains.mdx) for more details. | | `REDIRECT_DOMAINS` | unset | Comma-separated list of domain names that Anubis should allow redirects to when passing a challenge. See [Redirect Domain Configuration](./configuration/redirect-domains) for more details. |
| `SERVE_ROBOTS_TXT` | `false` | If set `true`, Anubis will serve a default `robots.txt` file that disallows all known AI scrapers by name and then additionally disallows every scraper. This is useful if facts and circumstances make it difficult to change the underlying service to serve such a `robots.txt` file. | | `SERVE_ROBOTS_TXT` | `false` | If set `true`, Anubis will serve a default `robots.txt` file that disallows all known AI scrapers by name and then additionally disallows every scraper. This is useful if facts and circumstances make it difficult to change the underlying service to serve such a `robots.txt` file. |
| `SLOG_LEVEL` | `INFO` | The log level for structured logging. Valid values are `DEBUG`, `INFO`, `WARN`, and `ERROR`. Set to `DEBUG` to see all requests, evaluations, and detailed diagnostic information. | | `SLOG_LEVEL` | `INFO` | The log level for structured logging. Valid values are `DEBUG`, `INFO`, `WARN`, and `ERROR`. Set to `DEBUG` to see all requests, evaluations, and detailed diagnostic information. |
| `SOCKET_MODE` | `0770` | _Only used when at least one of the `*_BIND_NETWORK` variables are set to `unix`._ The socket mode (permissions) for Unix domain sockets. | | `SOCKET_MODE` | `0770` | _Only used when at least one of the `*_BIND_NETWORK` variables are set to `unix`._ The socket mode (permissions) for Unix domain sockets. |
@@ -123,7 +123,7 @@ If you don't know or understand what these settings mean, ignore them. These are
| `TARGET_DISABLE_KEEPALIVE` | `false` | If `true`, disables HTTP keep-alive for connections to the target backend. Useful for backends that don't handle keep-alive properly. | | `TARGET_DISABLE_KEEPALIVE` | `false` | If `true`, disables HTTP keep-alive for connections to the target backend. Useful for backends that don't handle keep-alive properly. |
| `TARGET_HOST` | unset | If set, overrides the Host header in requests forwarded to `TARGET`. | | `TARGET_HOST` | unset | If set, overrides the Host header in requests forwarded to `TARGET`. |
| `TARGET_INSECURE_SKIP_VERIFY` | `false` | If `true`, skip TLS certificate validation for targets that listen over `https`. If your backend does not listen over `https`, ignore this setting. | | `TARGET_INSECURE_SKIP_VERIFY` | `false` | If `true`, skip TLS certificate validation for targets that listen over `https`. If your backend does not listen over `https`, ignore this setting. |
| `TARGET_SNI` | unset | If set, TLS handshake hostname when forwarding requests to the `TARGET`. If set to auto, use Host header. | | `TARGET_SNI` | unset | If set, TLS handshake hostname when forwarding requests to the `TARGET`. If set to auto, use Host header. |
</details> </details>
@@ -203,7 +203,6 @@ To get Anubis filtering your traffic, you need to make sure it's added to your H
- [Kubernetes](./environments/kubernetes.mdx) - [Kubernetes](./environments/kubernetes.mdx)
- [Nginx](./environments/nginx.mdx) - [Nginx](./environments/nginx.mdx)
- [Traefik](./environments/traefik.mdx) - [Traefik](./environments/traefik.mdx)
- [HAProxy](./environments/haproxy.mdx)
:::note :::note

View File

@@ -1,50 +0,0 @@
---
title: iplist2rule CLI tool
---
The `iplist2rule` tool converts IP blocklists into Anubis challenge policies. It reads common IP block list formats and generates the appropriate Anubis policy file for IP address filtering.
## Installation
Install directly with Go
```bash
go install github.com/TecharoHQ/anubis/utils/cmd/iplist2rule@latest
```
## Usage
Basic conversion from URL:
```bash
iplist2rule https://raw.githubusercontent.com/7c/torfilter/refs/heads/main/lists/txt/torfilter-1m-flat.txt filter-tor.yaml
```
Explicitly allow every IP address on a list:
```bash
iplist2rule --action ALLOW https://raw.githubusercontent.com/7c/torfilter/refs/heads/main/lists/txt/torfilter-1m-flat.txt filter-tor.yaml
```
Add weight to requests matching IP addresses on a list:
```bash
iplist2rule --action WEIGH --weight 20 https://raw.githubusercontent.com/7c/torfilter/refs/heads/main/lists/txt/torfilter-1m-flat.txt filter-tor.yaml
```
## Options
| Flag | Description | Default |
| :------------ | :----------------------------------------------------------------------------------------------- | :-------------------------------- |
| `--action` | The Anubis action to take for the IP address in question, must be in ALL CAPS. | `DENY` (forbids traffic) |
| `--rule-name` | The name for the generated Anubis rule, should be in kebab-case. | (not set, inferred from filename) |
| `--weight` | When `--action=WEIGH`, how many weight points should be added or removed from matching requests? | 0 (not set) |
## Using the Generated Policy
Save the output and import it in your main policy file:
```yaml
bots:
- import: "./filter-tor.yaml"
```

View File

@@ -143,4 +143,3 @@ For more details on particular reverse proxies, see here:
- [Apache](./environments/apache.mdx) - [Apache](./environments/apache.mdx)
- [Nginx](./environments/nginx.mdx) - [Nginx](./environments/nginx.mdx)
- [HAProxy](./environments/haproxy.mdx)

View File

@@ -393,32 +393,6 @@ logging:
When files are rotated out, the old files will be named after the rotation timestamp in [RFC 3339 format](https://www.rfc-editor.org/rfc/rfc3339). When files are rotated out, the old files will be named after the rotation timestamp in [RFC 3339 format](https://www.rfc-editor.org/rfc/rfc3339).
:::note
If you are running Anubis in systemd via a native package, the default systemd unit settings are very restrictive and will forbid writing to folders in `/var/log`. In order to fix this, please make a [drop-in unit](https://www.flatcar.org/docs/latest/setup/systemd/drop-in-units/) like the following:
```text
# /etc/systemd/anubis@instance-name.service.d/50-var-log-readwrite.conf
[Service]
ReadWritePaths=/run /var/log/anubis
```
Once you write this to the correct place, reload the systemd configuration:
```text
sudo systemctl daemon-reload
```
And then restart Anubis:
```text
sudo systemctl restart anubis@instance-name
```
You may be required to make drop-ins for each Anubis instance depending on the facts and circumstances of your deployment.
:::
### `stdio` sink ### `stdio` sink
By default, Anubis logs everything to the standard error stream of its process. This requires no configuration: By default, Anubis logs everything to the standard error stream of its process. This requires no configuration:

View File

@@ -12,7 +12,6 @@ Install directly with Go:
```bash ```bash
go install github.com/TecharoHQ/anubis/cmd/robots2policy@latest go install github.com/TecharoHQ/anubis/cmd/robots2policy@latest
``` ```
## Usage ## Usage
Basic conversion from URL: Basic conversion from URL:
@@ -36,8 +35,8 @@ robots2policy -input robots.txt -action DENY -format json
## Options ## Options
| Flag | Description | Default | | Flag | Description | Default |
| --------------------- | ------------------------------------------------------------------ | ------------------- | |-----------------------|--------------------------------------------------------------------|---------------------|
| `-input` | robots.txt file path or URL (use `-` for stdin) | _required_ | | `-input` | robots.txt file path or URL (use `-` for stdin) | *required* |
| `-output` | Output file (use `-` for stdout) | stdout | | `-output` | Output file (use `-` for stdout) | stdout |
| `-format` | Output format: `yaml` or `json` | `yaml` | | `-format` | Output format: `yaml` or `json` | `yaml` |
| `-action` | Action for disallowed paths: `ALLOW`, `DENY`, `CHALLENGE`, `WEIGH` | `CHALLENGE` | | `-action` | Action for disallowed paths: `ALLOW`, `DENY`, `CHALLENGE`, `WEIGH` | `CHALLENGE` |
@@ -48,7 +47,6 @@ robots2policy -input robots.txt -action DENY -format json
## Example ## Example
Input robots.txt: Input robots.txt:
```txt ```txt
User-agent: * User-agent: *
Disallow: /admin/ Disallow: /admin/
@@ -59,7 +57,6 @@ Disallow: /
``` ```
Generated policy: Generated policy:
```yaml ```yaml
- name: robots-txt-policy-disallow-1 - name: robots-txt-policy-disallow-1
action: CHALLENGE action: CHALLENGE
@@ -80,8 +77,8 @@ Generated policy:
Save the output and import it in your main policy file: Save the output and import it in your main policy file:
```yaml ```yaml
bots: import:
- import: "./robots-policy.yaml" - path: "./robots-policy.yaml"
``` ```
The tool handles wildcard patterns, user-agent specific rules, and blacklisted bots automatically. The tool handles wildcard patterns, user-agent specific rules, and blacklisted bots automatically.

View File

@@ -1,13 +0,0 @@
# AI Coding Policy
At some level it would be nice to be able to have the following AI coding policy from an ideological standpoint:
> Anubis does not accept code made primarily with the use of agentic AI tools such as Claude Code, Gemini CLI, GitHub Copilot, Zed, OpenCode, or any other similar tools. Please do not use them when contributing to this repo.
However, I'd be in violation by doing this because I have knowingly committed minor bits of code to the Anubis repo that were generated by AI tools (mostly things for smoke tests).
As such, Anubis is taking more of a centrist approach with regards to AI coding tools: regardless of what tool you use to make contributions to Anubis, when you sign off your code, you are taking responsibility for what you commit. You are also expected to understand what you are changing, what the implications are, and all other relevant factors.
If you use AI coding tools for a majority of your committed work, you MUST disclose it with [the `Assisted-by` footer](https://xeiaso.net/notes/2025/assisted-by-footer/). The Anubis maintainers will be using tooling that looks for these footers and will prioritize scrutiny and level of attention appropriately.
In order to ensure compliance with this policy, language has been placed in `AGENTS.md` and `CLAUDE.md` to entice AI coding tools to add these footers.

View File

@@ -0,0 +1,31 @@
---
title: Code quality guidelines
---
When submitting code to Anubis, please take the time to consider the fact that this project is security software. If things go bad, bots can pummel sites into oblivion. This is not ideal for uptime.
As such, code reviews will be a bit more strict than you have seen in other projects. This is not people trying to be mean, this is a side effect of taking the problem seriously.
When making code changes, try to do the following:
- If you're submitting a bugfix, add a test case for it
- If you're changing the JavaScript, make sure the integration tests pass (`npm run test:integration`)
## Commit messages
Anubis follows the Go project's conventions for commit messages. In general, an ideal commit message should read like this:
```text
path/to/folder: brief description of the change
If the change is subtle, has implementation consequences, or is otherwise
not entirely self-describing: take the time to spell out why. If things
are very subtle, please also amend the documentation accordingly
```
The subject of a commit message should be the second half of the sentence "This commit changes the Anubis project to:". Here's a few examples:
- `disable DroneBL by default`
- `port the challenge to WebAssembly`
The extended commit message is also your place to give rationale for a new feature. When maintainers are reviewing your code, they will use this to figure out if the burden from feature maintainership is worth the merge.

Some files were not shown because too many files have changed in this diff Show More