mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-06 00:38:18 +00:00
Compare commits
32 Commits
wasm
...
Xe/pkgserv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ff17954d3 | ||
|
|
bd1835aa98 | ||
|
|
b15017d097 | ||
|
|
2d22491e8c | ||
|
|
150523b9d3 | ||
|
|
6f652e711c | ||
|
|
75b97eb03d | ||
|
|
f5827721c3 | ||
|
|
a40c5e99fc | ||
|
|
af831f0d7f | ||
|
|
095e18d0c8 | ||
|
|
f844dba3dc | ||
|
|
736c3ade09 | ||
|
|
b20774d9a6 | ||
|
|
2c94090fde | ||
|
|
df3509ec99 | ||
|
|
8689143214 | ||
|
|
5d4d2e3e2a | ||
|
|
2ebce26709 | ||
|
|
ac273a8ad5 | ||
|
|
9865e3ded8 | ||
|
|
3438595f32 | ||
|
|
62e20a213a | ||
|
|
f2cb6ae121 | ||
|
|
92dbc22db0 | ||
|
|
971e781965 | ||
|
|
503f466ecf | ||
|
|
81307bcb5c | ||
|
|
40d7b2ec55 | ||
|
|
20f1d40b61 | ||
|
|
51bd058f2d | ||
|
|
1614504922 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
||||
patreon: cadey
|
||||
patreon: cadey
|
||||
github: xe
|
||||
10
.github/workflows/docker-pr.yml
vendored
10
.github/workflows/docker-pr.yml
vendored
@@ -12,10 +12,10 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
@@ -45,11 +45,9 @@ jobs:
|
||||
run: |
|
||||
brew bundle
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ghcr.io/techarohq/anubis
|
||||
|
||||
|
||||
16
.github/workflows/docker.yml
vendored
16
.github/workflows/docker.yml
vendored
@@ -18,10 +18,10 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
@@ -50,11 +50,9 @@ jobs:
|
||||
- name: Install Brew dependencies
|
||||
run: |
|
||||
brew bundle
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: techarohq
|
||||
@@ -62,7 +60,7 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ghcr.io/techarohq/anubis
|
||||
|
||||
@@ -76,8 +74,8 @@ jobs:
|
||||
SLOG_LEVEL: debug
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v2
|
||||
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
|
||||
with:
|
||||
subject-name: ghcr.io/techarohq/anubis
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: true
|
||||
push-to-registry: true
|
||||
|
||||
16
.github/workflows/docs-deploy.yml
vendored
16
.github/workflows/docs-deploy.yml
vendored
@@ -13,18 +13,18 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: techarohq
|
||||
@@ -32,13 +32,13 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: ghcr.io/techarohq/anubis/docs
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
||||
with:
|
||||
context: ./docs
|
||||
cache-to: type=gha
|
||||
@@ -49,14 +49,14 @@ jobs:
|
||||
push: true
|
||||
|
||||
- name: Apply k8s manifests to aeacus
|
||||
uses: actions-hub/kubectl@master
|
||||
uses: actions-hub/kubectl@9270913c29699788b51bc04becd0ebdf048ffb49 # v1.32.3
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.AEACUS_KUBECONFIG }}
|
||||
with:
|
||||
args: apply -k docs/manifest
|
||||
|
||||
- name: Apply k8s manifests to aeacus
|
||||
uses: actions-hub/kubectl@master
|
||||
uses: actions-hub/kubectl@9270913c29699788b51bc04becd0ebdf048ffb49 # v1.32.3
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.AEACUS_KUBECONFIG }}
|
||||
with:
|
||||
|
||||
18
.github/workflows/go.yml
vendored
18
.github/workflows/go.yml
vendored
@@ -13,9 +13,9 @@ permissions:
|
||||
jobs:
|
||||
go_tests:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
@@ -48,10 +48,8 @@ jobs:
|
||||
run: |
|
||||
brew bundle
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
|
||||
- name: Setup Golang caches
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
@@ -61,7 +59,7 @@ jobs:
|
||||
${{ runner.os }}-golang-
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: |
|
||||
@@ -70,8 +68,8 @@ jobs:
|
||||
|
||||
- name: install playwright browsers
|
||||
run: |
|
||||
npx --yes playwright@1.50.1 install --with-deps
|
||||
npx --yes playwright@1.50.1 run-server --port 9001 &
|
||||
npx --yes playwright@1.51.1 install --with-deps
|
||||
npx --yes playwright@1.51.1 run-server --port 9001 &
|
||||
|
||||
- name: install node deps
|
||||
run: |
|
||||
@@ -84,6 +82,6 @@ jobs:
|
||||
- name: Test
|
||||
run: npm run test
|
||||
|
||||
- uses: dominikh/staticcheck-action@v1
|
||||
- uses: dominikh/staticcheck-action@fe1dd0c3658873b46f8c9bb3291096a617310ca6 # v1.3.1
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
10
.github/workflows/package-builds-stable.yml
vendored
10
.github/workflows/package-builds-stable.yml
vendored
@@ -11,9 +11,9 @@ permissions:
|
||||
jobs:
|
||||
package_builds:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-tags: true
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
brew bundle
|
||||
|
||||
- name: Setup Golang caches
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
@@ -78,4 +78,4 @@ jobs:
|
||||
cd var
|
||||
for file in *; do
|
||||
gh release upload $RELEASE $file
|
||||
done
|
||||
done
|
||||
|
||||
12
.github/workflows/package-builds-unstable.yml
vendored
12
.github/workflows/package-builds-unstable.yml
vendored
@@ -13,9 +13,9 @@ permissions:
|
||||
jobs:
|
||||
package_builds:
|
||||
#runs-on: alrest-techarohq
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-tags: true
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Setup Homebrew cellar cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
/home/linuxbrew/.linuxbrew/Cellar
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
brew bundle
|
||||
|
||||
- name: Setup Golang caches
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
sudo apt -y install -f ./var/yeet.deb
|
||||
yeet
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: packages
|
||||
path: var/*
|
||||
path: var/*
|
||||
|
||||
8
.github/workflows/zizmor.yml
vendored
8
.github/workflows/zizmor.yml
vendored
@@ -11,17 +11,17 @@ on:
|
||||
jobs:
|
||||
zizmor:
|
||||
name: zizmor latest via PyPI
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@0c5e2b8115b80b4c7c5ddf6ffdd634974642d182 # v5.4.1
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -20,7 +20,3 @@ node_modules
|
||||
|
||||
# how does this get here
|
||||
doc/VERSION
|
||||
|
||||
*.wasm
|
||||
|
||||
target
|
||||
482
Cargo.lock
generated
482
Cargo.lock
generated
@@ -1,482 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anubis"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"wee_alloc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2id"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anubis",
|
||||
"argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
<<<<<<< HEAD
|
||||
name = "block-buffer"
|
||||
version = "0.11.0-rc.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a229bfd78e4827c91b9b95784f69492c1b77c1ab75a45a8a037b139215086f94"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
=======
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
>>>>>>> 8793853 (feat(wasm): broken equi-x solver)
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "170d71b5b14dec99db7739f6fc7d6ec2db80b78c3acb77db48392ccc3d8a9ea0"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common 0.1.6",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
<<<<<<< HEAD
|
||||
name = "digest"
|
||||
version = "0.11.0-pre.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c478574b20020306f98d61c8ca3322d762e1ff08117422ac6106438605ea516"
|
||||
dependencies = [
|
||||
"block-buffer 0.11.0-rc.4",
|
||||
"const-oid",
|
||||
"crypto-common 0.2.0-rc.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
=======
|
||||
name = "dynasm"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0cecff24995c8a5a3c3169cff4c733fe7d91aedf5d8cc96238738bfe53186b8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dynasmrt"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f5eab96b8688bcbf1d2354bcfe0261005ac1dd0616747152ada34948d4e9582"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dynasm",
|
||||
"fnv",
|
||||
"memmap2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equix"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anubis",
|
||||
"equix 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equix"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194df1f219a987430956f20faaf702fd4d434b1b2f7300014119854184107ac7"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"hashx",
|
||||
"num-traits",
|
||||
"thiserror",
|
||||
"visibility",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed-capacity-vec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b31a14f5ee08ed1a40e1252b35af18bed062e3f39b69aab34decde36bc43e40"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
>>>>>>> 8793853 (feat(wasm): broken equi-x solver)
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
<<<<<<< HEAD
|
||||
name = "hybrid-array"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dab50e193aebe510fe0e40230145820e02f48dae0cf339ea4204e6e708ff7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
=======
|
||||
name = "hashx"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572a61c460658ae7db71878dd2caa163f47ffe041cb40aeee1483d1ffbf5e84b"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"blake2",
|
||||
"dynasmrt",
|
||||
"fixed-capacity-vec",
|
||||
"hex",
|
||||
"rand_core 0.9.3",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
>>>>>>> 8793853 (feat(wasm): broken equi-x solver)
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
<<<<<<< HEAD
|
||||
name = "memory_units"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||
=======
|
||||
name = "memmap2"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
>>>>>>> 8793853 (feat(wasm): broken equi-x solver)
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.11.0-pre.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b4241d1a56954dce82cecda5c8e9c794eef6f53abe5e5216bac0a0ea71ffa7"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest 0.11.0-pre.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha256"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anubis",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
<<<<<<< HEAD
|
||||
name = "wee_alloc"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"memory_units",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
=======
|
||||
name = "visibility"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
>>>>>>> 8793853 (feat(wasm): broken equi-x solver)
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -1,10 +0,0 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["wasm/anubis", "wasm/pow/*"]
|
||||
|
||||
[profile.release]
|
||||
#strip = true
|
||||
opt-level = "s"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
50
Makefile
50
Makefile
@@ -1,33 +1,31 @@
|
||||
NODE_MODULES = node_modules
|
||||
VERSION := $(shell cat ./VERSION)
|
||||
VERSION= $(shell cat ./VERSION)
|
||||
GO?= go
|
||||
NPM?= npm
|
||||
|
||||
export RUSTFLAGS=-Ctarget-feature=+simd128
|
||||
|
||||
.PHONY: build assets deps lint prebaked-build test wasm
|
||||
|
||||
assets:
|
||||
npm run assets
|
||||
|
||||
deps:
|
||||
npm ci
|
||||
go mod download
|
||||
|
||||
build: deps
|
||||
npm run build
|
||||
@echo "Anubis is now built to ./var/anubis"
|
||||
.PHONY: build assets deps lint prebaked-build test
|
||||
|
||||
all: build
|
||||
|
||||
lint:
|
||||
go vet ./...
|
||||
go tool staticcheck ./...
|
||||
deps:
|
||||
$(NPM) ci
|
||||
$(GO) mod download
|
||||
|
||||
assets: PATH:=$(PWD)/node_modules/.bin:$(PATH)
|
||||
assets: deps
|
||||
$(GO) generate ./...
|
||||
./web/build.sh
|
||||
./xess/build.sh
|
||||
|
||||
build: assets
|
||||
$(GO) build -o ./var/anubis ./cmd/anubis
|
||||
@echo "Anubis is now built to ./var/anubis"
|
||||
|
||||
lint: assets
|
||||
$(GO) vet ./...
|
||||
$(GO) tool staticcheck ./...
|
||||
|
||||
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
|
||||
|
||||
test:
|
||||
npm run test
|
||||
|
||||
wasm:
|
||||
cargo build --release --target wasm32-unknown-unknown
|
||||
cp -vf ./target/wasm32-unknown-unknown/release/*.wasm ./web/static/wasm
|
||||
test: assets
|
||||
$(GO) test ./...
|
||||
|
||||
@@ -16,4 +16,4 @@ const StaticPath = "/.within.website/x/cmd/anubis/"
|
||||
|
||||
// DefaultDifficulty is the default "difficulty" (number of leading zeroes)
|
||||
// that must be met by the client in order to pass the challenge.
|
||||
const DefaultDifficulty uint32 = 4
|
||||
const DefaultDifficulty = 4
|
||||
|
||||
@@ -40,7 +40,7 @@ import (
|
||||
var (
|
||||
bind = flag.String("bind", ":8923", "network address to bind HTTP to")
|
||||
bindNetwork = flag.String("bind-network", "tcp", "network family to bind HTTP to, e.g. unix, tcp")
|
||||
challengeDifficulty = flag.Int("difficulty", int(anubis.DefaultDifficulty), "difficulty of the challenge")
|
||||
challengeDifficulty = flag.Int("difficulty", anubis.DefaultDifficulty, "difficulty of the challenge")
|
||||
cookieDomain = flag.String("cookie-domain", "", "if set, the top-level domain that the Anubis cookie will be valid for")
|
||||
cookiePartitioned = flag.Bool("cookie-partitioned", false, "if true, sets the partitioned flag on Anubis cookies, enabling CHIPS support")
|
||||
ed25519PrivateKeyHex = flag.String("ed25519-private-key-hex", "", "private key used to sign JWTs, if not set a random one will be assigned")
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
ogPassthrough = flag.Bool("og-passthrough", false, "enable Open Graph tag passthrough")
|
||||
ogTimeToLive = flag.Duration("og-expiry-time", 24*time.Hour, "Open Graph tag cache expiration time")
|
||||
extractResources = flag.String("extract-resources", "", "if set, extract the static resources to the specified folder")
|
||||
webmasterEmail = flag.String("webmaster-email", "", "if set, displays webmaster's email on the reject page for appeals")
|
||||
webmasterEmail = flag.String("webmaster-email", "", "if set, displays webmaster's email on the reject page for appeals")
|
||||
)
|
||||
|
||||
func keyFromHex(value string) (ed25519.PrivateKey, error) {
|
||||
@@ -194,7 +194,7 @@ func main() {
|
||||
log.Fatalf("can't make reverse proxy: %v", err)
|
||||
}
|
||||
|
||||
policy, err := libanubis.LoadPoliciesOrDefault(*policyFname, uint32(*challengeDifficulty))
|
||||
policy, err := libanubis.LoadPoliciesOrDefault(*policyFname, *challengeDifficulty)
|
||||
if err != nil {
|
||||
log.Fatalf("can't parse policy file: %v", err)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func main() {
|
||||
OGPassthrough: *ogPassthrough,
|
||||
OGTimeToLive: *ogTimeToLive,
|
||||
Target: *target,
|
||||
WebmasterEmail: *webmasterEmail,
|
||||
WebmasterEmail: *webmasterEmail,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("can't construct libanubis.Server: %v", err)
|
||||
|
||||
@@ -123,10 +123,10 @@ func parseImageList(imageList string) ([]image, error) {
|
||||
// reg.xeiaso.net/techaro/anubis:latest
|
||||
// repository: reg.xeiaso.net/techaro/anubis
|
||||
// tag: latest
|
||||
parts := strings.SplitN(img, ":", 2)
|
||||
index := strings.LastIndex(img, ":")
|
||||
result = append(result, image{
|
||||
repository: parts[0],
|
||||
tag: parts[1],
|
||||
repository: img[:index],
|
||||
tag: img[index+1:],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
14
cryptography/techaro-pkgs.pub.asc
Normal file
14
cryptography/techaro-pkgs.pub.asc
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaAU/hRYJKwYBBAHaRw8BAQdANg5d753IR7Q3NxfE+vVgpBx9w66HSzx74zEz
|
||||
2djVqZm0NlRlY2hhcm8gUGFja2FnZXMgU2lnbmF0dXJlcyA8Z3BnK3BhY2thZ2Vz
|
||||
QHRlY2hhcm8ubG9sPoiZBBMWCgBBFiEEgz9kFhFntQEFjDlHVjddot8Cq/8FAmgF
|
||||
P4UCGwMFCRLMAwAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQVjddot8C
|
||||
q/8PYwEAk1B1r8cBZi54fTOpMnkKUJnbk9wnmNLdUkRADlPRCcIBAN3Zgsjb/SXw
|
||||
nWINVPKvodwUVanDwl5uZuXtROv1mQ8GuDgEaAU/hRIKKwYBBAGXVQEFAQEHQLqY
|
||||
3RXQiv/4d7y+eGs+YE4BymAguYG44jRtHHwFERZEAwEIB4h+BBgWCgAmFiEEgz9k
|
||||
FhFntQEFjDlHVjddot8Cq/8FAmgFP4UCGwwFCRLMAwAACgkQVjddot8Cq/8WXgEA
|
||||
lMjj013kuKsSCrczDxCSH0boW6xSTfyjnC2SQ4VDj78A/i6JrmkMq0wUSevgxlGw
|
||||
ZIoXIS4aVdSnfY9LMcnklKcL
|
||||
=zIdk
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
14
cryptography/techaro-root.pub.asc
Normal file
14
cryptography/techaro-root.pub.asc
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaAU+KxYJKwYBBAHaRw8BAQdAroyz8ysjSTBcQgfN+StN6WAfNzPDwiTF1LvS
|
||||
bVRSu0a0KlRlY2hhcm8gUm9vdCBTaWduaW5nIEtleSA8Z3BnQHRlY2hhcm8ubG9s
|
||||
PoiZBBMWCgBBFiEEia0uWmNauBlpQAXmU41NIwnMwuwFAmgFPisCGwMFCRLMAwAF
|
||||
CwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQU41NIwnMwuxKFQD/ZovOrWC3
|
||||
DKcrYYKpCrX30iWDd+U2hqlPFFvRKhFx52UA+gO+UYeCBy/dnxc0GrNPE84yWQqr
|
||||
9eas/yEf1rsMInIEuDgEaAU+KxIKKwYBBAGXVQEFAQEHQAaSsoWAStlNTHgN9xuW
|
||||
aVsxZ9DQzPlD2osWHuvXCwknAwEIB4h+BBgWCgAmFiEEia0uWmNauBlpQAXmU41N
|
||||
IwnMwuwFAmgFPisCGwwFCRLMAwAACgkQU41NIwnMwuxs+AEAijEEHvssBYt80YZW
|
||||
/jCrp3vuD6aTFzb5NzvdQafPH5AA/0dt5ayS/vu31z2YTfSg5WNGWKvOvyGAG6jz
|
||||
TS5tWMYJ
|
||||
=uI7D
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
27
cryptography/yeetfile.js
Normal file
27
cryptography/yeetfile.js
Normal file
@@ -0,0 +1,27 @@
|
||||
rpm.build({
|
||||
name: "techaro-repo-keys",
|
||||
description: "Public keys for techaro.lol RPM packages",
|
||||
homepage: "https://techaro.lol",
|
||||
license: "MIT",
|
||||
goarch: "all",
|
||||
|
||||
build: (out) => {
|
||||
yeet.run(`mkdir`, `-p`, `${out}/etc/pki/rpm-gpg/techaro.lol-keys`);
|
||||
file.install("./techaro-pkgs.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-pkgs.asc`);
|
||||
file.install("./techaro-root.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-root.asc`);
|
||||
},
|
||||
});
|
||||
|
||||
deb.build({
|
||||
name: "techaro-repo-keys",
|
||||
description: "Public keys for techaro.lol RPM packages",
|
||||
homepage: "https://techaro.lol",
|
||||
license: "MIT",
|
||||
goarch: "all",
|
||||
|
||||
build: (out) => {
|
||||
yeet.run(`mkdir`, `-p`, `${out}/usr/share/keyrings`);
|
||||
file.install("./techaro-pkgs.pub.asc", `${out}/usr/share/keyrings/techaro-pkgs.asc`);
|
||||
file.install("./techaro-root.pub.asc", `${out}/usr/share/keyrings/techaro-root.asc`);
|
||||
},
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"bots": [
|
||||
{
|
||||
"name": "amazonbot",
|
||||
"user_agent_regex": "Amazonbot",
|
||||
"name": "ai-robots-txt",
|
||||
"user_agent_regex": "AI2Bot|Ai2Bot-Dolma|Amazonbot|anthropic-ai|Applebot|Applebot-Extended|Brightbot 1.0|Bytespider|CCBot|ChatGPT-User|Claude-Web|ClaudeBot|cohere-ai|cohere-training-data-crawler|Crawlspace|Diffbot|DuckAssistBot|FacebookBot|FriendlyCrawler|Google-Extended|GoogleOther|GoogleOther-Image|GoogleOther-Video|GPTBot|iaskspider/2.0|ICC-Crawler|ImagesiftBot|img2dataset|ISSCyberRiskCrawler|Kangaroo Bot|Meta-ExternalAgent|Meta-ExternalFetcher|OAI-SearchBot|omgili|omgilibot|PanguBot|Perplexity-User|PerplexityBot|PetalBot|Scrapy|SemrushBot-OCOB|SemrushBot-SWA|Sidetrade indexer bot|Timpibot|VelenPublicWebCrawler|Webzio-Extended|YouBot",
|
||||
"action": "DENY"
|
||||
},
|
||||
{
|
||||
@@ -304,6 +304,282 @@
|
||||
"40.77.178.0/23"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "duckduckbot",
|
||||
"user_agent_regex": "\\+http\\://duckduckgo\\.com/duckduckbot\\.html",
|
||||
"action": "ALLOW",
|
||||
"remote_addresses": [
|
||||
"57.152.72.128/32",
|
||||
"51.8.253.152/32",
|
||||
"40.80.242.63/32",
|
||||
"20.12.141.99/32",
|
||||
"20.49.136.28/32",
|
||||
"51.116.131.221/32",
|
||||
"51.107.40.209/32",
|
||||
"20.40.133.240/32",
|
||||
"20.50.168.91/32",
|
||||
"51.120.48.122/32",
|
||||
"20.193.45.113/32",
|
||||
"40.76.173.151/32",
|
||||
"40.76.163.7/32",
|
||||
"20.185.79.47/32",
|
||||
"52.142.26.175/32",
|
||||
"20.185.79.15/32",
|
||||
"52.142.24.149/32",
|
||||
"40.76.162.208/32",
|
||||
"40.76.163.23/32",
|
||||
"40.76.162.191/32",
|
||||
"40.76.162.247/32",
|
||||
"40.88.21.235/32",
|
||||
"20.191.45.212/32",
|
||||
"52.146.59.12/32",
|
||||
"52.146.59.156/32",
|
||||
"52.146.59.154/32",
|
||||
"52.146.58.236/32",
|
||||
"20.62.224.44/32",
|
||||
"51.104.180.53/32",
|
||||
"51.104.180.47/32",
|
||||
"51.104.180.26/32",
|
||||
"51.104.146.225/32",
|
||||
"51.104.146.235/32",
|
||||
"20.73.202.147/32",
|
||||
"20.73.132.240/32",
|
||||
"20.71.12.143/32",
|
||||
"20.56.197.58/32",
|
||||
"20.56.197.63/32",
|
||||
"20.43.150.93/32",
|
||||
"20.43.150.85/32",
|
||||
"20.44.222.1/32",
|
||||
"40.89.243.175/32",
|
||||
"13.89.106.77/32",
|
||||
"52.143.242.6/32",
|
||||
"52.143.241.111/32",
|
||||
"52.154.60.82/32",
|
||||
"20.197.209.11/32",
|
||||
"20.197.209.27/32",
|
||||
"20.226.133.105/32",
|
||||
"191.234.216.4/32",
|
||||
"191.234.216.178/32",
|
||||
"20.53.92.211/32",
|
||||
"20.53.91.2/32",
|
||||
"20.207.99.197/32",
|
||||
"20.207.97.190/32",
|
||||
"40.81.250.205/32",
|
||||
"40.64.106.11/32",
|
||||
"40.64.105.247/32",
|
||||
"20.72.242.93/32",
|
||||
"20.99.255.235/32",
|
||||
"20.113.3.121/32",
|
||||
"52.224.16.221/32",
|
||||
"52.224.21.53/32",
|
||||
"52.224.20.204/32",
|
||||
"52.224.21.19/32",
|
||||
"52.224.20.249/32",
|
||||
"52.224.20.203/32",
|
||||
"52.224.20.190/32",
|
||||
"52.224.16.229/32",
|
||||
"52.224.21.20/32",
|
||||
"52.146.63.80/32",
|
||||
"52.224.20.227/32",
|
||||
"52.224.20.193/32",
|
||||
"52.190.37.160/32",
|
||||
"52.224.21.23/32",
|
||||
"52.224.20.223/32",
|
||||
"52.224.20.181/32",
|
||||
"52.224.21.49/32",
|
||||
"52.224.21.55/32",
|
||||
"52.224.21.61/32",
|
||||
"52.224.19.152/32",
|
||||
"52.224.20.186/32",
|
||||
"52.224.21.27/32",
|
||||
"52.224.21.51/32",
|
||||
"52.224.20.174/32",
|
||||
"52.224.21.4/32",
|
||||
"51.104.164.109/32",
|
||||
"51.104.167.71/32",
|
||||
"51.104.160.177/32",
|
||||
"51.104.162.149/32",
|
||||
"51.104.167.95/32",
|
||||
"51.104.167.54/32",
|
||||
"51.104.166.111/32",
|
||||
"51.104.167.88/32",
|
||||
"51.104.161.32/32",
|
||||
"51.104.163.250/32",
|
||||
"51.104.164.189/32",
|
||||
"51.104.167.19/32",
|
||||
"51.104.160.167/32",
|
||||
"51.104.167.110/32",
|
||||
"20.191.44.119/32",
|
||||
"51.104.167.104/32",
|
||||
"20.191.44.234/32",
|
||||
"51.104.164.215/32",
|
||||
"51.104.167.52/32",
|
||||
"20.191.44.22/32",
|
||||
"51.104.167.87/32",
|
||||
"51.104.167.96/32",
|
||||
"20.191.44.16/32",
|
||||
"51.104.167.61/32",
|
||||
"51.104.164.147/32",
|
||||
"20.50.48.159/32",
|
||||
"40.114.182.172/32",
|
||||
"20.50.50.130/32",
|
||||
"20.50.50.163/32",
|
||||
"20.50.50.46/32",
|
||||
"40.114.182.153/32",
|
||||
"20.50.50.118/32",
|
||||
"20.50.49.55/32",
|
||||
"20.50.49.25/32",
|
||||
"40.114.183.251/32",
|
||||
"20.50.50.123/32",
|
||||
"20.50.49.237/32",
|
||||
"20.50.48.192/32",
|
||||
"20.50.50.134/32",
|
||||
"51.138.90.233/32",
|
||||
"40.114.183.196/32",
|
||||
"20.50.50.146/32",
|
||||
"40.114.183.88/32",
|
||||
"20.50.50.145/32",
|
||||
"20.50.50.121/32",
|
||||
"20.50.49.40/32",
|
||||
"51.138.90.206/32",
|
||||
"40.114.182.45/32",
|
||||
"51.138.90.161/32",
|
||||
"20.50.49.0/32",
|
||||
"40.119.232.215/32",
|
||||
"104.43.55.167/32",
|
||||
"40.119.232.251/32",
|
||||
"40.119.232.50/32",
|
||||
"40.119.232.146/32",
|
||||
"40.119.232.218/32",
|
||||
"104.43.54.127/32",
|
||||
"104.43.55.117/32",
|
||||
"104.43.55.116/32",
|
||||
"104.43.55.166/32",
|
||||
"52.154.169.50/32",
|
||||
"52.154.171.70/32",
|
||||
"52.154.170.229/32",
|
||||
"52.154.170.113/32",
|
||||
"52.154.171.44/32",
|
||||
"52.154.172.2/32",
|
||||
"52.143.244.81/32",
|
||||
"52.154.171.87/32",
|
||||
"52.154.171.250/32",
|
||||
"52.154.170.28/32",
|
||||
"52.154.170.122/32",
|
||||
"52.143.243.117/32",
|
||||
"52.143.247.235/32",
|
||||
"52.154.171.235/32",
|
||||
"52.154.171.196/32",
|
||||
"52.154.171.0/32",
|
||||
"52.154.170.243/32",
|
||||
"52.154.170.26/32",
|
||||
"52.154.169.200/32",
|
||||
"52.154.170.96/32",
|
||||
"52.154.170.88/32",
|
||||
"52.154.171.150/32",
|
||||
"52.154.171.205/32",
|
||||
"52.154.170.117/32",
|
||||
"52.154.170.209/32",
|
||||
"191.235.202.48/32",
|
||||
"191.233.3.202/32",
|
||||
"191.235.201.214/32",
|
||||
"191.233.3.197/32",
|
||||
"191.235.202.38/32",
|
||||
"20.53.78.144/32",
|
||||
"20.193.24.10/32",
|
||||
"20.53.78.236/32",
|
||||
"20.53.78.138/32",
|
||||
"20.53.78.123/32",
|
||||
"20.53.78.106/32",
|
||||
"20.193.27.215/32",
|
||||
"20.193.25.197/32",
|
||||
"20.193.12.126/32",
|
||||
"20.193.24.251/32",
|
||||
"20.204.242.101/32",
|
||||
"20.207.72.113/32",
|
||||
"20.204.242.19/32",
|
||||
"20.219.45.67/32",
|
||||
"20.207.72.11/32",
|
||||
"20.219.45.190/32",
|
||||
"20.204.243.55/32",
|
||||
"20.204.241.148/32",
|
||||
"20.207.72.110/32",
|
||||
"20.204.240.172/32",
|
||||
"20.207.72.21/32",
|
||||
"20.204.246.81/32",
|
||||
"20.207.107.181/32",
|
||||
"20.204.246.254/32",
|
||||
"20.219.43.246/32",
|
||||
"52.149.25.43/32",
|
||||
"52.149.61.51/32",
|
||||
"52.149.58.139/32",
|
||||
"52.149.60.38/32",
|
||||
"52.148.165.38/32",
|
||||
"52.143.95.162/32",
|
||||
"52.149.56.151/32",
|
||||
"52.149.30.45/32",
|
||||
"52.149.58.173/32",
|
||||
"52.143.95.204/32",
|
||||
"52.149.28.83/32",
|
||||
"52.149.58.69/32",
|
||||
"52.148.161.87/32",
|
||||
"52.149.58.27/32",
|
||||
"52.149.28.18/32",
|
||||
"20.79.226.26/32",
|
||||
"20.79.239.66/32",
|
||||
"20.79.238.198/32",
|
||||
"20.113.14.159/32",
|
||||
"20.75.144.152/32",
|
||||
"20.43.172.120/32",
|
||||
"20.53.134.160/32",
|
||||
"20.201.15.208/32",
|
||||
"20.93.28.24/32",
|
||||
"20.61.34.40/32",
|
||||
"52.242.224.168/32",
|
||||
"20.80.129.80/32",
|
||||
"20.195.108.47/32",
|
||||
"4.195.133.120/32",
|
||||
"4.228.76.163/32",
|
||||
"4.182.131.108/32",
|
||||
"4.209.224.56/32",
|
||||
"108.141.83.74/32",
|
||||
"4.213.46.14/32",
|
||||
"172.169.17.165/32",
|
||||
"51.8.71.117/32",
|
||||
"20.3.1.178/32",
|
||||
"52.149.56.151/32",
|
||||
"52.149.30.45/32",
|
||||
"52.149.58.173/32",
|
||||
"52.143.95.204/32",
|
||||
"52.149.28.83/32",
|
||||
"52.149.58.69/32",
|
||||
"52.148.161.87/32",
|
||||
"52.149.58.27/32",
|
||||
"52.149.28.18/32",
|
||||
"20.79.226.26/32",
|
||||
"20.79.239.66/32",
|
||||
"20.79.238.198/32",
|
||||
"20.113.14.159/32",
|
||||
"20.75.144.152/32",
|
||||
"20.43.172.120/32",
|
||||
"20.53.134.160/32",
|
||||
"20.201.15.208/32",
|
||||
"20.93.28.24/32",
|
||||
"20.61.34.40/32",
|
||||
"52.242.224.168/32",
|
||||
"20.80.129.80/32",
|
||||
"20.195.108.47/32",
|
||||
"4.195.133.120/32",
|
||||
"4.228.76.163/32",
|
||||
"4.182.131.108/32",
|
||||
"4.209.224.56/32",
|
||||
"108.141.83.74/32",
|
||||
"4.213.46.14/32",
|
||||
"172.169.17.165/32",
|
||||
"51.8.71.117/32",
|
||||
"20.3.1.178/32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "qwantbot",
|
||||
"user_agent_regex": "\\+https\\://help\\.qwant\\.com/bot/",
|
||||
@@ -312,6 +588,15 @@
|
||||
"91.242.162.0/24"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "internet-archive",
|
||||
"action": "ALLOW",
|
||||
"remote_addresses": [
|
||||
"207.241.224.0/20",
|
||||
"208.70.24.0/21",
|
||||
"2620:0:9c0::/48"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "kagibot",
|
||||
"user_agent_regex": "\\+https\\://kagi\\.com/bot",
|
||||
@@ -390,9 +675,9 @@
|
||||
},
|
||||
{
|
||||
"name": "generic-browser",
|
||||
"user_agent_regex": "Mozilla",
|
||||
"user_agent_regex": "Mozilla|Opera",
|
||||
"action": "CHALLENGE"
|
||||
}
|
||||
],
|
||||
"dnsbl": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
slug: first-blog-post
|
||||
title: First Blog Post
|
||||
authors: [slorber, yangshun]
|
||||
tags: [hola, docusaurus]
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet...
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
slug: long-blog-post
|
||||
title: Long Blog Post
|
||||
authors: yangshun
|
||||
tags: [hello, docusaurus]
|
||||
---
|
||||
|
||||
This is the summary of a very long blog post,
|
||||
|
||||
Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
slug: mdx-blog-post
|
||||
title: MDX Blog Post
|
||||
authors: [slorber]
|
||||
tags: [docusaurus]
|
||||
---
|
||||
|
||||
Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
|
||||
|
||||
:::tip
|
||||
|
||||
Use the power of React to create interactive blog posts.
|
||||
|
||||
:::
|
||||
|
||||
{/* truncate */}
|
||||
|
||||
For example, use JSX to create an interactive button:
|
||||
|
||||
```js
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
```
|
||||
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB |
@@ -1,29 +0,0 @@
|
||||
---
|
||||
slug: welcome
|
||||
title: Welcome
|
||||
authors: [slorber, yangshun]
|
||||
tags: [facebook, hello, docusaurus]
|
||||
---
|
||||
|
||||
[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
|
||||
|
||||
Here are a few tips you might find useful.
|
||||
|
||||
<!-- truncate -->
|
||||
|
||||
Simply add Markdown files (or folders) to the `blog` directory.
|
||||
|
||||
Regular blog authors can be added to `authors.yml`.
|
||||
|
||||
The blog post date can be extracted from filenames, such as:
|
||||
|
||||
- `2019-05-30-welcome.md`
|
||||
- `2019-05-30-welcome/index.md`
|
||||
|
||||
A blog post folder can be convenient to co-locate blog post images:
|
||||
|
||||

|
||||
|
||||
The blog supports tags as well!
|
||||
|
||||
**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
|
||||
@@ -1,23 +0,0 @@
|
||||
yangshun:
|
||||
name: Yangshun Tay
|
||||
title: Front End Engineer @ Facebook
|
||||
url: https://github.com/yangshun
|
||||
image_url: https://github.com/yangshun.png
|
||||
page: true
|
||||
socials:
|
||||
x: yangshunz
|
||||
github: yangshun
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
page:
|
||||
# customize the url of the author page at /blog/authors/<permalink>
|
||||
permalink: '/all-sebastien-lorber-articles'
|
||||
socials:
|
||||
x: sebastienlorber
|
||||
linkedin: sebastienlorber
|
||||
github: slorber
|
||||
newsletter: https://thisweekinreact.com
|
||||
@@ -1,19 +0,0 @@
|
||||
facebook:
|
||||
label: Facebook
|
||||
permalink: /facebook
|
||||
description: Facebook tag description
|
||||
|
||||
hello:
|
||||
label: Hello
|
||||
permalink: /hello
|
||||
description: Hello tag description
|
||||
|
||||
docusaurus:
|
||||
label: Docusaurus
|
||||
permalink: /docusaurus
|
||||
description: Docusaurus tag description
|
||||
|
||||
hola:
|
||||
label: Hola
|
||||
permalink: /hola
|
||||
description: Hola tag description
|
||||
@@ -11,9 +11,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Add more AI user agents based on the [ai.robots.txt](https://github.com/ai-robots-txt/ai.robots.txt) project
|
||||
- Embedded challenge data in initial HTML response to improve performance
|
||||
- Whitelisted [DuckDuckBot](https://duckduckgo.com/duckduckgo-help-pages/results/duckduckbot/) in botPolicies
|
||||
- Improvements to build scripts to make them less independent of the build host
|
||||
- Improved the OpenGraph error logging
|
||||
- Added `Opera` to the `generic-browser` bot policy rule
|
||||
- Added FreeBSD rc.d script so can be run as a FreeBSD daemon.
|
||||
- Allow requests from the Internet Archive
|
||||
- Added example nginx configuration to documentation
|
||||
- Added example Apache configuration to the documentation [#277](https://github.com/TecharoHQ/anubis/issues/277)
|
||||
- Move per-environment configuration details into their own pages
|
||||
|
||||
## v1.16.0
|
||||
|
||||
Fordola rem Lupis
|
||||
|
||||
> I want to make them pay! All of them! Everyone who ever mocked or looked down on me -- I want the power to make them pay!
|
||||
|
||||
The following features are the "big ticket" items:
|
||||
|
||||
- Added support for native Debian, Red Hat, and tarball packaging strategies including installation and use directions.
|
||||
- A prebaked tarball has been added, allowing distros to build Anubis like they could in v1.15.x.
|
||||
- The placeholder Anubis mascot has been replaced with a design by [CELPHASE](https://bsky.app/profile/celphase.bsky.social).
|
||||
- Verification page now shows hash rate and a progress bar for completion probability.
|
||||
- Added support for [OpenGraph tags](https://ogp.me/) when rendering the challenge page. This allows for social previews to be generated when sharing the challenge page on social media platforms ([#195](https://github.com/TecharoHQ/anubis/pull/195))
|
||||
- Added support for passing the ed25519 signing key in a file with `-ed25519-private-key-hex-file` or `ED25519_PRIVATE_KEY_HEX_FILE`.
|
||||
|
||||
The other small fixes have been made:
|
||||
|
||||
- Added a periodic cleanup routine for the decaymap that removes expired entries, ensuring stale data is properly pruned.
|
||||
- Added a no-store Cache-Control header to the challenge page
|
||||
- Hide the directory listings for Anubis' internal static content
|
||||
@@ -23,7 +49,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- The Dockerfile has been removed as it is no longer in use
|
||||
- Developer documentation has been added to the docs site
|
||||
- Show more errors when some predictable challenge page errors happen ([#150](https://github.com/TecharoHQ/anubis/issues/150))
|
||||
- Verification page now shows hash rate and a progress bar for completion probability.
|
||||
- Added the `--debug-benchmark-js` flag for testing proof-of-work performance during development.
|
||||
- Use `TrimSuffix` instead of `TrimRight` on containerbuild
|
||||
- Fix the startup logs to correctly show the address and port the server is listening on
|
||||
@@ -31,17 +56,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Added a wait with button continue + 30 second auto continue after 30s if you click "Why am I seeing this?"
|
||||
- Fixed a typo in the challenge page title.
|
||||
- Disabled running integration tests on Windows hosts due to it's reliance on posix features (see [#133](https://github.com/TecharoHQ/anubis/pull/133#issuecomment-2764732309)).
|
||||
- Added support for passing the ed25519 signing key in a file with `-ed25519-private-key-hex-file` or `ED25519_PRIVATE_KEY_HEX_FILE`.
|
||||
- Fixed minor typos
|
||||
- Added a Makefile to enable comfortable workflows for downstream packagers.
|
||||
- Added `zizmor` for GitHub Actions static analysis
|
||||
- Fixed most `zizmor` findings
|
||||
- Enabled Dependabot
|
||||
- Added an air config for autoreload support in development ([#195](https://github.com/TecharoHQ/anubis/pull/195))
|
||||
- Added support for [OpenGraph tags](https://ogp.me/) when rendering the challenge page. This allows for social previews to be generated when sharing the challenge page on social media platforms ([#195](https://github.com/TecharoHQ/anubis/pull/195))
|
||||
- Added an `--extract-resources` flag to extract static resources to a local folder.
|
||||
- Add noindex flag to all Anubis pages ([#227](https://github.com/TecharoHQ/anubis/issues/227)).
|
||||
- Added `WEBMASTER_EMAIL` variable, if it is present then display that email address on error pages ([#235](https://github.com/TecharoHQ/anubis/pull/235), [#115](https://github.com/TecharoHQ/anubis/issues/115))
|
||||
- Hash pinned all GitHub Actions
|
||||
|
||||
## v1.15.1
|
||||
|
||||
|
||||
8
docs/docs/admin/environments/_category_.json
Normal file
8
docs/docs/admin/environments/_category_.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"label": "Environments",
|
||||
"position": 20,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Detailed information about individual environments (such as HTTP servers, platforms, etc.) Anubis is known to work with."
|
||||
}
|
||||
}
|
||||
151
docs/docs/admin/environments/apache.mdx
Normal file
151
docs/docs/admin/environments/apache.mdx
Normal file
@@ -0,0 +1,151 @@
|
||||
# Apache
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
Anubis is intended to be a filter proxy. The way to integrate this is to break your configuration up into two parts: TLS termination and then HTTP routing. Consider this diagram:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Apache as tls terminator and HTTP router
|
||||
---
|
||||
|
||||
flowchart LR
|
||||
T(User Traffic)
|
||||
subgraph Apache 2
|
||||
TCP(TCP 80/443)
|
||||
US(TCP 3001)
|
||||
end
|
||||
|
||||
An(Anubis)
|
||||
B(Backend)
|
||||
|
||||
T --> |TLS termination| TCP
|
||||
TCP --> |Traffic filtering| An
|
||||
An --> |Happy traffic| US
|
||||
US --> |whatever you're doing| B
|
||||
```
|
||||
|
||||
Effectively you have one trip through Apache to do TLS termination, a detour through Anubis for traffic scrubbing, and then going to the backend directly. This final socket is what will do HTTP routing.
|
||||
|
||||
:::note
|
||||
|
||||
These examples assume that you are using a setup where your nginx configuration is made up of a bunch of files in `/etc/httpd/conf.d/*.conf`. This is not true for all deployments of Apache. If you are not in such an environment, append these snippets to your `/etc/httpd/conf/httpd.conf` file.
|
||||
|
||||
:::
|
||||
|
||||
## Dependencies
|
||||
|
||||
Install the following dependencies for proxying HTTP:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="rpm" label="Red Hat / RPM" default>
|
||||
|
||||
```text
|
||||
dnf -y install mod_proxy_html
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="deb" label="Debian / Ubuntu / apt">
|
||||
|
||||
```text
|
||||
apt-get install -y libapache2-mod-proxy-html libxml2-dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Configuration
|
||||
|
||||
Assuming you are protecting `anubistest.techaro.lol`, you need the following server configuration blocks:
|
||||
|
||||
1. A block on port 80 that forwards HTTP to HTTPS
|
||||
2. A block on port 443 that terminates TLS and forwards to Anubis
|
||||
3. A block on port 3001 that actually serves your websites
|
||||
|
||||
```text
|
||||
# Plain HTTP redirect to HTTPS
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin your@email.here
|
||||
ServerName anubistest.techaro.lol
|
||||
DocumentRoot /var/www/anubistest.techaro.lol
|
||||
ErrorLog /var/log/httpd/anubistest.techaro.lol_error.log
|
||||
CustomLog /var/log/httpd/anubistest.techaro.lol_access.log combined
|
||||
RewriteEngine on
|
||||
RewriteCond %{SERVER_NAME} =anubistest.techaro.lol
|
||||
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
|
||||
</VirtualHost>
|
||||
|
||||
# HTTPS listener that forwards to Anubis
|
||||
<VirtualHost *:443>
|
||||
ServerAdmin your@email.here
|
||||
ServerName anubistest.techaro.lol
|
||||
DocumentRoot /var/www/anubistest.techaro.lol
|
||||
ErrorLog /var/log/httpd/anubistest.techaro.lol_error.log
|
||||
CustomLog /var/log/httpd/anubistest.techaro.lol_access.log combined
|
||||
|
||||
SSLCertificateFile /etc/letsencrypt/live/anubistest.techaro.lol/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/anubistest.techaro.lol/privkey.pem
|
||||
Include /etc/letsencrypt/options-ssl-apache.conf
|
||||
|
||||
# These headers need to be set or else Anubis will
|
||||
# throw an "admin misconfiguration" error.
|
||||
RequestHeader set "X-Real-Ip" expr=%{REMOTE_ADDR}
|
||||
RequestHeader set X-Forwarded-Proto "https"
|
||||
|
||||
ProxyPreserveHost On
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyVia Off
|
||||
|
||||
# Replace 9000 with the port Anubis listens on
|
||||
ProxyPass / http://[::1]:9000/
|
||||
ProxyPassReverse / http://[::1]:9000/
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
|
||||
# Actual website config
|
||||
<VirtualHost *:3001>
|
||||
ServerAdmin your@email.here
|
||||
ServerName anubistest.techaro.lol
|
||||
DocumentRoot /var/www/anubistest.techaro.lol
|
||||
ErrorLog /var/log/httpd/anubistest.techaro.lol_error.log
|
||||
CustomLog /var/log/httpd/anubistest.techaro.lol_access.log combined
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
Make sure to add a separate configuration file for the listener on port 3001:
|
||||
|
||||
```text
|
||||
# /etc/httpd/conf.d/listener-3001.conf
|
||||
|
||||
Listen 3001
|
||||
```
|
||||
|
||||
This can be repeated for multiple sites. Anubis does not care about the HTTP `Host` header and will happily cope with multiple websites via the same instance.
|
||||
|
||||
Then reload your Apache config and load your website. You should see Anubis protecting your apps!
|
||||
|
||||
```text
|
||||
sudo systemctl reload httpd.service
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Here are some answers to questions that came in in testing:
|
||||
|
||||
### I'm running on a Red Hat distribution and Apache is saying "service unavailable" for every page load
|
||||
|
||||
If you see a "Service unavailable" error on every page load and run a Red Hat derived distribution, you are missing a `selinux` setting. The exact command will be in a journalctl log message like this:
|
||||
|
||||
```text
|
||||
***** Plugin catchall_boolean (89.3 confidence) suggests ******************
|
||||
|
||||
If you want to allow HTTPD scripts and modules to connect to the network using TCP.
|
||||
Then you must tell SELinux about this by enabling the 'httpd_can_network_connect' boolean.
|
||||
|
||||
Do
|
||||
setsebool -P httpd_can_network_connect 1
|
||||
```
|
||||
|
||||
This will fix the error immediately.
|
||||
26
docs/docs/admin/environments/docker-compose.mdx
Normal file
26
docs/docs/admin/environments/docker-compose.mdx
Normal file
@@ -0,0 +1,26 @@
|
||||
# Docker compose
|
||||
|
||||
Docker compose is typically used in concert with other load balancers such as [Apache](./apache.mdx) or [Nginx](./nginx.mdx). Below is a minimal example showing you how to set up an instance of Anubis listening on host port 8080 that points to a static website containing data in `./www`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
anubis-nginx:
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
environment:
|
||||
BIND: ":8080"
|
||||
DIFFICULTY: "5"
|
||||
METRICS_BIND: ":9090"
|
||||
SERVE_ROBOTS_TXT: "true"
|
||||
TARGET: "http://nginx"
|
||||
POLICY_FNAME: "/data/cfg/botPolicy.json"
|
||||
OG_PASSTHROUGH: "true"
|
||||
OG_EXPIRY_TIME: "24h"
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- "./botPolicy.json:/data/cfg/botPolicy.json:ro"
|
||||
nginx:
|
||||
image: nginx
|
||||
volumes:
|
||||
- "./www:/usr/share/nginx/html"
|
||||
```
|
||||
128
docs/docs/admin/environments/kubernetes.mdx
Normal file
128
docs/docs/admin/environments/kubernetes.mdx
Normal file
@@ -0,0 +1,128 @@
|
||||
# Kubernetes
|
||||
|
||||
When setting up Anubis in Kubernetes, you want to make sure that you thread requests through Anubis kinda like this:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Anubis embedded into workload pods
|
||||
---
|
||||
|
||||
flowchart LR
|
||||
T(User Traffic)
|
||||
|
||||
IngressController(IngressController)
|
||||
|
||||
subgraph Service
|
||||
AnPort(Anubis Port)
|
||||
BPort(Backend Port)
|
||||
end
|
||||
|
||||
subgraph Pod
|
||||
An(Anubis)
|
||||
B(Backend)
|
||||
end
|
||||
|
||||
T --> IngressController
|
||||
IngressController --> AnPort
|
||||
AnPort --> An
|
||||
An --> B
|
||||
```
|
||||
|
||||
Anubis is lightweight enough that you should be able to have many instances of it running without many problems. If this is a concern for you, please check out [ingress-anubis](https://github.com/jaredallard/ingress-anubis?ref=anubis.techaro.lol).
|
||||
|
||||
This example makes the following assumptions:
|
||||
|
||||
- Your target service is listening on TCP port `5000`.
|
||||
- Anubis will be listening on port `8080`.
|
||||
|
||||
Adjust these values as facts and circumstances demand.
|
||||
|
||||
Create a secret with the signing key Anubis should use for its responses:
|
||||
|
||||
```
|
||||
kubectl create secret generic anubis-key \
|
||||
--namespace default \
|
||||
--from-literal=ED25519_PRIVATE_KEY_HEX=$(openssl rand -hex 32)
|
||||
```
|
||||
|
||||
Attach Anubis to your Deployment:
|
||||
|
||||
```yaml
|
||||
containers:
|
||||
# ...
|
||||
- name: anubis
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: "BIND"
|
||||
value: ":8080"
|
||||
- name: "DIFFICULTY"
|
||||
value: "4"
|
||||
- name: ED25519_PRIVATE_KEY_HEX
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: anubis-key
|
||||
key: ED25519_PRIVATE_KEY_HEX
|
||||
- name: "METRICS_BIND"
|
||||
value: ":9090"
|
||||
- name: "SERVE_ROBOTS_TXT"
|
||||
value: "true"
|
||||
- name: "TARGET"
|
||||
value: "http://localhost:5000"
|
||||
- name: "OG_PASSTHROUGH"
|
||||
value: "true"
|
||||
- name: "OG_EXPIRY_TIME"
|
||||
value: "24h"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
runAsNonRoot: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
```
|
||||
|
||||
Then add a Service entry for Anubis:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
spec:
|
||||
ports:
|
||||
# diff-add
|
||||
- protocol: TCP
|
||||
# diff-add
|
||||
port: 8080
|
||||
# diff-add
|
||||
targetPort: 8080
|
||||
# diff-add
|
||||
name: anubis
|
||||
```
|
||||
|
||||
Then point your Ingress to the Anubis port:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- host: git.xeserv.us
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/"
|
||||
backend:
|
||||
service:
|
||||
name: git
|
||||
port:
|
||||
# diff-remove
|
||||
name: http
|
||||
# diff-add
|
||||
name: anubis
|
||||
```
|
||||
166
docs/docs/admin/environments/nginx.mdx
Normal file
166
docs/docs/admin/environments/nginx.mdx
Normal file
@@ -0,0 +1,166 @@
|
||||
# Nginx
|
||||
|
||||
Anubis is intended to be a filter proxy. The way to integrate this with nginx is to break your configuration up into two parts: TLS termination and then HTTP routing. Consider this diagram:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Nginx as tls terminator and HTTP router
|
||||
---
|
||||
|
||||
flowchart LR
|
||||
T(User Traffic)
|
||||
subgraph Nginx
|
||||
TCP(TCP 80/443)
|
||||
US(Unix Socket or
|
||||
another TCP port)
|
||||
end
|
||||
|
||||
An(Anubis)
|
||||
B(Backend)
|
||||
|
||||
T --> |TLS termination| TCP
|
||||
TCP --> |Traffic filtering| An
|
||||
An --> |Happy traffic| US
|
||||
US --> |whatever you're doing| B
|
||||
```
|
||||
|
||||
Instead of your traffic going right from TLS termination into the backend, it takes a detour through Anubis. Anubis filters out the "bad" traffic and then passes the "good" traffic to another socket that Nginx has open. This final socket is what you will use to do HTTP routing.
|
||||
|
||||
Effectively, you have two roles for nginx: TLS termination (converting HTTPS to HTTP) and HTTP routing (distributing requests to the individual vhosts). This can stack with something like Apache in case you have a legacy deployment. Make sure you have the right [TLS certificates configured](https://code.kuederle.com/letsencrypt/) at the TLS termination level.
|
||||
|
||||
:::note
|
||||
|
||||
These examples assume that you are using a setup where your nginx configuration is made up of a bunch of files in `/etc/nginx/conf.d/*.conf`. This is not true for all deployments of nginx. If you are not in such an environment, append these snippets to your `/etc/nginx/nginx.conf` file.
|
||||
|
||||
:::
|
||||
|
||||
Assuming that we are protecting `anubistest.techaro.lol`, here's what the server configuration file would look like:
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/conf.d/server-anubistest-techaro-lol.conf
|
||||
|
||||
# HTTP - Redirect all HTTP traffic to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name anubistest.techaro.lol;
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# TLS termination server, this will listen over TLS (https) and then
|
||||
# proxy all traffic to the target via Anubis.
|
||||
server {
|
||||
# Listen on TCP port 443 with TLS (https) and HTTP/2
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_pass http://anubis;
|
||||
}
|
||||
|
||||
server_name anubistest.techaro.lol;
|
||||
|
||||
ssl_certificate /path/to/your/certs/anubistest.techaro.lol.crt;
|
||||
ssl_certificate_key /path/to/your/certs/anubistest.techaro.lol.key;
|
||||
}
|
||||
|
||||
# Backend server, this is where your webapp should actually live.
|
||||
server {
|
||||
listen unix:/run/nginx/nginx.sock;
|
||||
|
||||
server_name anubistest.techaro.lol;
|
||||
root "/srv/http/anubistest.techaro.lol";
|
||||
index index.html;
|
||||
|
||||
# Your normal configuration can go here
|
||||
# location .php { fastcgi...} etc.
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
You can copy the `location /` block into a separate file named something like `conf-anubis.inc` and then include it inline to other `server` blocks:
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/conf.d/conf-anubis.inc
|
||||
|
||||
# Forward to anubis
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_pass http://anubis;
|
||||
}
|
||||
```
|
||||
|
||||
Then in a server block:
|
||||
|
||||
<details>
|
||||
<summary>Full nginx config</summary>
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/conf.d/server-mimi-techaro-lol.conf
|
||||
|
||||
server {
|
||||
# Listen on 443 with SSL
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
# Slipstream via Anubis
|
||||
include "conf-anubis.inc";
|
||||
|
||||
server_name mimi.techaro.lol;
|
||||
|
||||
ssl_certificate /path/to/your/certs/mimi.techaro.lol.crt;
|
||||
ssl_certificate_key /path/to/your/certs/mimi.techaro.lol.key;
|
||||
}
|
||||
|
||||
server {
|
||||
listen unix:/run/nginx/nginx.sock;
|
||||
|
||||
server_name mimi.techaro.lol;
|
||||
root "/srv/http/mimi.techaro.lol";
|
||||
index index.html;
|
||||
|
||||
# Your normal configuration can go here
|
||||
# location .php { fastcgi...} etc.
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
:::
|
||||
|
||||
Create an upstream for Anubis.
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/conf.d/upstream-anubis.conf
|
||||
|
||||
upstream anubis {
|
||||
# Make sure this matches the values you set for `BIND` and `BIND_NETWORK`.
|
||||
# If this does not match, your services will not be protected by Anubis.
|
||||
|
||||
# Try anubis first over a UNIX socket
|
||||
server unix:/run/anubis/nginx.sock;
|
||||
#server http://127.0.0.1:8923;
|
||||
|
||||
# Optional: fall back to serving the websites directly. This allows your
|
||||
# websites to be resilient against Anubis failing, at the risk of exposing
|
||||
# them to the raw internet without protection. This is a tradeoff and can
|
||||
# be worth it in some edge cases.
|
||||
#server unix:/run/nginx.sock backup;
|
||||
}
|
||||
```
|
||||
|
||||
This can be repeated for multiple sites. Anubis does not care about the HTTP `Host` header and will happily cope with multiple websites via the same instance.
|
||||
|
||||
Then reload your nginx config and load your website. You should see Anubis protecting your apps!
|
||||
|
||||
```text
|
||||
sudo systemctl reload nginx.service
|
||||
```
|
||||
@@ -4,6 +4,9 @@ title: Setting up Anubis
|
||||
|
||||
import RandomKey from "@site/src/components/RandomKey";
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
Anubis is meant to sit between your reverse proxy (such as Nginx or Caddy) and your target service. One instance of Anubis must be used per service you are protecting.
|
||||
|
||||
<center>
|
||||
@@ -38,6 +41,10 @@ The Docker image runs Anubis as user ID 1000 and group ID 1000. If you are mount
|
||||
|
||||
Anubis has very minimal system requirements. I suspect that 128Mi of ram may be sufficient for a large number of concurrent clients. Anubis may be a poor fit for apps that use WebSockets and maintain open connections, but I don't have enough real-world experience to know one way or another.
|
||||
|
||||
## Native packages
|
||||
|
||||
For more detailed information on installing Anubis with native packages, please read [the native install directions](./native-install.mdx).
|
||||
|
||||
## Environment variables
|
||||
|
||||
Anubis uses these environment variables for configuration:
|
||||
@@ -76,113 +83,11 @@ Alternatively here is a key generated by your browser:
|
||||
|
||||
<RandomKey />
|
||||
|
||||
## Docker compose
|
||||
## Next steps
|
||||
|
||||
Add Anubis to your compose file pointed at your service:
|
||||
To get Anubis filtering your traffic, you need to make sure it's added to your HTTP load balancer or platform configuration. See the [environments category](/docs/category/environments) for detailed information on individual environments.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
anubis-nginx:
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
environment:
|
||||
BIND: ":8080"
|
||||
DIFFICULTY: "5"
|
||||
METRICS_BIND: ":9090"
|
||||
SERVE_ROBOTS_TXT: "true"
|
||||
TARGET: "http://nginx"
|
||||
POLICY_FNAME: "/data/cfg/botPolicy.json"
|
||||
OG_PASSTHROUGH: "true"
|
||||
OG_EXPIRY_TIME: "24h"
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- "./botPolicy.json:/data/cfg/botPolicy.json:ro"
|
||||
nginx:
|
||||
image: nginx
|
||||
volumes:
|
||||
- "./www:/usr/share/nginx/html"
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
This example makes the following assumptions:
|
||||
|
||||
- Your target service is listening on TCP port `5000`.
|
||||
- Anubis will be listening on port `8080`.
|
||||
|
||||
Attach Anubis to your Deployment:
|
||||
|
||||
```yaml
|
||||
containers:
|
||||
# ...
|
||||
- name: anubis
|
||||
image: ghcr.io/techarohq/anubis:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: "BIND"
|
||||
value: ":8080"
|
||||
- name: "DIFFICULTY"
|
||||
value: "5"
|
||||
- name: "METRICS_BIND"
|
||||
value: ":9090"
|
||||
- name: "SERVE_ROBOTS_TXT"
|
||||
value: "true"
|
||||
- name: "TARGET"
|
||||
value: "http://localhost:5000"
|
||||
- name: "OG_PASSTHROUGH"
|
||||
value: "true"
|
||||
- name: "OG_EXPIRY_TIME"
|
||||
value: "24h"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 128Mi
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
runAsNonRoot: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
```
|
||||
|
||||
Then add a Service entry for Anubis:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
spec:
|
||||
ports:
|
||||
# diff-add
|
||||
- protocol: TCP
|
||||
# diff-add
|
||||
port: 8080
|
||||
# diff-add
|
||||
targetPort: 8080
|
||||
# diff-add
|
||||
name: anubis
|
||||
```
|
||||
|
||||
Then point your Ingress to the Anubis port:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- host: git.xeserv.us
|
||||
http:
|
||||
paths:
|
||||
- pathType: Prefix
|
||||
path: "/"
|
||||
backend:
|
||||
service:
|
||||
name: git
|
||||
port:
|
||||
# diff-remove
|
||||
name: http
|
||||
# diff-add
|
||||
name: anubis
|
||||
```
|
||||
- [Apache](./environments/apache.mdx)
|
||||
- [Docker compose](./environments/docker-compose.mdx)
|
||||
- [Kubernetes](./environments/kubernetes.mdx)
|
||||
- [Nginx](./environments/nginx.mdx)
|
||||
|
||||
@@ -5,6 +5,8 @@ title: Installing Anubis with a native package
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
|
||||
Download the package for your system from [the most recent release on GitHub](https://github.com/TecharoHQ/anubis/releases).
|
||||
|
||||
Install the Anubis package using your package manager of choice:
|
||||
|
||||
<Tabs>
|
||||
@@ -129,3 +131,8 @@ curl http://localhost:8240/metrics
|
||||
```
|
||||
|
||||
Then set up your reverse proxy (Nginx, Caddy, etc.) to point to the Anubis port. Anubis will then reverse proxy all requests that meet the policies in `/etc/anubis/gitea.botPolicies.json` to the target service.
|
||||
|
||||
For more details on particular reverse proxies, see here:
|
||||
|
||||
- [Apache](./environments/apache.mdx)
|
||||
- [Nginx](./environments/nginx.mdx)
|
||||
|
||||
@@ -12,6 +12,12 @@ These instructions may work, but for right now they are informative for downstre
|
||||
|
||||
If you are doing a build entirely from source, here's what you need to do:
|
||||
|
||||
:::info
|
||||
|
||||
If you maintain a package for Anubis v1.15.x or older, you will need to update your package build. You may want to use one of the half-baked tarballs if your distro/environment of choice makes it difficult to use npm.
|
||||
|
||||
:::
|
||||
|
||||
### Tools needed
|
||||
|
||||
In order to build a production-ready binary of Anubis, you need the following packages in your environment:
|
||||
|
||||
@@ -26,3 +26,19 @@ Anubis is a bit of a nuclear response. This will result in your website being bl
|
||||
If you run into any issues running Anubis, please [open an issue](https://github.com/TecharoHQ/anubis/issues/new?template=Blank+issue) and include all the information I would need to diagnose your issue.
|
||||
|
||||
For live chat, please join the [Patreon](https://patreon.com/cadey) and ask in the Patron discord in the channel `#anubis`.
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#TecharoHQ/anubis&Date)
|
||||
|
||||
## Packaging Status
|
||||
|
||||
[](https://repology.org/project/anubis-anti-crawler/versions)
|
||||
|
||||
## Contributors
|
||||
|
||||
<a href="https://github.com/TecharoHQ/anubis/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=TecharoHQ/anubis" />
|
||||
</a>
|
||||
|
||||
Made with [contrib.rocks](https://contrib.rocks).
|
||||
|
||||
31
docs/docs/user/known-instances.md
Normal file
31
docs/docs/user/known-instances.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: List of known websites using Anubis
|
||||
---
|
||||
|
||||
This page contains a non-exhaustive list with all websites using Anubis.
|
||||
|
||||
* <details>
|
||||
<summary>The Linux Foundation</summary>
|
||||
|
||||
* https://git.kernel.org/
|
||||
* https://lore.kernel.org/
|
||||
</details>
|
||||
* https://gitlab.gnome.org/
|
||||
* https://scioly.org/
|
||||
* https://bugs.winehq.org/
|
||||
* https://svnweb.freebsd.org/
|
||||
* https://trac.ffmpeg.org/
|
||||
* https://git.sr.ht/
|
||||
* https://xeiaso.net/
|
||||
* https://source.puri.sm/
|
||||
* https://git.enlightenment.org/
|
||||
* https://superlove.sayitditto.net/
|
||||
* https://linktaco.com/
|
||||
* https://jaredallard.dev/
|
||||
* https://dev.sanctum.geek.nz/
|
||||
* https://canine.tools/
|
||||
* <details>
|
||||
<summary>The United Nations</summary>
|
||||
|
||||
* https://policytoolbox.iiep.unesco.org/
|
||||
</details>
|
||||
@@ -125,10 +125,6 @@ const config: Config = {
|
||||
{
|
||||
title: 'More',
|
||||
items: [
|
||||
{
|
||||
label: 'Blog',
|
||||
to: '/blog',
|
||||
},
|
||||
{
|
||||
label: 'GitHub',
|
||||
href: 'https://github.com/TecharoHQ/anubis',
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary: #2e8555;
|
||||
--ifm-color-primary-dark: #29784c;
|
||||
--ifm-color-primary-darker: #277148;
|
||||
--ifm-color-primary-darkest: #205d3b;
|
||||
--ifm-color-primary-light: #33925d;
|
||||
--ifm-color-primary-lighter: #359962;
|
||||
--ifm-color-primary-lightest: #3cad6e;
|
||||
--ifm-color-primary: #ff5630;
|
||||
--ifm-color-primary-dark: #ad422a;
|
||||
--ifm-color-primary-darker: #8f3521;
|
||||
--ifm-color-primary-darkest: #592115;
|
||||
--ifm-color-primary-light: #ff7152;
|
||||
--ifm-color-primary-lighter: #ff9178;
|
||||
--ifm-color-primary-lightest: #ffb09e;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
--code-block-diff-add-line-color: #ccffd8;
|
||||
|
||||
17
go.mod
17
go.mod
@@ -1,15 +1,14 @@
|
||||
module github.com/TecharoHQ/anubis
|
||||
|
||||
go 1.24.2
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.3.857
|
||||
github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/playwright-community/playwright-go v0.5001.0
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/playwright-community/playwright-go v0.5101.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a
|
||||
github.com/tetratelabs/wazero v1.9.0
|
||||
github.com/yl2chen/cidranger v1.0.2
|
||||
golang.org/x/net v0.39.0
|
||||
)
|
||||
@@ -17,7 +16,6 @@ require (
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
@@ -32,7 +30,6 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
@@ -43,17 +40,15 @@ require (
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/perf v0.0.0-20250408013232-71ba5bc8ccce // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
honnef.co/go/tools v0.6.1 // indirect
|
||||
)
|
||||
|
||||
tool (
|
||||
github.com/a-h/templ/cmd/templ
|
||||
golang.org/x/perf/cmd/benchstat
|
||||
golang.org/x/tools/cmd/stringer
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
)
|
||||
|
||||
34
go.sum
34
go.sum
@@ -4,8 +4,6 @@ github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ6
|
||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
|
||||
github.com/a-h/templ v0.3.857 h1:6EqcJuGZW4OL+2iZ3MD+NnIcG7nGkaQeF2Zq5kf9ZGg=
|
||||
github.com/a-h/templ v0.3.857/go.mod h1:qhrhAkRFubE7khxLZHsBFHfX+gWwVNKbzKeF9GlPV4M=
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 h1:xlwdaKcTNVW4PtpQb8aKA4Pjy0CdJHEqvFbAnvR5m2g=
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -40,10 +38,10 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
@@ -57,13 +55,13 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
|
||||
github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
|
||||
github.com/playwright-community/playwright-go v0.5001.0 h1:EY3oB+rU9cUp6CLHguWE8VMZTwAg+83Yyb7dQqEmGLg=
|
||||
github.com/playwright-community/playwright-go v0.5001.0/go.mod h1:kBNWs/w2aJ2ZUp1wEOOFLXgOqvppFngM5OS+qyhl+ZM=
|
||||
github.com/playwright-community/playwright-go v0.5101.0 h1:gVCMZThDO76LJ/aCI27lpB8hEAWhZszeS0YB+oTxJp0=
|
||||
github.com/playwright-community/playwright-go v0.5101.0/go.mod h1:kBNWs/w2aJ2ZUp1wEOOFLXgOqvppFngM5OS+qyhl+ZM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
@@ -77,8 +75,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
@@ -96,19 +92,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/perf v0.0.0-20250408013232-71ba5bc8ccce h1:KAIyikguO7lID+oSo3Dnut9RawUS+RWK8Ejj9KPvwU4=
|
||||
golang.org/x/perf v0.0.0-20250408013232-71ba5bc8ccce/go.mod h1:tAdCL3nMN92yGFHY2TrzbGPP0q+LaOFewlib1WPJdpA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -119,8 +109,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -140,11 +128,9 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -23,8 +23,8 @@ func (c *OGTagCache) GetOGTags(url *url.URL) (map[string]string, error) {
|
||||
if errors.Is(err, syscall.ECONNREFUSED) {
|
||||
slog.Debug("Connection refused, returning empty tags")
|
||||
return nil, nil
|
||||
} else if errors.Is(err, ErrNotFound) {
|
||||
// not even worth a debug log...
|
||||
} else if errors.Is(err, ErrOgHandled) {
|
||||
// Error was handled in fetchHTMLDocument, return empty tags
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("page not found") /*todo: refactor into common errors lib? */
|
||||
emptyMap = map[string]string{} // used to indicate an empty result in the cache. Can't use nil as it would be a cache miss.
|
||||
ErrOgHandled = errors.New("og: handled error") // used to indicate that the error was handled and should not be logged
|
||||
emptyMap = map[string]string{} // used to indicate an empty result in the cache. Can't use nil as it would be a cache miss.
|
||||
)
|
||||
|
||||
func (c *OGTagCache) fetchHTMLDocument(urlStr string) (*html.Node, error) {
|
||||
@@ -31,7 +31,7 @@ func (c *OGTagCache) fetchHTMLDocument(urlStr string) (*html.Node, error) {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
slog.Debug("og: received non-OK status code", "url", urlStr, "status", resp.StatusCode)
|
||||
c.cache.Set(urlStr, emptyMap, c.ogTimeToLive) // Cache empty result for non-successful status codes
|
||||
return nil, ErrNotFound
|
||||
return nil, fmt.Errorf("%w: page not found", ErrOgHandled)
|
||||
}
|
||||
|
||||
// Check content type
|
||||
@@ -43,11 +43,13 @@ func (c *OGTagCache) fetchHTMLDocument(urlStr string) (*html.Node, error) {
|
||||
mediaType, _, err := mime.ParseMediaType(ct)
|
||||
if err != nil {
|
||||
// Malformed Content-Type header
|
||||
return nil, fmt.Errorf("invalid Content-Type '%s': %w", ct, err)
|
||||
slog.Debug("og: malformed Content-Type header", "url", urlStr, "contentType", ct)
|
||||
return nil, fmt.Errorf("%w malformed Content-Type header: %w", ErrOgHandled, err)
|
||||
}
|
||||
|
||||
if mediaType != "text/html" && mediaType != "application/xhtml+xml" {
|
||||
return nil, fmt.Errorf("unsupported Content-Type: %s", mediaType)
|
||||
slog.Debug("og: unsupported Content-Type", "url", urlStr, "contentType", mediaType)
|
||||
return nil, fmt.Errorf("%w unsupported Content-Type: %s", ErrOgHandled, mediaType)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -53,6 +52,24 @@ var (
|
||||
realIP: placeholderIP,
|
||||
userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/120.0.6099.28 Safari/537.36",
|
||||
},
|
||||
{
|
||||
name: "Amazonbot",
|
||||
action: actionDeny,
|
||||
realIP: placeholderIP,
|
||||
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)",
|
||||
},
|
||||
{
|
||||
name: "Amazonbot",
|
||||
action: actionDeny,
|
||||
realIP: placeholderIP,
|
||||
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)",
|
||||
},
|
||||
{
|
||||
name: "PerplexityAI",
|
||||
action: actionDeny,
|
||||
realIP: placeholderIP,
|
||||
userAgent: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)",
|
||||
},
|
||||
{
|
||||
name: "kagiBadIP",
|
||||
action: actionChallenge,
|
||||
@@ -81,7 +98,7 @@ const (
|
||||
actionChallenge action = "CHALLENGE"
|
||||
|
||||
placeholderIP = "fd11:5ee:bad:c0de::"
|
||||
playwrightVersion = "1.50.1"
|
||||
playwrightVersion = "1.51.1"
|
||||
)
|
||||
|
||||
type action string
|
||||
@@ -102,6 +119,9 @@ func doesNPXExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func run(t *testing.T, command string) string {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration smoke testing in short mode")
|
||||
}
|
||||
t.Helper()
|
||||
|
||||
shPath, err := exec.LookPath("sh")
|
||||
@@ -379,7 +399,7 @@ func saveScreenshot(t *testing.T, page playwright.Page) {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp("./var", "anubis-test-fail-"+strings.ReplaceAll(t.Name(), "/", "--")+"-*.png")
|
||||
f, err := os.CreateTemp("", "anubis-test-fail-*.png")
|
||||
if err != nil {
|
||||
t.Logf("could not create temporary file: %v", err)
|
||||
return
|
||||
|
||||
131
lib/anubis.go
131
lib/anubis.go
@@ -1,23 +1,19 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -35,7 +31,6 @@ import (
|
||||
"github.com/TecharoHQ/anubis/internal/ogtags"
|
||||
"github.com/TecharoHQ/anubis/lib/policy"
|
||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||
"github.com/TecharoHQ/anubis/wasm"
|
||||
"github.com/TecharoHQ/anubis/web"
|
||||
"github.com/TecharoHQ/anubis/xess"
|
||||
)
|
||||
@@ -85,7 +80,7 @@ type Options struct {
|
||||
WebmasterEmail string
|
||||
}
|
||||
|
||||
func LoadPoliciesOrDefault(fname string, defaultDifficulty uint32) (*policy.ParsedConfig, error) {
|
||||
func LoadPoliciesOrDefault(fname string, defaultDifficulty int) (*policy.ParsedConfig, error) {
|
||||
var fin io.ReadCloser
|
||||
var err error
|
||||
|
||||
@@ -127,36 +122,6 @@ func New(opts Options) (*Server, error) {
|
||||
opts: opts,
|
||||
DNSBLCache: decaymap.New[string, dnsbl.DroneBLResponse](),
|
||||
OGTags: ogtags.NewOGTagCache(opts.Target, opts.OGPassthrough, opts.OGTimeToLive),
|
||||
validators: map[string]Verifier{
|
||||
"fast": VerifierFunc(BasicSHA256Verify),
|
||||
"slow": VerifierFunc(BasicSHA256Verify),
|
||||
},
|
||||
}
|
||||
|
||||
finfos, err := fs.ReadDir(web.Static, "static/wasm")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[unexpected] can't read any webassembly files in the static folder: %w", err)
|
||||
}
|
||||
|
||||
for _, finfo := range finfos {
|
||||
fin, err := web.Static.Open("static/wasm/" + finfo.Name())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[unexpected] can't read static/wasm/%s: %w", finfo.Name(), err)
|
||||
}
|
||||
defer fin.Close()
|
||||
|
||||
name := strings.TrimSuffix(finfo.Name(), filepath.Ext(finfo.Name()))
|
||||
|
||||
runner, err := wasm.NewRunner(context.Background(), finfo.Name(), fin)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load static/wasm/%s: %w", finfo.Name(), err)
|
||||
}
|
||||
|
||||
var concurrentLimit int64 = 4
|
||||
|
||||
cv := NewConcurrentVerifier(runner, concurrentLimit)
|
||||
|
||||
result.validators[name] = cv
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
@@ -196,15 +161,13 @@ type Server struct {
|
||||
opts Options
|
||||
DNSBLCache *decaymap.Impl[string, dnsbl.DroneBLResponse]
|
||||
OGTags *ogtags.OGTagCache
|
||||
|
||||
validators map[string]Verifier
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) challengeFor(r *http.Request, difficulty uint32) string {
|
||||
func (s *Server) challengeFor(r *http.Request, difficulty int) string {
|
||||
fp := sha256.Sum256(s.priv.Seed())
|
||||
|
||||
challengeData := fmt.Sprintf(
|
||||
@@ -300,21 +263,21 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
if err != nil {
|
||||
lg.Debug("cookie not found", "path", r.URL.Path)
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ckie.Valid(); err != nil {
|
||||
lg.Debug("cookie is invalid", "err", err)
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
|
||||
if time.Now().After(ckie.Expires) && !ckie.Expires.IsZero() {
|
||||
lg.Debug("cookie expired", "path", r.URL.Path)
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,7 +288,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
if err != nil || !token.Valid {
|
||||
lg.Debug("invalid token", "path", r.URL.Path, "err", err)
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -340,7 +303,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
if !ok {
|
||||
lg.Debug("invalid token claims type", "path", r.URL.Path)
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
challenge := s.challengeFor(r, rule.Challenge.Difficulty)
|
||||
@@ -348,7 +311,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
if claims["challenge"] != challenge {
|
||||
lg.Debug("invalid challenge", "path", r.URL.Path)
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -365,7 +328,7 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
lg.Debug("invalid response", "path", r.URL.Path)
|
||||
failedValidations.Inc()
|
||||
s.ClearCookie(w)
|
||||
s.RenderIndex(w, r)
|
||||
s.RenderIndex(w, r, cr, rule)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -374,21 +337,36 @@ func (s *Server) MaybeReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
s.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr CheckResult, rule *policy.Bot) {
|
||||
lg := slog.With(
|
||||
"user_agent", r.UserAgent(),
|
||||
"accept_language", r.Header.Get("Accept-Language"),
|
||||
"priority", r.Header.Get("Priority"),
|
||||
"x-forwarded-for",
|
||||
r.Header.Get("X-Forwarded-For"),
|
||||
"x-real-ip", r.Header.Get("X-Real-Ip"),
|
||||
)
|
||||
|
||||
challenge := s.challengeFor(r, rule.Challenge.Difficulty)
|
||||
|
||||
var ogTags map[string]string = nil
|
||||
if s.opts.OGPassthrough {
|
||||
var err error
|
||||
ogTags, err = s.OGTags.GetOGTags(r.URL)
|
||||
if err != nil {
|
||||
slog.Error("failed to get OG tags", "err", err)
|
||||
lg.Error("failed to get OG tags", "err", err)
|
||||
ogTags = nil
|
||||
}
|
||||
}
|
||||
handler := internal.NoStoreCache(
|
||||
templ.Handler(
|
||||
web.BaseWithOGTags("Making sure you're not a bot!", web.Index(), ogTags),
|
||||
),
|
||||
)
|
||||
|
||||
component, err := web.BaseWithChallengeAndOGTags("Making sure you're not a bot!", web.Index(), challenge, rule.Challenge, ogTags)
|
||||
if err != nil {
|
||||
lg.Error("render failed", "err", err)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Other internal server error (contact the admin)", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
handler := internal.NoStoreCache(templ.Handler(component))
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
@@ -441,7 +419,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Internal Server Error: administrator has misconfigured Anubis. Please contact the administrator and ask them to look for the logs around \"passChallenge\".", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
lg = lg.With("check_result", cr, "algorithm", rule.Challenge.Algorithm)
|
||||
lg = lg.With("check_result", cr)
|
||||
|
||||
nonceStr := r.FormValue("nonce")
|
||||
if nonceStr == "" {
|
||||
@@ -473,52 +451,33 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) {
|
||||
response := r.FormValue("response")
|
||||
redir := r.FormValue("redir")
|
||||
|
||||
responseBytes, err := hex.DecodeString(response)
|
||||
if err != nil {
|
||||
s.ClearCookie(w)
|
||||
lg.Debug("response doesn't parse", "err", err)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response format", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
challenge := s.challengeFor(r, rule.Challenge.Difficulty)
|
||||
challengeBytes, err := hex.DecodeString(challenge)
|
||||
if err != nil {
|
||||
s.ClearCookie(w)
|
||||
lg.Debug("challenge doesn't parse", "err", err)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid internal challenge format", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
nonceRaw, err := strconv.ParseUint(nonceStr, 10, 32)
|
||||
nonce, err := strconv.Atoi(nonceStr)
|
||||
if err != nil {
|
||||
s.ClearCookie(w)
|
||||
lg.Debug("nonce doesn't parse", "err", err)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid nonce", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
nonce := uint32(nonceRaw)
|
||||
|
||||
validator, ok := s.validators[string(rule.Challenge.Algorithm)]
|
||||
if !ok {
|
||||
calcString := fmt.Sprintf("%s%d", challenge, nonce)
|
||||
calculated := internal.SHA256sum(calcString)
|
||||
|
||||
if subtle.ConstantTimeCompare([]byte(response), []byte(calculated)) != 1 {
|
||||
s.ClearCookie(w)
|
||||
lg.Debug("no validator found for algorithm", "algorithm", rule.Challenge.Algorithm)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage(fmt.Sprintf("Internal anubis error has been detected and you cannot proceed. Tried to look up a validator for algorithm %s but wasn't able to find one. Please contact the administrator of this instance of anubis", rule.Challenge.Algorithm), s.opts.WebmasterEmail)), templ.WithStatus(http.StatusInternalServerError)).ServeHTTP(w, r)
|
||||
lg.Debug("hash does not match", "got", response, "want", calculated)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
|
||||
failedValidations.Inc()
|
||||
return
|
||||
}
|
||||
|
||||
ok, err = validator.Verify(r.Context(), challengeBytes, responseBytes, nonce, rule.Challenge.Difficulty)
|
||||
if err != nil {
|
||||
// compare the leading zeroes
|
||||
if !strings.HasPrefix(response, strings.Repeat("0", rule.Challenge.Difficulty)) {
|
||||
s.ClearCookie(w)
|
||||
lg.Debug("verification error", "err", err)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Your challenge failed validation. Please go back and try your challenge again", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusBadRequest)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
s.ClearCookie(w)
|
||||
lg.Debug("response invalid")
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("Your challenge failed validation. Please go back and try your challenge again", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusBadRequest)).ServeHTTP(w, r)
|
||||
lg.Debug("difficulty check failed", "response", response, "difficulty", rule.Challenge.Difficulty)
|
||||
templ.Handler(web.Base("Oh noes!", web.ErrorPage("invalid response", s.opts.WebmasterEmail)), templ.WithStatus(http.StatusForbidden)).ServeHTTP(w, r)
|
||||
failedValidations.Inc()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,12 @@ import (
|
||||
func loadPolicies(t *testing.T, fname string) *policy.ParsedConfig {
|
||||
t.Helper()
|
||||
|
||||
policy, err := LoadPoliciesOrDefault("", anubis.DefaultDifficulty)
|
||||
anubisPolicy, err := LoadPoliciesOrDefault("", anubis.DefaultDifficulty)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return policy
|
||||
return anubisPolicy
|
||||
}
|
||||
|
||||
func spawnAnubis(t *testing.T, opts Options) *Server {
|
||||
@@ -197,7 +197,7 @@ func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
|
||||
fmt.Fprintln(w, "OK")
|
||||
})
|
||||
|
||||
for i := uint32(1); i < 10; i++ {
|
||||
for i := 1; i < 10; i++ {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
anubisPolicy, err := LoadPoliciesOrDefault("", i)
|
||||
if err != nil {
|
||||
|
||||
@@ -31,11 +31,9 @@ const (
|
||||
type Algorithm string
|
||||
|
||||
const (
|
||||
AlgorithmUnknown Algorithm = ""
|
||||
AlgorithmFast Algorithm = "fast"
|
||||
AlgorithmSlow Algorithm = "slow"
|
||||
AlgorithmArgon2ID Algorithm = "argon2id"
|
||||
AlgorithmSHA256 Algorithm = "sha256"
|
||||
AlgorithmUnknown Algorithm = ""
|
||||
AlgorithmFast Algorithm = "fast"
|
||||
AlgorithmSlow Algorithm = "slow"
|
||||
)
|
||||
|
||||
type BotConfig struct {
|
||||
@@ -103,8 +101,8 @@ func (b BotConfig) Valid() error {
|
||||
}
|
||||
|
||||
type ChallengeRules struct {
|
||||
Difficulty uint32 `json:"difficulty"`
|
||||
ReportAs uint32 `json:"report_as"`
|
||||
Difficulty int `json:"difficulty"`
|
||||
ReportAs int `json:"report_as"`
|
||||
Algorithm Algorithm `json:"algorithm"`
|
||||
}
|
||||
|
||||
@@ -126,7 +124,7 @@ func (cr ChallengeRules) Valid() error {
|
||||
}
|
||||
|
||||
switch cr.Algorithm {
|
||||
case AlgorithmFast, AlgorithmSlow, AlgorithmArgon2ID, AlgorithmSHA256, AlgorithmUnknown:
|
||||
case AlgorithmFast, AlgorithmSlow, AlgorithmUnknown:
|
||||
// do nothing, it's all good
|
||||
default:
|
||||
errs = append(errs, fmt.Errorf("%w: %q", ErrChallengeRuleHasWrongAlgorithm, cr.Algorithm))
|
||||
|
||||
@@ -27,7 +27,7 @@ type ParsedConfig struct {
|
||||
|
||||
Bots []Bot
|
||||
DNSBL bool
|
||||
DefaultDifficulty uint32
|
||||
DefaultDifficulty int
|
||||
}
|
||||
|
||||
func NewParsedConfig(orig config.Config) *ParsedConfig {
|
||||
@@ -36,7 +36,7 @@ func NewParsedConfig(orig config.Config) *ParsedConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func ParseConfig(fin io.Reader, fname string, defaultDifficulty uint32) (*ParsedConfig, error) {
|
||||
func ParseConfig(fin io.Reader, fname string, defaultDifficulty int) (*ParsedConfig, error) {
|
||||
var c config.Config
|
||||
if err := json.NewDecoder(fin).Decode(&c); err != nil {
|
||||
return nil, fmt.Errorf("can't parse policy config JSON %s: %w", fname, err)
|
||||
@@ -99,12 +99,12 @@ func ParseConfig(fin io.Reader, fname string, defaultDifficulty uint32) (*Parsed
|
||||
parsedBot.Challenge = &config.ChallengeRules{
|
||||
Difficulty: defaultDifficulty,
|
||||
ReportAs: defaultDifficulty,
|
||||
Algorithm: config.AlgorithmArgon2ID,
|
||||
Algorithm: config.AlgorithmFast,
|
||||
}
|
||||
} else {
|
||||
parsedBot.Challenge = b.Challenge
|
||||
if parsedBot.Challenge.Algorithm == config.AlgorithmUnknown {
|
||||
parsedBot.Challenge.Algorithm = config.AlgorithmArgon2ID
|
||||
parsedBot.Challenge.Algorithm = config.AlgorithmFast
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrChallengeFailed = errors.New("libanubis: challenge failed, hash does not match what the server calculated")
|
||||
ErrWrongChallengeDifficulty = errors.New("libanubis: wrong challenge difficulty")
|
||||
)
|
||||
|
||||
type Verifier interface {
|
||||
Verify(ctx context.Context, challenge, verify []byte, nonce, difficulty uint32) (bool, error)
|
||||
}
|
||||
|
||||
type VerifierFunc func(ctx context.Context, challenge, verify []byte, nonce, difficulty uint32) (bool, error)
|
||||
|
||||
func (vf VerifierFunc) Verify(ctx context.Context, challenge, verify []byte, nonce, difficulty uint32) (bool, error) {
|
||||
return vf(ctx, challenge, verify, nonce, difficulty)
|
||||
}
|
||||
|
||||
func BasicSHA256Verify(ctx context.Context, challenge, verify []byte, nonce, difficulty uint32) (bool, error) {
|
||||
h := sha256.New()
|
||||
fmt.Fprintf(h, "%x%d", challenge, nonce)
|
||||
data := h.Sum(nil)
|
||||
|
||||
if subtle.ConstantTimeCompare(data, verify) != 1 {
|
||||
return false, fmt.Errorf("%w: wanted %x, got: %x", ErrChallengeFailed, verify, data)
|
||||
}
|
||||
|
||||
if !hasLeadingZeroNibbles(data, difficulty) {
|
||||
return false, fmt.Errorf("%w: wanted %d leading zeroes in calculated data %x, but did not get it", ErrWrongChallengeDifficulty, difficulty, data)
|
||||
}
|
||||
|
||||
if !hasLeadingZeroNibbles(verify, difficulty) {
|
||||
return false, fmt.Errorf("%w: wanted %d leading zeroes in verification data %x, but did not get it", ErrWrongChallengeDifficulty, verify, difficulty)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// hasLeadingZeroNibbles checks if the first `n` nibbles (in order) are zero.
|
||||
// Nibbles are read from high to low for each byte (e.g., 0x12 -> nibbles [0x1, 0x2]).
|
||||
func hasLeadingZeroNibbles(data []byte, n uint32) bool {
|
||||
count := uint32(0)
|
||||
for _, b := range data {
|
||||
// Check high nibble (first 4 bits)
|
||||
if (b >> 4) != 0 {
|
||||
break // Non-zero found in leading nibbles
|
||||
}
|
||||
count++
|
||||
if count >= n {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check low nibble (last 4 bits)
|
||||
if (b & 0x0F) != 0 {
|
||||
break // Non-zero found in leading nibbles
|
||||
}
|
||||
count++
|
||||
if count >= n {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return count >= n
|
||||
}
|
||||
|
||||
type ConcurrentVerifier struct {
|
||||
Verifier
|
||||
sem *semaphore.Weighted
|
||||
}
|
||||
|
||||
func NewConcurrentVerifier(v Verifier, maxConcurrent int64) *ConcurrentVerifier {
|
||||
return &ConcurrentVerifier{
|
||||
Verifier: v,
|
||||
sem: semaphore.NewWeighted(maxConcurrent),
|
||||
}
|
||||
}
|
||||
|
||||
func (cv *ConcurrentVerifier) Verify(ctx context.Context, challenge, verify []byte, nonce, difficulty uint32) (bool, error) {
|
||||
if err := cv.sem.Acquire(ctx, 1); err != nil {
|
||||
return false, fmt.Errorf("can't verify solution: %w", err)
|
||||
}
|
||||
|
||||
return cv.Verifier.Verify(ctx, challenge, verify, nonce, difficulty)
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// echo -n "hi2" | sha256sum
|
||||
const hi2SHA256 = "0251f1ec2880f67631b8d0b3a62cf71a17dfa31858a323e7fc38068fcfaeded0"
|
||||
const nonce uint32 = 5
|
||||
const expectedVerifyString = "0543cbd94db5da055e82263cb775ac16f59fbbc1900645458baa197f9036ae9d"
|
||||
|
||||
func TestBasicSHA256Verify(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
challenge, err := hex.DecodeString(hi2SHA256)
|
||||
if err != nil {
|
||||
t.Fatalf("[unexpected] %s does not decode as hex", hi2SHA256)
|
||||
}
|
||||
|
||||
expectedVerify, err := hex.DecodeString(expectedVerifyString)
|
||||
if err != nil {
|
||||
t.Fatalf("[unexpected] %s does not decode as hex", expectedVerifyString)
|
||||
}
|
||||
|
||||
t.Logf("got nonce: %d", nonce)
|
||||
t.Logf("got hash: %x", expectedVerify)
|
||||
|
||||
invalidVerify := make([]byte, len(expectedVerify))
|
||||
copy(invalidVerify, expectedVerify)
|
||||
invalidVerify[len(invalidVerify)-1] ^= 0xFF // Flip the last byte
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
challenge []byte
|
||||
verify []byte
|
||||
nonce uint32
|
||||
difficulty uint32
|
||||
want bool
|
||||
expectError error
|
||||
}{
|
||||
{
|
||||
name: "valid verification",
|
||||
challenge: challenge,
|
||||
verify: expectedVerify,
|
||||
nonce: nonce,
|
||||
difficulty: 1,
|
||||
want: true,
|
||||
expectError: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid verify data",
|
||||
challenge: challenge,
|
||||
verify: invalidVerify,
|
||||
nonce: nonce,
|
||||
difficulty: 1,
|
||||
want: false,
|
||||
expectError: ErrChallengeFailed,
|
||||
},
|
||||
{
|
||||
name: "insufficient computed data difficulty",
|
||||
challenge: challenge,
|
||||
verify: expectedVerify,
|
||||
nonce: nonce,
|
||||
difficulty: 5,
|
||||
want: false,
|
||||
expectError: ErrWrongChallengeDifficulty,
|
||||
},
|
||||
{
|
||||
name: "zero difficulty",
|
||||
challenge: challenge,
|
||||
verify: expectedVerify,
|
||||
nonce: nonce,
|
||||
difficulty: 0,
|
||||
want: true,
|
||||
expectError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := BasicSHA256Verify(ctx, tc.challenge, tc.verify, tc.nonce, tc.difficulty)
|
||||
if !errors.Is(err, tc.expectError) {
|
||||
t.Errorf("BasicSHA256Verify() error = %v, expectError %v", err, tc.expectError)
|
||||
return
|
||||
}
|
||||
if got != tc.want {
|
||||
t.Errorf("BasicSHA256Verify() got = %v, want %v", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasLeadingZeroNibbles(t *testing.T) {
|
||||
for _, cs := range []struct {
|
||||
data []byte
|
||||
difficulty uint32
|
||||
valid bool
|
||||
}{
|
||||
{[]byte{0x10, 0x00}, 1, false},
|
||||
{[]byte{0x00, 0x00}, 4, true},
|
||||
{[]byte{0x01, 0x00}, 4, false},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%x-%d-%v", cs.data, cs.difficulty, cs.valid), func(t *testing.T) {
|
||||
result := hasLeadingZeroNibbles(cs.data, cs.difficulty)
|
||||
if result != cs.valid {
|
||||
t.Errorf("wanted %v, but got: %v", cs.valid, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
467
package-lock.json
generated
467
package-lock.json
generated
@@ -11,12 +11,438 @@
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.0.6",
|
||||
"cssnano-preset-advanced": "^7.0.6",
|
||||
"esbuild": "^0.25.2",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-import-url": "^7.2.0",
|
||||
"postcss-url": "^10.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
|
||||
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
|
||||
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
|
||||
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
|
||||
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
|
||||
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
|
||||
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
|
||||
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
|
||||
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
|
||||
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
|
||||
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
|
||||
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
|
||||
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@trysound/sax": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||
@@ -617,6 +1043,47 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
|
||||
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.2",
|
||||
"@esbuild/android-arm": "0.25.2",
|
||||
"@esbuild/android-arm64": "0.25.2",
|
||||
"@esbuild/android-x64": "0.25.2",
|
||||
"@esbuild/darwin-arm64": "0.25.2",
|
||||
"@esbuild/darwin-x64": "0.25.2",
|
||||
"@esbuild/freebsd-arm64": "0.25.2",
|
||||
"@esbuild/freebsd-x64": "0.25.2",
|
||||
"@esbuild/linux-arm": "0.25.2",
|
||||
"@esbuild/linux-arm64": "0.25.2",
|
||||
"@esbuild/linux-ia32": "0.25.2",
|
||||
"@esbuild/linux-loong64": "0.25.2",
|
||||
"@esbuild/linux-mips64el": "0.25.2",
|
||||
"@esbuild/linux-ppc64": "0.25.2",
|
||||
"@esbuild/linux-riscv64": "0.25.2",
|
||||
"@esbuild/linux-s390x": "0.25.2",
|
||||
"@esbuild/linux-x64": "0.25.2",
|
||||
"@esbuild/netbsd-arm64": "0.25.2",
|
||||
"@esbuild/netbsd-x64": "0.25.2",
|
||||
"@esbuild/openbsd-arm64": "0.25.2",
|
||||
"@esbuild/openbsd-x64": "0.25.2",
|
||||
"@esbuild/sunos-x64": "0.25.2",
|
||||
"@esbuild/win32-arm64": "0.25.2",
|
||||
"@esbuild/win32-ia32": "0.25.2",
|
||||
"@esbuild/win32-x64": "0.25.2"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
"scripts": {
|
||||
"test": "npm run assets && go test ./...",
|
||||
"test:integration": "npm run assets && go test -v ./internal/test",
|
||||
"assets:frontend": "go generate ./... && ./web/build.sh && ./xess/build.sh",
|
||||
"assets:wasm": "RUSTFLAGS='-C target-feature=+simd128' cargo build --release --target wasm32-unknown-unknown && sh -c 'cp -vf ./target/wasm32-unknown-unknown/release/*.wasm ./web/static/wasm'",
|
||||
"assets": "npm run assets:frontend && npm run assets:wasm",
|
||||
"assets": "go generate ./... && ./web/build.sh && ./xess/build.sh",
|
||||
"build": "npm run assets && go build -o ./var/anubis ./cmd/anubis",
|
||||
"dev": "npm run assets && go run ./cmd/anubis --use-remote-address",
|
||||
"container": "npm run assets && go run ./cmd/containerbuild",
|
||||
@@ -19,9 +17,10 @@
|
||||
"devDependencies": {
|
||||
"cssnano": "^7.0.6",
|
||||
"cssnano-preset-advanced": "^7.0.6",
|
||||
"esbuild": "^0.25.2",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-import-url": "^7.2.0",
|
||||
"postcss-url": "^10.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
pkgs/cryptography/techaro-pkgs.pub.asc
Normal file
14
pkgs/cryptography/techaro-pkgs.pub.asc
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaAU/hRYJKwYBBAHaRw8BAQdANg5d753IR7Q3NxfE+vVgpBx9w66HSzx74zEz
|
||||
2djVqZm0NlRlY2hhcm8gUGFja2FnZXMgU2lnbmF0dXJlcyA8Z3BnK3BhY2thZ2Vz
|
||||
QHRlY2hhcm8ubG9sPoiZBBMWCgBBFiEEgz9kFhFntQEFjDlHVjddot8Cq/8FAmgF
|
||||
P4UCGwMFCRLMAwAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQVjddot8C
|
||||
q/8PYwEAk1B1r8cBZi54fTOpMnkKUJnbk9wnmNLdUkRADlPRCcIBAN3Zgsjb/SXw
|
||||
nWINVPKvodwUVanDwl5uZuXtROv1mQ8GuDgEaAU/hRIKKwYBBAGXVQEFAQEHQLqY
|
||||
3RXQiv/4d7y+eGs+YE4BymAguYG44jRtHHwFERZEAwEIB4h+BBgWCgAmFiEEgz9k
|
||||
FhFntQEFjDlHVjddot8Cq/8FAmgFP4UCGwwFCRLMAwAACgkQVjddot8Cq/8WXgEA
|
||||
lMjj013kuKsSCrczDxCSH0boW6xSTfyjnC2SQ4VDj78A/i6JrmkMq0wUSevgxlGw
|
||||
ZIoXIS4aVdSnfY9LMcnklKcL
|
||||
=zIdk
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
14
pkgs/cryptography/techaro-root.pub.asc
Normal file
14
pkgs/cryptography/techaro-root.pub.asc
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaAU+KxYJKwYBBAHaRw8BAQdAroyz8ysjSTBcQgfN+StN6WAfNzPDwiTF1LvS
|
||||
bVRSu0a0KlRlY2hhcm8gUm9vdCBTaWduaW5nIEtleSA8Z3BnQHRlY2hhcm8ubG9s
|
||||
PoiZBBMWCgBBFiEEia0uWmNauBlpQAXmU41NIwnMwuwFAmgFPisCGwMFCRLMAwAF
|
||||
CwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQU41NIwnMwuxKFQD/ZovOrWC3
|
||||
DKcrYYKpCrX30iWDd+U2hqlPFFvRKhFx52UA+gO+UYeCBy/dnxc0GrNPE84yWQqr
|
||||
9eas/yEf1rsMInIEuDgEaAU+KxIKKwYBBAGXVQEFAQEHQAaSsoWAStlNTHgN9xuW
|
||||
aVsxZ9DQzPlD2osWHuvXCwknAwEIB4h+BBgWCgAmFiEEia0uWmNauBlpQAXmU41N
|
||||
IwnMwuwFAmgFPisCGwwFCRLMAwAACgkQU41NIwnMwuxs+AEAijEEHvssBYt80YZW
|
||||
/jCrp3vuD6aTFzb5NzvdQafPH5AA/0dt5ayS/vu31z2YTfSg5WNGWKvOvyGAG6jz
|
||||
TS5tWMYJ
|
||||
=uI7D
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
2
pkgs/cryptography/var/.gitignore
vendored
Normal file
2
pkgs/cryptography/var/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
29
pkgs/cryptography/yeetfile.js
Normal file
29
pkgs/cryptography/yeetfile.js
Normal file
@@ -0,0 +1,29 @@
|
||||
rpm.build({
|
||||
name: "techaro-repo-keys",
|
||||
description: "Public keys for techaro.lol RPM packages",
|
||||
homepage: "https://techaro.lol",
|
||||
license: "MIT",
|
||||
goarch: "all",
|
||||
version: "1.0.0",
|
||||
|
||||
build: (out) => {
|
||||
yeet.run(`mkdir`, `-p`, `${out}/etc/pki/rpm-gpg/techaro.lol-keys`);
|
||||
file.install("./techaro-pkgs.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-pkgs.asc`);
|
||||
file.install("./techaro-root.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-root.asc`);
|
||||
},
|
||||
});
|
||||
|
||||
deb.build({
|
||||
name: "techaro-repo-keys",
|
||||
description: "Public keys for techaro.lol RPM packages",
|
||||
homepage: "https://techaro.lol",
|
||||
license: "MIT",
|
||||
goarch: "all",
|
||||
version: "1.0.0",
|
||||
|
||||
build: (out) => {
|
||||
yeet.run(`mkdir`, `-p`, `${out}/usr/share/keyrings`);
|
||||
file.install("./techaro-pkgs.pub.asc", `${out}/usr/share/keyrings/techaro-pkgs.asc`);
|
||||
file.install("./techaro-root.pub.asc", `${out}/usr/share/keyrings/techaro-root.asc`);
|
||||
},
|
||||
});
|
||||
14
pkgs/repos/techaro-pkgs.pub.asc
Normal file
14
pkgs/repos/techaro-pkgs.pub.asc
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaAU/hRYJKwYBBAHaRw8BAQdANg5d753IR7Q3NxfE+vVgpBx9w66HSzx74zEz
|
||||
2djVqZm0NlRlY2hhcm8gUGFja2FnZXMgU2lnbmF0dXJlcyA8Z3BnK3BhY2thZ2Vz
|
||||
QHRlY2hhcm8ubG9sPoiZBBMWCgBBFiEEgz9kFhFntQEFjDlHVjddot8Cq/8FAmgF
|
||||
P4UCGwMFCRLMAwAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQVjddot8C
|
||||
q/8PYwEAk1B1r8cBZi54fTOpMnkKUJnbk9wnmNLdUkRADlPRCcIBAN3Zgsjb/SXw
|
||||
nWINVPKvodwUVanDwl5uZuXtROv1mQ8GuDgEaAU/hRIKKwYBBAGXVQEFAQEHQLqY
|
||||
3RXQiv/4d7y+eGs+YE4BymAguYG44jRtHHwFERZEAwEIB4h+BBgWCgAmFiEEgz9k
|
||||
FhFntQEFjDlHVjddot8Cq/8FAmgFP4UCGwwFCRLMAwAACgkQVjddot8Cq/8WXgEA
|
||||
lMjj013kuKsSCrczDxCSH0boW6xSTfyjnC2SQ4VDj78A/i6JrmkMq0wUSevgxlGw
|
||||
ZIoXIS4aVdSnfY9LMcnklKcL
|
||||
=zIdk
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
14
pkgs/repos/techaro-root.pub.asc
Normal file
14
pkgs/repos/techaro-root.pub.asc
Normal file
@@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEaAU+KxYJKwYBBAHaRw8BAQdAroyz8ysjSTBcQgfN+StN6WAfNzPDwiTF1LvS
|
||||
bVRSu0a0KlRlY2hhcm8gUm9vdCBTaWduaW5nIEtleSA8Z3BnQHRlY2hhcm8ubG9s
|
||||
PoiZBBMWCgBBFiEEia0uWmNauBlpQAXmU41NIwnMwuwFAmgFPisCGwMFCRLMAwAF
|
||||
CwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQU41NIwnMwuxKFQD/ZovOrWC3
|
||||
DKcrYYKpCrX30iWDd+U2hqlPFFvRKhFx52UA+gO+UYeCBy/dnxc0GrNPE84yWQqr
|
||||
9eas/yEf1rsMInIEuDgEaAU+KxIKKwYBBAGXVQEFAQEHQAaSsoWAStlNTHgN9xuW
|
||||
aVsxZ9DQzPlD2osWHuvXCwknAwEIB4h+BBgWCgAmFiEEia0uWmNauBlpQAXmU41N
|
||||
IwnMwuwFAmgFPisCGwwFCRLMAwAACgkQU41NIwnMwuxs+AEAijEEHvssBYt80YZW
|
||||
/jCrp3vuD6aTFzb5NzvdQafPH5AA/0dt5ayS/vu31z2YTfSg5WNGWKvOvyGAG6jz
|
||||
TS5tWMYJ
|
||||
=uI7D
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
6
pkgs/repos/techaro-stable.repo
Normal file
6
pkgs/repos/techaro-stable.repo
Normal file
@@ -0,0 +1,6 @@
|
||||
[techaro-stable]
|
||||
name=Techaro Packages (stable)
|
||||
baseurl=https://pkgs.techaro.lol/rpm/stable
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=file:///etc/pki/rpm-gpg/techaro.lol-keys/techaro-pkgs.asc
|
||||
6
pkgs/repos/techaro-unstable.repo
Normal file
6
pkgs/repos/techaro-unstable.repo
Normal file
@@ -0,0 +1,6 @@
|
||||
[techaro-unstable]
|
||||
name=Techaro Packages (unstable)
|
||||
baseurl=https://pkgs.techaro.lol/rpm/unstable
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=file:///etc/pki/rpm-gpg/techaro.lol-keys/techaro-pkgs.asc
|
||||
2
pkgs/repos/var/.gitignore
vendored
Normal file
2
pkgs/repos/var/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
29
pkgs/repos/yeetfile.js
Normal file
29
pkgs/repos/yeetfile.js
Normal file
@@ -0,0 +1,29 @@
|
||||
rpm.build({
|
||||
name: "techaro-repos-stable",
|
||||
description: "Repo definitions for stable Techaro packages",
|
||||
homepage: "https://techaro.lol",
|
||||
license: "MIT",
|
||||
goarch: "all",
|
||||
version: "1.0.0",
|
||||
|
||||
build: (out) => {
|
||||
file.install("./techaro-pkgs.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-pkgs.asc`);
|
||||
file.install("./techaro-root.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-root.asc`);
|
||||
file.install("./techaro-stable.repo", `${out}/etc/yum.repos.d/techaro-stable.repo`);
|
||||
},
|
||||
});
|
||||
|
||||
rpm.build({
|
||||
name: "techaro-repos-unstable",
|
||||
description: "Repo definitions for unstable Techaro packages",
|
||||
homepage: "https://techaro.lol",
|
||||
license: "MIT",
|
||||
goarch: "all",
|
||||
version: "1.0.0",
|
||||
|
||||
build: (out) => {
|
||||
file.install("./techaro-pkgs.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-pkgs.asc`);
|
||||
file.install("./techaro-root.pub.asc", `${out}/etc/pki/rpm-gpg/techaro.lol-keys/techaro-root.asc`);
|
||||
file.install("./techaro-stable.repo", `${out}/etc/yum.repos.d/techaro-stable.repo`);
|
||||
},
|
||||
});
|
||||
34
run/anubis.freebsd
Normal file
34
run/anubis.freebsd
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
# PROVIDE: anubis
|
||||
# REQUIRE: NETWORKING
|
||||
# KEYWORD: shutdown
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name=anubis
|
||||
rcvar=anubis_enable
|
||||
|
||||
load_rc_config ${name}
|
||||
|
||||
: ${anubis_enable="NO"}
|
||||
: ${anubis_user="anubis"}
|
||||
: ${anubis_bin="/usr/local/bin/anubis"}
|
||||
: ${anubis_environment_file="/etc/anubis.env"}
|
||||
|
||||
command=/usr/sbin/daemon
|
||||
procname=${anubis_bin}
|
||||
pidfile=/var/run/anubis.pid
|
||||
logfile=/var/log/anubis.log
|
||||
command_args="-c -f -p ${pidfile} -o ${logfile} ${procname}"
|
||||
start_precmd=anubis_precmd
|
||||
|
||||
anubis_precmd () {
|
||||
export $(xargs < ${anubis_environment_file})
|
||||
if [ ! -f ${logfile} ]; then
|
||||
install -o anubis /dev/null ${logfile}
|
||||
fi
|
||||
install -o anubis /dev/null ${pidfile}
|
||||
}
|
||||
|
||||
run_rc_command "$1"
|
||||
@@ -1,4 +0,0 @@
|
||||
[toolchain]
|
||||
channel = "stable"
|
||||
targets = ["wasm32-unknown-unknown"]
|
||||
profile = "minimal"
|
||||
@@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "anubis"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
wee_alloc = "0.4"
|
||||
@@ -1,60 +0,0 @@
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
extern crate wee_alloc;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod hostimport {
|
||||
use crate::{DATA_BUFFER, DATA_LENGTH};
|
||||
|
||||
#[link(wasm_import_module = "anubis")]
|
||||
unsafe extern "C" {
|
||||
/// The runtime expects this function to be defined. It is called whenever the Anubis check
|
||||
/// worker processes about 1024 hashes. This can be a no-op if you want.
|
||||
fn anubis_update_nonce(nonce: u32);
|
||||
}
|
||||
|
||||
/// Safe wrapper to `anubis_update_nonce`.
|
||||
pub fn update_nonce(nonce: u32) {
|
||||
unsafe {
|
||||
anubis_update_nonce(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_ptr() -> *const u8 {
|
||||
let challenge = &DATA_BUFFER;
|
||||
challenge.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn set_data_length(len: u32) {
|
||||
let mut data_length = DATA_LENGTH.lock().unwrap();
|
||||
*data_length = len as usize;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod hostimport {
|
||||
pub fn update_nonce(_nonce: u32) {
|
||||
// This is intentionally blank
|
||||
}
|
||||
}
|
||||
|
||||
/// The data buffer is a bit weird in that it doesn't have an explicit length as it can
|
||||
/// and will change depending on the challenge input that was sent by the server.
|
||||
/// However, it can only fit 4096 bytes of data (one amd64 machine page). This is
|
||||
/// slightly overkill for the purposes of an Anubis check, but it's fine to assume
|
||||
/// that the browser can afford this much ram usage.
|
||||
///
|
||||
/// Callers should fetch the base data pointer, write up to 4096 bytes, and then
|
||||
/// `set_data_length` the number of bytes they have written
|
||||
///
|
||||
/// This is also functionally a write-only buffer, so it doesn't really matter that
|
||||
/// the length of this buffer isn't exposed.
|
||||
pub static DATA_BUFFER: LazyLock<[u8; 4096]> = LazyLock::new(|| [0; 4096]);
|
||||
pub static DATA_LENGTH: LazyLock<Mutex<usize>> = LazyLock::new(|| Mutex::new(0));
|
||||
|
||||
pub use hostimport::update_nonce;
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
name = "argon2id"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
argon2 = "0.5"
|
||||
|
||||
anubis = { path = "../../anubis" }
|
||||
|
||||
[lints.clippy]
|
||||
nursery = { level = "warn", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
unwrap_used = "warn"
|
||||
uninlined_format_args = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
cognitive_complexity = "allow"
|
||||
@@ -1,176 +0,0 @@
|
||||
use anubis::{DATA_BUFFER, DATA_LENGTH, update_nonce};
|
||||
use argon2::Argon2;
|
||||
use std::boxed::Box;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
/// SHA-256 hashes are 32 bytes (256 bits). These are stored in static buffers due to the
|
||||
/// fact that you cannot easily pass data from host space to WebAssembly space.
|
||||
pub static RESULT_HASH: LazyLock<Mutex<[u8; 32]>> = LazyLock::new(|| Mutex::new([0; 32]));
|
||||
|
||||
pub static VERIFICATION_HASH: LazyLock<Box<Mutex<[u8; 32]>>> =
|
||||
LazyLock::new(|| Box::new(Mutex::new([0; 32])));
|
||||
|
||||
/// Core validation function. Compare each bit in the hash by progressively masking bits until
|
||||
/// some are found to not be matching.
|
||||
///
|
||||
/// There are probably more clever ways to do this, likely involving lookup tables or something
|
||||
/// really fun like that. However in my testing this lets us get up to 200 kilohashes per second
|
||||
/// on my Ryzen 7950x3D, up from about 50 kilohashes per second in JavaScript.
|
||||
fn validate(hash: &[u8], difficulty: u32) -> bool {
|
||||
let mut remaining = difficulty;
|
||||
for &byte in hash {
|
||||
// If we're out of bits to check, exit. This is all good.
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
// If there are more than 8 bits remaining, the entire byte should be a
|
||||
// zero. This fast-path compares the byte to 0 and if it matches, subtract
|
||||
// 8 bits.
|
||||
if remaining >= 8 {
|
||||
if byte != 0 {
|
||||
return false;
|
||||
}
|
||||
remaining -= 8;
|
||||
} else {
|
||||
// Otherwise mask off individual bits and check against them.
|
||||
let mask = 0xFF << (8 - remaining);
|
||||
if (byte & mask) != 0 {
|
||||
return false;
|
||||
}
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Computes hash for given nonce.
|
||||
///
|
||||
/// This differs from the JavaScript implementations by constructing the hash differently. In
|
||||
/// JavaScript implementations, the SHA-256 input is the result of appending the nonce as an
|
||||
/// integer to the hex-formatted challenge, eg:
|
||||
///
|
||||
/// sha256(`${challenge}${nonce}`);
|
||||
///
|
||||
/// This **does work**, however I think that this can be done a bit better by operating on the
|
||||
/// challenge bytes _directly_ and treating the nonce as a salt.
|
||||
///
|
||||
/// The nonce is also randomly encoded in either big or little endian depending on the last
|
||||
/// byte of the data buffer in an effort to make it more annoying to automate with GPUs.
|
||||
fn compute_hash(nonce: u32) -> [u8; 32] {
|
||||
let data = &DATA_BUFFER;
|
||||
let data_len = *DATA_LENGTH.lock().unwrap();
|
||||
let use_le = data[data_len - 1] >= 128;
|
||||
let mut result = [0u8; 32];
|
||||
|
||||
let nonce = nonce as u64;
|
||||
|
||||
let data_slice = &data[..data_len];
|
||||
|
||||
let nonce = if use_le {
|
||||
nonce.to_le_bytes()
|
||||
} else {
|
||||
nonce.to_be_bytes()
|
||||
};
|
||||
|
||||
let argon2 = Argon2::default();
|
||||
argon2
|
||||
.hash_password_into(&data_slice, &nonce, &mut result)
|
||||
.unwrap();
|
||||
result
|
||||
}
|
||||
|
||||
/// This function is the main entrypoint for the Anubis proof of work implementation.
|
||||
///
|
||||
/// This expects `DATA_BUFFER` to be pre-populated with the challenge value as "raw bytes".
|
||||
/// The definition of what goes in the data buffer is an exercise for the implementor, but
|
||||
/// for SHA-256 we store the hash as "raw bytes". The data buffer is intentionally oversized
|
||||
/// so that the challenge value can be expanded in the future.
|
||||
///
|
||||
/// `difficulty` is the number of leading bits that must match `0` in order for the
|
||||
/// challenge to be successfully passed. This will be validated by the server.
|
||||
///
|
||||
/// `initial_nonce` is the initial value of the nonce (number used once). This nonce will be
|
||||
/// appended to the challenge value in order to find a hash matching the specified
|
||||
/// difficulty.
|
||||
///
|
||||
/// `iterand` (noun form of iterate) is the amount that the nonce should be increased by
|
||||
/// every iteration of the proof of work loop. This will vary by how many threads are
|
||||
/// running the proof-of-work check, and also functions as a thread ID. This prevents
|
||||
/// wasting CPU time retrying a hash+nonce pair that likely won't work.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn anubis_work(difficulty: u32, initial_nonce: u32, iterand: u32) -> u32 {
|
||||
let mut nonce = initial_nonce;
|
||||
|
||||
loop {
|
||||
let hash = compute_hash(nonce);
|
||||
|
||||
if validate(&hash, difficulty) {
|
||||
// If the challenge worked, copy the bytes into `RESULT_HASH` so the runtime
|
||||
// can pick it up.
|
||||
let mut challenge = RESULT_HASH.lock().unwrap();
|
||||
challenge.copy_from_slice(&hash);
|
||||
return nonce;
|
||||
}
|
||||
|
||||
let old_nonce = nonce;
|
||||
nonce = nonce.wrapping_add(iterand);
|
||||
|
||||
// send a progress update every 1024 iterations. since each thread checks
|
||||
// separate values, one simple way to do this is by bit masking the
|
||||
// nonce for multiples of 1024. unfortunately, if the number of threads
|
||||
// is not prime, only some of the threads will be sending the status
|
||||
// update and they will get behind the others. this is slightly more
|
||||
// complicated but ensures an even distribution between threads.
|
||||
if nonce > old_nonce + 1023 && (nonce >> 10) % iterand == initial_nonce {
|
||||
update_nonce(nonce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is called by the server in order to validate a proof-of-work challenge.
|
||||
/// This expects `DATA_BUFFER` to be set to the challenge value and `VERIFICATION_HASH` to
|
||||
/// be set to the "raw bytes" of the SHA-256 hash that the client calculated.
|
||||
///
|
||||
/// If everything is good, it returns true. Otherwise, it returns false.
|
||||
///
|
||||
/// XXX(Xe): this could probably return an error code for what step fails, but this is fine
|
||||
/// for now.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn anubis_validate(nonce: u32, difficulty: u32) -> bool {
|
||||
let computed = compute_hash(nonce);
|
||||
let valid = validate(&computed, difficulty);
|
||||
if !valid {
|
||||
return false;
|
||||
}
|
||||
|
||||
let verification = VERIFICATION_HASH.lock().unwrap();
|
||||
computed == *verification
|
||||
}
|
||||
|
||||
// These functions exist to give pointers and lengths to the runtime around the Anubis
|
||||
// checks, this allows JavaScript and Go to safely manipulate the memory layout that Rust
|
||||
// has statically allocated at compile time without having to assume how the Rust compiler
|
||||
// is going to lay it out.
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn result_hash_ptr() -> *const u8 {
|
||||
let challenge = RESULT_HASH.lock().unwrap();
|
||||
challenge.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn result_hash_size() -> usize {
|
||||
RESULT_HASH.lock().unwrap().len()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn verification_hash_ptr() -> *const u8 {
|
||||
let verification = VERIFICATION_HASH.lock().unwrap();
|
||||
verification.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn verification_hash_size() -> usize {
|
||||
VERIFICATION_HASH.lock().unwrap().len()
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
name = "equix"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
equix = "0.2"
|
||||
|
||||
anubis = { path = "../../anubis" }
|
||||
|
||||
[lints.clippy]
|
||||
nursery = { level = "warn", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
unwrap_used = "warn"
|
||||
uninlined_format_args = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
cognitive_complexity = "allow"
|
||||
@@ -1,75 +0,0 @@
|
||||
use anubis::{DATA_BUFFER, DATA_LENGTH, update_nonce};
|
||||
use std::boxed::Box;
|
||||
use std::mem::size_of;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
pub static RESULT_HASH: LazyLock<Mutex<[u8; 16]>> = LazyLock::new(|| Mutex::new([0; 16]));
|
||||
|
||||
pub static VERIFICATION_HASH: LazyLock<Box<Mutex<[u8; 16]>>> =
|
||||
LazyLock::new(|| Box::new(Mutex::new([0; 16])));
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn anubis_work(_difficulty: u32, initial_nonce: u32, iterand: u32) -> u32 {
|
||||
let data = &mut DATA_BUFFER.clone();
|
||||
let mut data_len = DATA_LENGTH.lock().unwrap();
|
||||
|
||||
// Ensure there's enough space in the buffer for the nonce (4 bytes)
|
||||
if *data_len + size_of::<u32>() > data.len() {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
unreachable!();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
panic!("Not enough space in DATA_BUFFER to write nonce");
|
||||
}
|
||||
|
||||
let mut nonce = initial_nonce;
|
||||
|
||||
loop {
|
||||
let nonce_bytes = nonce.to_le_bytes();
|
||||
let start = *data_len;
|
||||
let end = start + size_of::<u32>();
|
||||
data[start..end].copy_from_slice(&nonce_bytes);
|
||||
|
||||
// Update the data length
|
||||
*data_len += size_of::<u32>();
|
||||
let data_slice = &data[..*data_len];
|
||||
|
||||
let result = equix::solve(data_slice).unwrap();
|
||||
|
||||
if result.len() == 0 {
|
||||
nonce += iterand;
|
||||
update_nonce(nonce);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut challenge = RESULT_HASH.lock().unwrap();
|
||||
challenge.copy_from_slice(&result[0].to_bytes());
|
||||
return nonce;
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn anubis_validate(nonce: u32, difficulty: u32) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn result_hash_ptr() -> *const u8 {
|
||||
let challenge = RESULT_HASH.lock().unwrap();
|
||||
challenge.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn result_hash_size() -> usize {
|
||||
RESULT_HASH.lock().unwrap().len()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn verification_hash_ptr() -> *const u8 {
|
||||
let verification = VERIFICATION_HASH.lock().unwrap();
|
||||
verification.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn verification_hash_size() -> usize {
|
||||
VERIFICATION_HASH.lock().unwrap().len()
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
name = "sha256"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
sha2 = "0.11.0-pre.5"
|
||||
|
||||
anubis = { path = "../../anubis" }
|
||||
|
||||
[lints.clippy]
|
||||
nursery = { level = "warn", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
unwrap_used = "warn"
|
||||
uninlined_format_args = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
cognitive_complexity = "allow"
|
||||
@@ -1 +0,0 @@
|
||||
<script src="run.js" type="module"></script>
|
||||
@@ -1,105 +0,0 @@
|
||||
// Load and instantiate the .wasm file
|
||||
const response = await fetch("sha256.wasm");
|
||||
|
||||
const importObject = {
|
||||
anubis: {
|
||||
anubis_update_nonce: (nonce) => {
|
||||
console.log(`Received nonce update: ${nonce}`);
|
||||
// Your logic here
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const module = await WebAssembly.compileStreaming(response);
|
||||
const instance = await WebAssembly.instantiate(module, importObject);
|
||||
|
||||
// Get exports
|
||||
const {
|
||||
anubis_work,
|
||||
anubis_validate,
|
||||
data_ptr,
|
||||
result_hash_ptr,
|
||||
result_hash_size,
|
||||
verification_hash_ptr,
|
||||
verification_hash_size,
|
||||
set_data_length,
|
||||
memory
|
||||
} = instance.exports;
|
||||
|
||||
console.log(instance.exports);
|
||||
|
||||
function uint8ArrayToHex(arr) {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
function hexToUint8Array(hexString) {
|
||||
// Remove whitespace and optional '0x' prefix
|
||||
hexString = hexString.replace(/\s+/g, '').replace(/^0x/, '');
|
||||
|
||||
// Check for valid length
|
||||
if (hexString.length % 2 !== 0) {
|
||||
throw new Error('Invalid hex string length');
|
||||
}
|
||||
|
||||
// Check for valid characters
|
||||
if (!/^[0-9a-fA-F]+$/.test(hexString)) {
|
||||
throw new Error('Invalid hex characters');
|
||||
}
|
||||
|
||||
// Convert to Uint8Array
|
||||
const byteArray = new Uint8Array(hexString.length / 2);
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
const byteValue = parseInt(hexString.substr(i * 2, 2), 16);
|
||||
byteArray[i] = byteValue;
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
// Write data to buffer
|
||||
function writeToBuffer(data) {
|
||||
if (data.length > 1024) throw new Error("Data exceeds buffer size");
|
||||
|
||||
// Get pointer and create view
|
||||
const offset = data_ptr();
|
||||
const buffer = new Uint8Array(memory.buffer, offset, data.length);
|
||||
|
||||
// Copy data
|
||||
buffer.set(data);
|
||||
|
||||
// Set data length
|
||||
set_data_length(data.length);
|
||||
}
|
||||
|
||||
function readFromChallenge() {
|
||||
const offset = result_hash_ptr();
|
||||
const buffer = new Uint8Array(memory.buffer, offset, result_hash_size());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
const data = hexToUint8Array("98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4");
|
||||
writeToBuffer(data);
|
||||
|
||||
// Call work function
|
||||
const t0 = Date.now();
|
||||
const nonce = anubis_work(16, 0, 1);
|
||||
const t1 = Date.now();
|
||||
|
||||
console.log(`Done! Took ${t1 - t0}ms, ${nonce} iterations`);
|
||||
|
||||
const challengeBuffer = readFromChallenge();
|
||||
|
||||
{
|
||||
const buffer = new Uint8Array(memory.buffer, verification_hash_ptr(), verification_hash_size());
|
||||
buffer.set(challengeBuffer);
|
||||
}
|
||||
|
||||
// Validate
|
||||
const isValid = anubis_validate(nonce, 10) === 1;
|
||||
console.log(isValid);
|
||||
|
||||
console.log(uint8ArrayToHex(readFromChallenge()));
|
||||
@@ -1,171 +0,0 @@
|
||||
use anubis::{DATA_BUFFER, DATA_LENGTH, update_nonce};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::boxed::Box;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
/// SHA-256 hashes are 32 bytes (256 bits). These are stored in static buffers due to the
|
||||
/// fact that you cannot easily pass data from host space to WebAssembly space.
|
||||
pub static RESULT_HASH: LazyLock<Box<Mutex<[u8; 32]>>> =
|
||||
LazyLock::new(|| Box::new(Mutex::new([0; 32])));
|
||||
|
||||
pub static VERIFICATION_HASH: LazyLock<Box<Mutex<[u8; 32]>>> =
|
||||
LazyLock::new(|| Box::new(Mutex::new([0; 32])));
|
||||
|
||||
/// Core validation function. Compare each bit in the hash by progressively masking bits until
|
||||
/// some are found to not be matching.
|
||||
///
|
||||
/// There are probably more clever ways to do this, likely involving lookup tables or something
|
||||
/// really fun like that. However in my testing this lets us get up to 200 kilohashes per second
|
||||
/// on my Ryzen 7950x3D, up from about 50 kilohashes per second in JavaScript.
|
||||
fn validate(hash: &[u8], difficulty: u32) -> bool {
|
||||
let mut remaining = difficulty;
|
||||
for &byte in hash {
|
||||
// If we're out of bits to check, exit. This is all good.
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
// If there are more than 8 bits remaining, the entire byte should be a
|
||||
// zero. This fast-path compares the byte to 0 and if it matches, subtract
|
||||
// 8 bits.
|
||||
if remaining >= 8 {
|
||||
if byte != 0 {
|
||||
return false;
|
||||
}
|
||||
remaining -= 8;
|
||||
} else {
|
||||
// Otherwise mask off individual bits and check against them.
|
||||
let mask = 0xFF << (8 - remaining);
|
||||
if (byte & mask) != 0 {
|
||||
return false;
|
||||
}
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Computes hash for given nonce.
|
||||
///
|
||||
/// This differs from the JavaScript implementations by constructing the hash differently. In
|
||||
/// JavaScript implementations, the SHA-256 input is the result of appending the nonce as an
|
||||
/// integer to the hex-formatted challenge, eg:
|
||||
///
|
||||
/// sha256(`${challenge}${nonce}`);
|
||||
///
|
||||
/// This **does work**, however I think that this can be done a bit better by operating on the
|
||||
/// challenge bytes _directly_ and treating the nonce as a salt.
|
||||
///
|
||||
/// The nonce is also randomly encoded in either big or little endian depending on the last
|
||||
/// byte of the data buffer in an effort to make it more annoying to automate with GPUs.
|
||||
fn compute_hash(nonce: u32) -> [u8; 32] {
|
||||
let data = &DATA_BUFFER;
|
||||
let data_len = *DATA_LENGTH.lock().unwrap();
|
||||
let use_le = data[data_len - 1] >= 128;
|
||||
|
||||
let data_slice = &data[..data_len];
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(data_slice);
|
||||
hasher.update(if use_le {
|
||||
nonce.to_le_bytes()
|
||||
} else {
|
||||
nonce.to_be_bytes()
|
||||
});
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
/// This function is the main entrypoint for the Anubis proof of work implementation.
|
||||
///
|
||||
/// This expects `DATA_BUFFER` to be pre-populated with the challenge value as "raw bytes".
|
||||
/// The definition of what goes in the data buffer is an exercise for the implementor, but
|
||||
/// for SHA-256 we store the hash as "raw bytes". The data buffer is intentionally oversized
|
||||
/// so that the challenge value can be expanded in the future.
|
||||
///
|
||||
/// `difficulty` is the number of leading bits that must match `0` in order for the
|
||||
/// challenge to be successfully passed. This will be validated by the server.
|
||||
///
|
||||
/// `initial_nonce` is the initial value of the nonce (number used once). This nonce will be
|
||||
/// appended to the challenge value in order to find a hash matching the specified
|
||||
/// difficulty.
|
||||
///
|
||||
/// `iterand` (noun form of iterate) is the amount that the nonce should be increased by
|
||||
/// every iteration of the proof of work loop. This will vary by how many threads are
|
||||
/// running the proof-of-work check, and also functions as a thread ID. This prevents
|
||||
/// wasting CPU time retrying a hash+nonce pair that likely won't work.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn anubis_work(difficulty: u32, initial_nonce: u32, iterand: u32) -> u32 {
|
||||
let mut nonce = initial_nonce;
|
||||
|
||||
loop {
|
||||
let hash = compute_hash(nonce);
|
||||
|
||||
if validate(&hash, difficulty) {
|
||||
// If the challenge worked, copy the bytes into `RESULT_HASH` so the runtime
|
||||
// can pick it up.
|
||||
let mut challenge = RESULT_HASH.lock().unwrap();
|
||||
challenge.copy_from_slice(&hash);
|
||||
return nonce;
|
||||
}
|
||||
|
||||
let old_nonce = nonce;
|
||||
nonce = nonce.wrapping_add(iterand);
|
||||
|
||||
// send a progress update every 1024 iterations. since each thread checks
|
||||
// separate values, one simple way to do this is by bit masking the
|
||||
// nonce for multiples of 1024. unfortunately, if the number of threads
|
||||
// is not prime, only some of the threads will be sending the status
|
||||
// update and they will get behind the others. this is slightly more
|
||||
// complicated but ensures an even distribution between threads.
|
||||
if nonce > old_nonce | 1023 && (nonce >> 10) % iterand == initial_nonce {
|
||||
update_nonce(nonce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is called by the server in order to validate a proof-of-work challenge.
|
||||
/// This expects `DATA_BUFFER` to be set to the challenge value and `VERIFICATION_HASH` to
|
||||
/// be set to the "raw bytes" of the SHA-256 hash that the client calculated.
|
||||
///
|
||||
/// If everything is good, it returns true. Otherwise, it returns false.
|
||||
///
|
||||
/// XXX(Xe): this could probably return an error code for what step fails, but this is fine
|
||||
/// for now.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn anubis_validate(nonce: u32, difficulty: u32) -> bool {
|
||||
let computed = compute_hash(nonce);
|
||||
let valid = validate(&computed, difficulty);
|
||||
if !valid {
|
||||
return false;
|
||||
}
|
||||
|
||||
let verification = VERIFICATION_HASH.lock().unwrap();
|
||||
computed == *verification
|
||||
}
|
||||
|
||||
// These functions exist to give pointers and lengths to the runtime around the Anubis
|
||||
// checks, this allows JavaScript and Go to safely manipulate the memory layout that Rust
|
||||
// has statically allocated at compile time without having to assume how the Rust compiler
|
||||
// is going to lay it out.
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn result_hash_ptr() -> *const u8 {
|
||||
let challenge = RESULT_HASH.lock().unwrap();
|
||||
challenge.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn result_hash_size() -> usize {
|
||||
RESULT_HASH.lock().unwrap().len()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn verification_hash_ptr() -> *const u8 {
|
||||
let verification = VERIFICATION_HASH.lock().unwrap();
|
||||
verification.as_ptr()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn verification_hash_size() -> usize {
|
||||
VERIFICATION_HASH.lock().unwrap().len()
|
||||
}
|
||||
299
wasm/wasm.go
299
wasm/wasm.go
@@ -1,299 +0,0 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
func UpdateNonce(uint32) {}
|
||||
|
||||
var (
|
||||
validationTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Name: "anubis_wasm_validation_time",
|
||||
Help: "The time taken for the validation function to run per checker (nanoseconds)",
|
||||
Buckets: prometheus.ExponentialBucketsRange(1, math.Pow(2, 31), 32),
|
||||
}, []string{"fname"})
|
||||
|
||||
validationCount = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "anubis_wasm_validation",
|
||||
Help: "The number of times the validation logic has been run and its success rate",
|
||||
}, []string{"fname", "success"})
|
||||
)
|
||||
|
||||
type Runner struct {
|
||||
r wazero.Runtime
|
||||
code wazero.CompiledModule
|
||||
fname string
|
||||
}
|
||||
|
||||
func NewRunner(ctx context.Context, fname string, fin io.ReadCloser) (*Runner, error) {
|
||||
data, err := io.ReadAll(fin)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wasm: can't read from fin: %w", err)
|
||||
}
|
||||
|
||||
r := wazero.NewRuntime(ctx)
|
||||
|
||||
_, err = r.NewHostModuleBuilder("anubis").
|
||||
NewFunctionBuilder().
|
||||
WithFunc(func(context.Context, uint32) {}).
|
||||
Export("anubis_update_nonce").
|
||||
Instantiate(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wasm: can't export anubis_update_nonce: %w", err)
|
||||
}
|
||||
|
||||
code, err := r.CompileModule(ctx, data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wasm: can't compile module: %w", err)
|
||||
}
|
||||
|
||||
result := &Runner{
|
||||
r: r,
|
||||
code: code,
|
||||
fname: fname,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *Runner) checkExports(module api.Module) error {
|
||||
funcs := []string{
|
||||
"anubis_work",
|
||||
"anubis_validate",
|
||||
"data_ptr",
|
||||
"set_data_length",
|
||||
"result_hash_ptr",
|
||||
"result_hash_size",
|
||||
"verification_hash_ptr",
|
||||
"verification_hash_size",
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
for _, fun := range funcs {
|
||||
if module.ExportedFunction(fun) == nil {
|
||||
errs = append(errs, fmt.Errorf("function %s is not defined", fun))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) anubisWork(ctx context.Context, module api.Module, difficulty, initialNonce, iterand uint32) (uint32, error) {
|
||||
results, err := module.ExportedFunction("anubis_work").Call(ctx, uint64(difficulty), uint64(initialNonce), uint64(iterand))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(results[0]), nil
|
||||
}
|
||||
|
||||
func (r *Runner) anubisValidate(ctx context.Context, module api.Module, nonce, difficulty uint32) (bool, error) {
|
||||
results, err := module.ExportedFunction("anubis_validate").Call(ctx, uint64(nonce), uint64(difficulty))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Rust booleans are 1 if true
|
||||
return results[0] == 1, nil
|
||||
}
|
||||
|
||||
func (r *Runner) dataPtr(ctx context.Context, module api.Module) (uint32, error) {
|
||||
results, err := module.ExportedFunction("data_ptr").Call(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(results[0]), nil
|
||||
}
|
||||
|
||||
func (r *Runner) setDataLength(ctx context.Context, module api.Module, length uint32) error {
|
||||
_, err := module.ExportedFunction("set_data_length").Call(ctx, uint64(length))
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Runner) resultHashPtr(ctx context.Context, module api.Module) (uint32, error) {
|
||||
results, err := module.ExportedFunction("result_hash_ptr").Call(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(results[0]), nil
|
||||
}
|
||||
|
||||
func (r *Runner) resultHashSize(ctx context.Context, module api.Module) (uint32, error) {
|
||||
results, err := module.ExportedFunction("result_hash_size").Call(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(results[0]), nil
|
||||
}
|
||||
|
||||
func (r *Runner) verificationHashPtr(ctx context.Context, module api.Module) (uint32, error) {
|
||||
results, err := module.ExportedFunction("verification_hash_ptr").Call(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(results[0]), nil
|
||||
}
|
||||
|
||||
func (r *Runner) verificationHashSize(ctx context.Context, module api.Module) (uint32, error) {
|
||||
results, err := module.ExportedFunction("verification_hash_size").Call(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint32(results[0]), nil
|
||||
}
|
||||
|
||||
func (r *Runner) writeData(ctx context.Context, module api.Module, data []byte) error {
|
||||
if len(data) > 4096 {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
length := uint32(len(data))
|
||||
|
||||
dataPtr, err := r.dataPtr(ctx, module)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't read data pointer: %w", err)
|
||||
}
|
||||
|
||||
if !module.Memory().Write(dataPtr, data) {
|
||||
return fmt.Errorf("[unexpected] can't write memory, is data out of range??")
|
||||
}
|
||||
|
||||
if err := r.setDataLength(ctx, module, length); err != nil {
|
||||
return fmt.Errorf("can't set data length: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Runner) readResult(ctx context.Context, module api.Module) ([]byte, error) {
|
||||
length, err := r.resultHashSize(ctx, module)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get result hash size: %w", err)
|
||||
}
|
||||
|
||||
ptr, err := r.resultHashPtr(ctx, module)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get result hash pointer: %w", err)
|
||||
}
|
||||
|
||||
buf, ok := module.Memory().Read(ptr, length)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("[unexpected] can't read from memory, is something out of range??")
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (r *Runner) run(ctx context.Context, data []byte, difficulty, initialNonce, iterand uint32) (uint32, []byte, api.Module, error) {
|
||||
mod, err := r.r.InstantiateModule(ctx, r.code, wazero.NewModuleConfig().WithName(r.fname))
|
||||
if err != nil {
|
||||
return 0, nil, nil, fmt.Errorf("can't instantiate module: %w", err)
|
||||
}
|
||||
|
||||
if err := r.checkExports(mod); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
|
||||
if err := r.writeData(ctx, mod, data); err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
|
||||
nonce, err := r.anubisWork(ctx, mod, difficulty, initialNonce, iterand)
|
||||
if err != nil {
|
||||
return 0, nil, nil, fmt.Errorf("can't run work function: %w", err)
|
||||
}
|
||||
|
||||
hash, err := r.readResult(ctx, mod)
|
||||
if err != nil {
|
||||
return 0, nil, nil, fmt.Errorf("can't read result: %w", err)
|
||||
}
|
||||
|
||||
return nonce, hash, mod, nil
|
||||
}
|
||||
|
||||
func (r *Runner) Run(ctx context.Context, data []byte, difficulty, initialNonce, iterand uint32) (uint32, []byte, error) {
|
||||
nonce, hash, _, err := r.run(ctx, data, difficulty, initialNonce, iterand)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("can't run %s: %w", r.fname, err)
|
||||
}
|
||||
|
||||
return nonce, hash, nil
|
||||
}
|
||||
|
||||
func (r *Runner) verify(ctx context.Context, data, verify []byte, nonce, difficulty uint32) (bool, api.Module, error) {
|
||||
mod, err := r.r.InstantiateModule(ctx, r.code, wazero.NewModuleConfig().WithName(r.fname))
|
||||
if err != nil {
|
||||
return false, nil, fmt.Errorf("can't instantiate module: %w", err)
|
||||
}
|
||||
|
||||
if err := r.checkExports(mod); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if err := r.writeData(ctx, mod, data); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
if err := r.writeVerification(ctx, mod, verify); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
ok, err := r.anubisValidate(ctx, mod, nonce, difficulty)
|
||||
if err != nil {
|
||||
return false, nil, fmt.Errorf("can't validate hash %x from challenge %x, nonce %d and difficulty %d: %w", verify, data, nonce, difficulty, err)
|
||||
}
|
||||
|
||||
return ok, mod, nil
|
||||
}
|
||||
|
||||
func (r *Runner) Verify(ctx context.Context, data, verify []byte, nonce, difficulty uint32) (bool, error) {
|
||||
t0 := time.Now()
|
||||
ok, _, err := r.verify(ctx, data, verify, nonce, difficulty)
|
||||
validationTime.WithLabelValues(r.fname).Observe(float64(time.Since(t0)))
|
||||
validationCount.WithLabelValues(r.fname, strconv.FormatBool(ok))
|
||||
return ok, err
|
||||
}
|
||||
|
||||
func (r *Runner) writeVerification(ctx context.Context, module api.Module, data []byte) error {
|
||||
length, err := r.verificationHashSize(ctx, module)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get verification hash size: %v", err)
|
||||
}
|
||||
|
||||
if length != uint32(len(data)) {
|
||||
return fmt.Errorf("data is too big, want %d bytes, got: %d", length, len(data))
|
||||
}
|
||||
|
||||
ptr, err := r.verificationHashPtr(ctx, module)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't get verification hash pointer: %v", err)
|
||||
}
|
||||
|
||||
if !module.Memory().Write(ptr, data) {
|
||||
return fmt.Errorf("[unexpected] can't write memory, is data out of range??")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/TecharoHQ/anubis/web"
|
||||
)
|
||||
|
||||
func abiTest(t testing.TB, fname string, difficulty uint32) {
|
||||
fin, err := web.Static.Open("static/wasm/" + fname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fin.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
runner, err := NewRunner(ctx, fname, fin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
fmt.Fprint(h, t.Name())
|
||||
data := h.Sum(nil)
|
||||
|
||||
nonce, hash, mod, err := runner.run(ctx, data, difficulty, 0, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := runner.writeVerification(ctx, mod, hash); err != nil {
|
||||
t.Fatalf("can't write verification: %v", err)
|
||||
}
|
||||
|
||||
ok, err := runner.anubisValidate(ctx, mod, nonce, difficulty)
|
||||
if err != nil {
|
||||
t.Fatalf("can't run validation: %v", err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Error("validation failed")
|
||||
}
|
||||
|
||||
t.Logf("used %d pages of wasm memory (%d bytes)", mod.Memory().Size()/63356, mod.Memory().Size())
|
||||
}
|
||||
|
||||
func TestAlgos(t *testing.T) {
|
||||
fnames, err := fs.ReadDir(web.Static, "static/wasm")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, fname := range fnames {
|
||||
fname := fname
|
||||
t.Run(fname.Name(), func(t *testing.T) {
|
||||
abiTest(t, fname.Name(), 4)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func bench(b *testing.B, fname string, difficulties []uint32) {
|
||||
b.Helper()
|
||||
|
||||
fin, err := web.Static.Open("static/wasm/" + fname)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer fin.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
b.Cleanup(cancel)
|
||||
|
||||
runner, err := NewRunner(ctx, fname, fin)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
fmt.Fprint(h, "This is an example value that exists only to test the system.")
|
||||
data := h.Sum(nil)
|
||||
|
||||
_, _, mod, err := runner.run(ctx, data, 0, 0, 1)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
for _, difficulty := range difficulties {
|
||||
b.Run(fmt.Sprintf("difficulty/%d", difficulty), func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
difficulty := difficulty
|
||||
_, err := runner.anubisWork(ctx, mod, difficulty, 0, 1)
|
||||
if err != nil {
|
||||
b.Fatalf("can't do test work run: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSHA256(b *testing.B) {
|
||||
bench(b, "sha256.wasm", []uint32{4, 6, 8, 10, 12, 14, 16})
|
||||
}
|
||||
|
||||
func BenchmarkArgon2ID(b *testing.B) {
|
||||
bench(b, "argon2id.wasm", []uint32{4, 6, 8})
|
||||
}
|
||||
|
||||
func BenchmarkValidate(b *testing.B) {
|
||||
fnames, err := fs.ReadDir(web.Static, "static/wasm")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
fmt.Fprint(h, "This is an example value that exists only to test the system.")
|
||||
data := h.Sum(nil)
|
||||
|
||||
for _, fname := range fnames {
|
||||
fname := fname.Name()
|
||||
|
||||
difficulty := uint32(1)
|
||||
|
||||
switch fname {
|
||||
case "sha256.wasm":
|
||||
difficulty = 16
|
||||
}
|
||||
|
||||
b.Run(fname, func(b *testing.B) {
|
||||
fin, err := web.Static.Open("static/wasm/" + fname)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer fin.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
b.Cleanup(cancel)
|
||||
|
||||
runner, err := NewRunner(ctx, fname, fin)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
nonce, hash, mod, err := runner.run(ctx, data, difficulty, 0, 1)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
if err := runner.writeVerification(ctx, mod, hash); err != nil {
|
||||
b.Fatalf("can't write verification: %v", err)
|
||||
}
|
||||
|
||||
for b.Loop() {
|
||||
_, err := runner.anubisValidate(ctx, mod, nonce, difficulty)
|
||||
if err != nil {
|
||||
b.Fatalf("can't run validation: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
14
web/index.go
14
web/index.go
@@ -2,14 +2,22 @@ package web
|
||||
|
||||
import (
|
||||
"github.com/a-h/templ"
|
||||
|
||||
"github.com/TecharoHQ/anubis/lib/policy/config"
|
||||
)
|
||||
|
||||
func Base(title string, body templ.Component) templ.Component {
|
||||
return base(title, body, nil)
|
||||
return base(title, body, nil, nil)
|
||||
}
|
||||
|
||||
func BaseWithOGTags(title string, body templ.Component, ogTags map[string]string) templ.Component {
|
||||
return base(title, body, ogTags)
|
||||
func BaseWithChallengeAndOGTags(title string, body templ.Component, challenge string, rules *config.ChallengeRules, ogTags map[string]string) (templ.Component, error) {
|
||||
return base(title, body, struct {
|
||||
Challenge string `json:"challenge"`
|
||||
Rules *config.ChallengeRules `json:"rules"`
|
||||
}{
|
||||
Challenge: challenge,
|
||||
Rules: rules,
|
||||
}, ogTags), nil
|
||||
}
|
||||
|
||||
func Index() templ.Component {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/TecharoHQ/anubis/xess"
|
||||
)
|
||||
|
||||
templ base(title string, body templ.Component, ogTags map[string]string) {
|
||||
templ base(title string, body templ.Component, challenge any, ogTags map[string]string) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -42,10 +42,8 @@ templ base(title string, body templ.Component, ogTags map[string]string) {
|
||||
border-radius: 1rem;
|
||||
overflow: hidden;
|
||||
margin: 1rem 0 2rem;
|
||||
outline-color: #b16286;
|
||||
outline-offset: 2px;
|
||||
outline-style: solid;
|
||||
outline-width: 4px;
|
||||
outline: #b16286 solid 4px;
|
||||
}
|
||||
|
||||
.bar-inner {
|
||||
@@ -56,6 +54,9 @@ templ base(title string, body templ.Component, ogTags map[string]string) {
|
||||
}
|
||||
</style>
|
||||
@templ.JSONScript("anubis_version", anubis.Version)
|
||||
if challenge != nil {
|
||||
@templ.JSONScript("anubis_challenge", challenge)
|
||||
}
|
||||
|
||||
</head>
|
||||
<body id="top">
|
||||
|
||||
26
web/index_templ.go
generated
26
web/index_templ.go
generated
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/TecharoHQ/anubis/xess"
|
||||
)
|
||||
|
||||
func base(title string, body templ.Component, ogTags map[string]string) templ.Component {
|
||||
func base(title string, body templ.Component, challenge any, ogTags map[string]string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@@ -104,6 +104,12 @@ func base(title string, body templ.Component, ogTags map[string]string) templ.Co
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if challenge != nil {
|
||||
templ_7745c5c3_Err = templ.JSONScript("anubis_challenge", challenge).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</head><body id=\"top\"><main><center><h1 id=\"title\" class=\".centered-div\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
@@ -111,7 +117,7 @@ func base(title string, body templ.Component, ogTags map[string]string) templ.Co
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 64, Col: 52}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 67, Col: 52}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -162,7 +168,7 @@ func index() templ.Component {
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" +
|
||||
anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 88, Col: 18}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 91, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -176,7 +182,7 @@ func index() templ.Component {
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/happy.webp?cacheBuster=" +
|
||||
anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 94, Col: 18}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 97, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -190,7 +196,7 @@ func index() templ.Component {
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(
|
||||
"/.within.website/x/cmd/anubis/static/js/main.mjs?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 98, Col: 84}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 101, Col: 84}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -232,7 +238,7 @@ func errorPage(message string, mail string) templ.Component {
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/reject.webp?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 138, Col: 102}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 141, Col: 102}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -245,7 +251,7 @@ func errorPage(message string, mail string) templ.Component {
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(message)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 140, Col: 16}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 143, Col: 16}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -272,7 +278,7 @@ func errorPage(message string, mail string) templ.Component {
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(mail)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 144, Col: 9}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 147, Col: 9}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -325,7 +331,7 @@ func bench() templ.Component {
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs("/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" +
|
||||
anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 175, Col: 22}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 178, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -339,7 +345,7 @@ func bench() templ.Component {
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(
|
||||
"/.within.website/x/cmd/anubis/static/js/bench.mjs?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 179, Col: 89}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 182, Col: 89}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
import { u } from "../xeact.mjs";
|
||||
|
||||
export default function process(
|
||||
data,
|
||||
difficulty = 16,
|
||||
signal = null,
|
||||
pc = null,
|
||||
threads = (navigator.hardwareConcurrency || 1),
|
||||
) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let webWorkerURL = URL.createObjectURL(new Blob([
|
||||
'(', processTask(), ')()'
|
||||
], { type: 'application/javascript' }));
|
||||
|
||||
const module = await fetch(u("/.within.website/x/cmd/anubis/static/wasm/argon2id.wasm"))
|
||||
.then(resp => WebAssembly.compileStreaming(resp));
|
||||
|
||||
const workers = [];
|
||||
const terminate = () => {
|
||||
workers.forEach((w) => w.terminate());
|
||||
if (signal != null) {
|
||||
// clean up listener to avoid memory leak
|
||||
signal.removeEventListener("abort", terminate);
|
||||
if (signal.aborted) {
|
||||
console.log("PoW aborted");
|
||||
reject(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (signal != null) {
|
||||
signal.addEventListener("abort", terminate, { once: true });
|
||||
}
|
||||
|
||||
for (let i = 0; i < threads; i++) {
|
||||
let worker = new Worker(webWorkerURL);
|
||||
|
||||
worker.onmessage = (event) => {
|
||||
if (typeof event.data === "number") {
|
||||
pc?.(event.data);
|
||||
} else {
|
||||
terminate();
|
||||
resolve(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = (event) => {
|
||||
terminate();
|
||||
reject(event);
|
||||
};
|
||||
|
||||
worker.postMessage({
|
||||
data,
|
||||
difficulty,
|
||||
nonce: i,
|
||||
threads,
|
||||
module,
|
||||
});
|
||||
|
||||
workers.push(worker);
|
||||
}
|
||||
|
||||
URL.revokeObjectURL(webWorkerURL);
|
||||
});
|
||||
}
|
||||
|
||||
function processTask() {
|
||||
return function () {
|
||||
addEventListener('message', async (event) => {
|
||||
const importObject = {
|
||||
anubis: {
|
||||
anubis_update_nonce: (nonce) => postMessage(nonce),
|
||||
}
|
||||
};
|
||||
|
||||
const instance = await WebAssembly.instantiate(event.data.module, importObject);
|
||||
|
||||
// Get exports
|
||||
const {
|
||||
anubis_work,
|
||||
data_ptr,
|
||||
result_hash_ptr,
|
||||
result_hash_size,
|
||||
set_data_length,
|
||||
memory
|
||||
} = instance.exports;
|
||||
|
||||
function uint8ArrayToHex(arr) {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
function hexToUint8Array(hexString) {
|
||||
// Remove whitespace and optional '0x' prefix
|
||||
hexString = hexString.replace(/\s+/g, '').replace(/^0x/, '');
|
||||
|
||||
// Check for valid length
|
||||
if (hexString.length % 2 !== 0) {
|
||||
throw new Error('Invalid hex string length');
|
||||
}
|
||||
|
||||
// Check for valid characters
|
||||
if (!/^[0-9a-fA-F]+$/.test(hexString)) {
|
||||
throw new Error('Invalid hex characters');
|
||||
}
|
||||
|
||||
// Convert to Uint8Array
|
||||
const byteArray = new Uint8Array(hexString.length / 2);
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
const byteValue = parseInt(hexString.substr(i * 2, 2), 16);
|
||||
byteArray[i] = byteValue;
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
// Write data to buffer
|
||||
function writeToBuffer(data) {
|
||||
if (data.length > 1024) throw new Error("Data exceeds buffer size");
|
||||
|
||||
// Get pointer and create view
|
||||
const offset = data_ptr();
|
||||
const buffer = new Uint8Array(memory.buffer, offset, data.length);
|
||||
|
||||
// Copy data
|
||||
buffer.set(data);
|
||||
|
||||
// Set data length
|
||||
set_data_length(data.length);
|
||||
}
|
||||
|
||||
function readFromChallenge() {
|
||||
const offset = result_hash_ptr();
|
||||
const buffer = new Uint8Array(memory.buffer, offset, result_hash_size());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
let data = event.data.data;
|
||||
let difficulty = event.data.difficulty;
|
||||
let nonce = event.data.nonce;
|
||||
let interand = event.data.threads;
|
||||
|
||||
writeToBuffer(hexToUint8Array(data));
|
||||
|
||||
nonce = anubis_work(difficulty, nonce, interand);
|
||||
const challenge = readFromChallenge();
|
||||
|
||||
data = uint8ArrayToHex(challenge);
|
||||
|
||||
postMessage({
|
||||
hash: data,
|
||||
difficulty,
|
||||
nonce,
|
||||
});
|
||||
});
|
||||
}.toString();
|
||||
}
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
import { u } from "../xeact.mjs";
|
||||
|
||||
export default function process(
|
||||
data,
|
||||
difficulty = 16,
|
||||
signal = null,
|
||||
pc = null,
|
||||
threads = (navigator.hardwareConcurrency || 1),
|
||||
) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let webWorkerURL = URL.createObjectURL(new Blob([
|
||||
'(', processTask(), ')()'
|
||||
], { type: 'application/javascript' }));
|
||||
|
||||
const module = await fetch(u("/.within.website/x/cmd/anubis/static/wasm/sha256.wasm"))
|
||||
.then(resp => WebAssembly.compileStreaming(resp));
|
||||
|
||||
const workers = [];
|
||||
const terminate = () => {
|
||||
workers.forEach((w) => w.terminate());
|
||||
if (signal != null) {
|
||||
// clean up listener to avoid memory leak
|
||||
signal.removeEventListener("abort", terminate);
|
||||
if (signal.aborted) {
|
||||
console.log("PoW aborted");
|
||||
reject(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (signal != null) {
|
||||
signal.addEventListener("abort", terminate, { once: true });
|
||||
}
|
||||
|
||||
for (let i = 0; i < threads; i++) {
|
||||
let worker = new Worker(webWorkerURL);
|
||||
|
||||
worker.onmessage = (event) => {
|
||||
if (typeof event.data === "number") {
|
||||
pc?.(event.data);
|
||||
} else {
|
||||
terminate();
|
||||
resolve(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = (event) => {
|
||||
terminate();
|
||||
reject(event);
|
||||
};
|
||||
|
||||
worker.postMessage({
|
||||
data,
|
||||
difficulty,
|
||||
nonce: i,
|
||||
threads,
|
||||
module,
|
||||
});
|
||||
|
||||
workers.push(worker);
|
||||
}
|
||||
|
||||
URL.revokeObjectURL(webWorkerURL);
|
||||
});
|
||||
}
|
||||
|
||||
function processTask() {
|
||||
return function () {
|
||||
addEventListener('message', async (event) => {
|
||||
const importObject = {
|
||||
anubis: {
|
||||
anubis_update_nonce: (nonce) => postMessage(nonce),
|
||||
}
|
||||
};
|
||||
|
||||
const instance = await WebAssembly.instantiate(event.data.module, importObject);
|
||||
|
||||
// Get exports
|
||||
const {
|
||||
anubis_work,
|
||||
data_ptr,
|
||||
result_hash_ptr,
|
||||
result_hash_size,
|
||||
set_data_length,
|
||||
memory
|
||||
} = instance.exports;
|
||||
|
||||
function uint8ArrayToHex(arr) {
|
||||
return Array.from(arr)
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
function hexToUint8Array(hexString) {
|
||||
// Remove whitespace and optional '0x' prefix
|
||||
hexString = hexString.replace(/\s+/g, '').replace(/^0x/, '');
|
||||
|
||||
// Check for valid length
|
||||
if (hexString.length % 2 !== 0) {
|
||||
throw new Error('Invalid hex string length');
|
||||
}
|
||||
|
||||
// Check for valid characters
|
||||
if (!/^[0-9a-fA-F]+$/.test(hexString)) {
|
||||
throw new Error('Invalid hex characters');
|
||||
}
|
||||
|
||||
// Convert to Uint8Array
|
||||
const byteArray = new Uint8Array(hexString.length / 2);
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
const byteValue = parseInt(hexString.substr(i * 2, 2), 16);
|
||||
byteArray[i] = byteValue;
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
// Write data to buffer
|
||||
function writeToBuffer(data) {
|
||||
if (data.length > 1024) throw new Error("Data exceeds buffer size");
|
||||
|
||||
// Get pointer and create view
|
||||
const offset = data_ptr();
|
||||
const buffer = new Uint8Array(memory.buffer, offset, data.length);
|
||||
|
||||
// Copy data
|
||||
buffer.set(data);
|
||||
|
||||
// Set data length
|
||||
set_data_length(data.length);
|
||||
}
|
||||
|
||||
function readFromChallenge() {
|
||||
const offset = result_hash_ptr();
|
||||
const buffer = new Uint8Array(memory.buffer, offset, result_hash_size());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
let data = event.data.data;
|
||||
let difficulty = event.data.difficulty;
|
||||
let nonce = event.data.nonce;
|
||||
let interand = event.data.threads;
|
||||
|
||||
writeToBuffer(hexToUint8Array(data));
|
||||
|
||||
nonce = anubis_work(difficulty, nonce, interand);
|
||||
const challenge = readFromChallenge();
|
||||
|
||||
data = uint8ArrayToHex(challenge);
|
||||
|
||||
postMessage({
|
||||
hash: data,
|
||||
difficulty,
|
||||
nonce,
|
||||
});
|
||||
});
|
||||
}.toString();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import fast from "./algos/fast.mjs";
|
||||
import slow from "./algos/slow.mjs";
|
||||
import sha256 from "./algos/sha256.mjs";
|
||||
import processFast from "./proof-of-work.mjs";
|
||||
import processSlow from "./proof-of-work-slow.mjs";
|
||||
|
||||
const defaultDifficulty = 16;
|
||||
const defaultDifficulty = 4;
|
||||
const algorithms = {
|
||||
sha256: sha256,
|
||||
fast: fast,
|
||||
slow: slow,
|
||||
fast: processFast,
|
||||
slow: processSlow,
|
||||
};
|
||||
|
||||
const status = document.getElementById("status");
|
||||
@@ -43,13 +41,10 @@ const benchmarkTrial = async (stats, difficulty, algorithm, signal) => {
|
||||
.map((c) => c.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
|
||||
if (algorithm != "sha256") {
|
||||
difficulty = Math.round(difficulty / 4);
|
||||
}
|
||||
|
||||
const t0 = performance.now();
|
||||
const { hash, nonce } = await process(challenge, Number(difficulty), signal);
|
||||
const t1 = performance.now();
|
||||
console.log({ hash, nonce });
|
||||
|
||||
stats.time += t1 - t0;
|
||||
stats.iters += nonce;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import argon2id from "./algos/argon2id.mjs";
|
||||
import fast from "./algos/fast.mjs";
|
||||
import slow from "./algos/slow.mjs";
|
||||
import sha256 from "./algos/sha256.mjs";
|
||||
import processFast from "./proof-of-work.mjs";
|
||||
import processSlow from "./proof-of-work-slow.mjs";
|
||||
import { testVideo } from "./video.mjs";
|
||||
import { u } from "./xeact.mjs";
|
||||
|
||||
const algorithms = {
|
||||
"argon2id": argon2id,
|
||||
"fast": fast,
|
||||
"slow": slow,
|
||||
"sha256": sha256,
|
||||
"fast": processFast,
|
||||
"slow": processSlow,
|
||||
};
|
||||
|
||||
// from Xeact
|
||||
const u = (url = "", params = {}) => {
|
||||
let result = new URL(url, window.location.href);
|
||||
Object.entries(params).forEach(([k, v]) => result.searchParams.set(k, v));
|
||||
return result.toString();
|
||||
};
|
||||
|
||||
const imageURL = (mood, cacheBuster) =>
|
||||
@@ -26,11 +28,6 @@ const dependencies = [
|
||||
msg: "Your browser doesn't support web workers (Anubis uses this to avoid freezing your browser). Do you have a plugin like JShelter installed?",
|
||||
value: window.Worker,
|
||||
},
|
||||
{
|
||||
name: "WebAssembly",
|
||||
msg: "Your browser doesn't have WebAssembly support. If you are running a big endian system, I'm sorry but this is something we can't work around with a polyfill.",
|
||||
value: window.WebAssembly,
|
||||
},
|
||||
];
|
||||
|
||||
function showContinueBar(hash, nonce, t0, t1) {
|
||||
@@ -136,19 +133,7 @@ function showContinueBar(hash, nonce, t0, t1) {
|
||||
}
|
||||
}
|
||||
|
||||
const { challenge, rules } = await fetch("/.within.website/x/cmd/anubis/api/make-challenge", { method: "POST" })
|
||||
.then(r => {
|
||||
if (!r.ok) throw new Error("Failed to fetch config");
|
||||
return r.json();
|
||||
})
|
||||
.catch(err => {
|
||||
ohNoes({
|
||||
titleMsg: "Internal error!",
|
||||
statusMsg: `Failed to fetch challenge config: ${err.message}`,
|
||||
imageSrc: imageURL("reject", anubisVersion),
|
||||
});
|
||||
throw err;
|
||||
});
|
||||
const { challenge, rules } = JSON.parse(document.getElementById('anubis_challenge').textContent);
|
||||
|
||||
const process = algorithms[rules.algorithm];
|
||||
if (!process) {
|
||||
|
||||
@@ -7,6 +7,7 @@ export default function process(
|
||||
progressCallback = null,
|
||||
_threads = 1,
|
||||
) {
|
||||
console.debug("slow algo");
|
||||
return new Promise((resolve, reject) => {
|
||||
let webWorkerURL = URL.createObjectURL(new Blob([
|
||||
'(', processTask(), ')()'
|
||||
@@ -5,6 +5,7 @@ export default function process(
|
||||
progressCallback = null,
|
||||
threads = (navigator.hardwareConcurrency || 1),
|
||||
) {
|
||||
console.debug("fast algo");
|
||||
return new Promise((resolve, reject) => {
|
||||
let webWorkerURL = URL.createObjectURL(new Blob([
|
||||
'(', processTask(), ')()'
|
||||
@@ -98,6 +99,7 @@ function processTask() {
|
||||
|
||||
if (valid) {
|
||||
hash = uint8ArrayToHexString(thisHash);
|
||||
console.log(hash);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Generate a relative URL from `url`, appending all key-value pairs from `params` as URL-encoded parameters.
|
||||
*
|
||||
* @type{function(string=, Object=): string}
|
||||
*/
|
||||
export const u = (url = "", params = {}) => {
|
||||
let result = new URL(url, window.location.href);
|
||||
Object.entries(params).forEach((kv) => {
|
||||
let [k, v] = kv;
|
||||
result.searchParams.set(k, v);
|
||||
});
|
||||
return result.toString();
|
||||
};
|
||||
@@ -24,6 +24,7 @@ var (
|
||||
func init() {
|
||||
Mount(http.DefaultServeMux)
|
||||
|
||||
//goland:noinspection GoBoolExpressions
|
||||
if anubis.Version != "devel" {
|
||||
URL = filepath.Join(filepath.Dir(URL), "xess.min.css")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user