From aa6a953917222549f58f0d48278fe3f14e2c0eaf Mon Sep 17 00:00:00 2001 From: Nikolay Govorov Date: Sun, 30 Aug 2026 02:50:45 +0100 Subject: Named Tesor --- .dockerignore | 2 +- .github/workflows/build.yml | 20 +- .gitignore | 2 +- CLA.md | 4 +- Cargo.lock | 90 +- Cargo.toml | 2 +- Dockerfile | 18 +- README.md | 38 +- REUSE.toml | 16 +- charts/recluse/Chart.yaml | 10 - charts/recluse/templates/_helpers.tpl | 37 - charts/recluse/templates/configmap.yaml | 46 - charts/recluse/templates/deployment.yaml | 97 -- charts/recluse/templates/httproute.yaml | 26 - charts/recluse/templates/pvc.yaml | 22 - charts/recluse/templates/service.yaml | 19 - charts/recluse/values.yaml | 78 -- charts/tesor/Chart.yaml | 10 + charts/tesor/templates/_helpers.tpl | 37 + charts/tesor/templates/configmap.yaml | 46 + charts/tesor/templates/deployment.yaml | 97 ++ charts/tesor/templates/httproute.yaml | 26 + charts/tesor/templates/pvc.yaml | 22 + charts/tesor/templates/service.yaml | 19 + charts/tesor/values.yaml | 78 ++ crates/recluse/Cargo.toml | 62 - crates/recluse/build.rs | 30 - .../recluse/src/assets/apple-touch-icon.png | Bin 715 -> 0 bytes crates/recluse/src/assets/base.css | 128 -- crates/recluse/src/assets/favicon-192.png | Bin 715 -> 0 bytes crates/recluse/src/assets/favicon-512.png | Bin 2329 -> 0 bytes crates/recluse/src/assets/favicon.ico | Bin 32038 -> 0 bytes crates/recluse/src/assets/favicon.svg | 4 - .../JetBrainsMono-Italic[wght].woff2 | Bin 122452 -> 0 bytes .../jetbrainsmono/JetBrainsMono[wght].woff2 | Bin 113700 -> 0 bytes .../recluse/src/assets/manifest.webmanifest | 8 - crates/recluse/src/assets/robots.txt | 5 - crates/recluse/src/config.rs | 627 --------- crates/recluse/src/controller_backend.rs | 111 -- crates/recluse/src/controller_web.rs | 486 ------- crates/recluse/src/main.rs | 567 -------- crates/recluse/src/proxy.rs | 78 -- crates/recluse/src/storage.rs | 1203 ----------------- crates/recluse/src/telemetry.rs | 247 ---- crates/repos/src/go.rs | 4 +- crates/tesor/Cargo.toml | 62 + crates/tesor/build.rs | 30 + crates/tesor/src/assets/apple-touch-icon.png | Bin 0 -> 715 bytes crates/tesor/src/assets/base.css | 128 ++ crates/tesor/src/assets/favicon-192.png | Bin 0 -> 715 bytes crates/tesor/src/assets/favicon-512.png | Bin 0 -> 2329 bytes crates/tesor/src/assets/favicon.ico | Bin 0 -> 32038 bytes crates/tesor/src/assets/favicon.svg | 4 + .../JetBrainsMono-Italic[wght].woff2 | Bin 0 -> 122452 bytes .../jetbrainsmono/JetBrainsMono[wght].woff2 | Bin 0 -> 113700 bytes crates/tesor/src/assets/manifest.webmanifest | 8 + crates/tesor/src/assets/robots.txt | 5 + crates/tesor/src/config.rs | 627 +++++++++ crates/tesor/src/controller_backend.rs | 111 ++ crates/tesor/src/controller_web.rs | 486 +++++++ crates/tesor/src/main.rs | 567 ++++++++ crates/tesor/src/proxy.rs | 78 ++ crates/tesor/src/storage.rs | 1203 +++++++++++++++++ crates/tesor/src/telemetry.rs | 247 ++++ deny.toml | 2 +- mise.toml | 2 +- nfpm.yaml | 24 +- pkg/recluse.service | 51 - pkg/recluse.toml | 59 - pkg/scripts/postinstall.sh | 4 +- pkg/scripts/preinstall.sh | 12 +- pkg/scripts/preremove.sh | 6 +- pkg/tesor.service | 51 + pkg/tesor.toml | 59 + tests/smoke/run-local.sh | 24 +- tests/smoke/src/main.rs | 36 +- 76 files changed, 4154 insertions(+), 4154 deletions(-) delete mode 100644 charts/recluse/Chart.yaml delete mode 100644 charts/recluse/templates/_helpers.tpl delete mode 100644 charts/recluse/templates/configmap.yaml delete mode 100644 charts/recluse/templates/deployment.yaml delete mode 100644 charts/recluse/templates/httproute.yaml delete mode 100644 charts/recluse/templates/pvc.yaml delete mode 100644 charts/recluse/templates/service.yaml delete mode 100644 charts/recluse/values.yaml create mode 100644 charts/tesor/Chart.yaml create mode 100644 charts/tesor/templates/_helpers.tpl create mode 100644 charts/tesor/templates/configmap.yaml create mode 100644 charts/tesor/templates/deployment.yaml create mode 100644 charts/tesor/templates/httproute.yaml create mode 100644 charts/tesor/templates/pvc.yaml create mode 100644 charts/tesor/templates/service.yaml create mode 100644 charts/tesor/values.yaml delete mode 100644 crates/recluse/Cargo.toml delete mode 100644 crates/recluse/build.rs delete mode 100644 crates/recluse/src/assets/apple-touch-icon.png delete mode 100644 crates/recluse/src/assets/base.css delete mode 100644 crates/recluse/src/assets/favicon-192.png delete mode 100644 crates/recluse/src/assets/favicon-512.png delete mode 100644 crates/recluse/src/assets/favicon.ico delete mode 100644 crates/recluse/src/assets/favicon.svg delete mode 100644 crates/recluse/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 delete mode 100644 crates/recluse/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 delete mode 100644 crates/recluse/src/assets/manifest.webmanifest delete mode 100644 crates/recluse/src/assets/robots.txt delete mode 100644 crates/recluse/src/config.rs delete mode 100644 crates/recluse/src/controller_backend.rs delete mode 100644 crates/recluse/src/controller_web.rs delete mode 100644 crates/recluse/src/main.rs delete mode 100644 crates/recluse/src/proxy.rs delete mode 100644 crates/recluse/src/storage.rs delete mode 100644 crates/recluse/src/telemetry.rs create mode 100644 crates/tesor/Cargo.toml create mode 100644 crates/tesor/build.rs create mode 100644 crates/tesor/src/assets/apple-touch-icon.png create mode 100644 crates/tesor/src/assets/base.css create mode 100644 crates/tesor/src/assets/favicon-192.png create mode 100644 crates/tesor/src/assets/favicon-512.png create mode 100644 crates/tesor/src/assets/favicon.ico create mode 100644 crates/tesor/src/assets/favicon.svg create mode 100644 crates/tesor/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 create mode 100644 crates/tesor/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 create mode 100644 crates/tesor/src/assets/manifest.webmanifest create mode 100644 crates/tesor/src/assets/robots.txt create mode 100644 crates/tesor/src/config.rs create mode 100644 crates/tesor/src/controller_backend.rs create mode 100644 crates/tesor/src/controller_web.rs create mode 100644 crates/tesor/src/main.rs create mode 100644 crates/tesor/src/proxy.rs create mode 100644 crates/tesor/src/storage.rs create mode 100644 crates/tesor/src/telemetry.rs delete mode 100644 pkg/recluse.service delete mode 100644 pkg/recluse.toml create mode 100644 pkg/tesor.service create mode 100644 pkg/tesor.toml diff --git a/.dockerignore b/.dockerignore index 90a5968..373529b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,7 +3,7 @@ .git .github -.recluse-state +.tesor-state dist target *.lcov diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07dadb3..4078821 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ permissions: contents: read concurrency: - group: recluse-build-${{ github.ref }} + group: tesor-build-${{ github.ref }} cancel-in-progress: true env: @@ -72,11 +72,11 @@ jobs: - run: cargo clippy --workspace --locked --all-targets --all-features --release -- -D warnings - run: cargo test --all-features --release --locked - - run: cargo build --package recluse --bin recluse --release --locked + - run: cargo build --package tesor --bin tesor --release --locked - name: Build packages run: | - PKG_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "recluse") | .version') + PKG_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "tesor") | .version') if [[ "$GITHUB_REF" == refs/tags/v* ]]; then VERSION="${GITHUB_REF_NAME#v}" if [[ "$VERSION" != "$PKG_VERSION" ]]; then @@ -114,7 +114,7 @@ jobs: - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: binary-${{ matrix.arch }} - path: target/release/recluse + path: target/release/tesor smoke: name: Smoke tests @@ -138,7 +138,7 @@ jobs: - name: Run smoke tests against local instance run: | - chmod +x target/release/recluse + chmod +x target/release/tesor ./tests/smoke/run-local.sh publish: @@ -146,7 +146,7 @@ jobs: runs-on: ubuntu-latest needs: [build, smoke] concurrency: - group: package-publish-recluse-nightly + group: package-publish-tesor-nightly cancel-in-progress: false if: github.ref == 'refs/heads/main' permissions: @@ -199,7 +199,7 @@ jobs: - name: Publish package repositories run: | mise run publish -- \ - --service recluse \ + --service tesor \ --channel nightly \ --input dist \ deb rpm @@ -275,7 +275,7 @@ jobs: shell: bash run: | version="${{ steps.artifact.outputs.version }}" - image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/recluse" + image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/tesor" tags=( --tag "$image:sha-$GITHUB_SHA" --tag "$image:$version" @@ -302,11 +302,11 @@ jobs: --context . \ --file Dockerfile \ --platform linux/amd64,linux/arm64 \ - --cache-scope recluse \ + --cache-scope tesor \ "${tags[@]}" "${labels[@]}" "${image_output[@]}" mise run chart -- \ - --chart charts/recluse \ + --chart charts/tesor \ --version "$version" \ --app-version "$version" \ "${chart_output[@]}" diff --git a/.gitignore b/.gitignore index ef36af8..2c7fb07 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ mise.local.toml mise.*.local.toml /.container -/.recluse-state +/.tesor-state diff --git a/CLA.md b/CLA.md index f8240f7..c0418c8 100644 --- a/CLA.md +++ b/CLA.md @@ -1,4 +1,4 @@ -# Recluse Individual Contributor License Agreement +# Tesor Individual Contributor License Agreement Version 1.0 @@ -6,7 +6,7 @@ Version 1.0 > Agreement Version 1.0 licensed under a > [Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/). -Thank you for your interest in contributing to Recluse (the "Project"). In this +Thank you for your interest in contributing to Tesor (the "Project"). In this Agreement, "We" and "Us" mean Nikolay Govorov. This contributor agreement ("Agreement") documents the rights granted by diff --git a/Cargo.lock b/Cargo.lock index 94f3647..d2e2df0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1912,51 +1912,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "recluse" -version = "0.1.0" -dependencies = [ - "async-trait", - "axum", - "axum-server", - "base", - "bytes", - "bytesize", - "chrono", - "crc32fast", - "hex", - "http-body-util", - "hyper", - "hyper-tls", - "hyper-util", - "maud", - "opentelemetry", - "opentelemetry-appender-tracing", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry_sdk", - "repos", - "rustls", - "sd-notify", - "serde", - "serde_json", - "sqlx", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "toml", - "tonic", - "tower", - "tower-http", - "tower_governor", - "tracing", - "tracing-opentelemetry", - "tracing-subscriber", - "url", - "uuid", -] - [[package]] name = "redox_syscall" version = "0.5.18" @@ -2735,6 +2690,51 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tesor" +version = "0.1.0" +dependencies = [ + "async-trait", + "axum", + "axum-server", + "base", + "bytes", + "bytesize", + "chrono", + "crc32fast", + "hex", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "maud", + "opentelemetry", + "opentelemetry-appender-tracing", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "repos", + "rustls", + "sd-notify", + "serde", + "serde_json", + "sqlx", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "toml", + "tonic", + "tower", + "tower-http", + "tower_governor", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber", + "url", + "uuid", +] + [[package]] name = "thiserror" version = "1.0.69" diff --git a/Cargo.toml b/Cargo.toml index 06e3c86..19cca99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ version = "0.1.0" license = "AGPL-3.0-or-later" authors = ["Nikolay Govorov "] homepage = "https://pkg.earth" -repository = "https://github.com/dimidiumlabs/recluse" +repository = "https://git.dimidiumlabs.io/tesor" [workspace.dependencies] # local dependencies diff --git a/Dockerfile b/Dockerfile index e4f4141..d82aa9e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,23 +7,23 @@ ARG ALPINE_DIGEST=sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952b ARG DISTROLESS_DIGEST=sha256:a77defd6fedbb3392b175ba8ea3d1c22be963c1597c248c3ba987ddd80bfb512 FROM --platform=$BUILDPLATFORM docker.io/library/alpine:${ALPINE_VERSION}@${ALPINE_DIGEST} AS rootfs -RUN install -d -m 0750 /rootfs/var/lib/recluse +RUN install -d -m 0750 /rootfs/var/lib/tesor FROM gcr.io/distroless/cc-debian13:nonroot@${DISTROLESS_DIGEST} ARG TARGETARCH -LABEL org.opencontainers.image.source="https://github.com/dimidiumlabs/recluse" \ +LABEL org.opencontainers.image.source="https://git.dimidiumlabs.io/tesor" \ org.opencontainers.image.licenses="AGPL-3.0-or-later" -COPY --chown=root:root --chmod=0755 .container/binary-${TARGETARCH}/recluse /usr/local/bin/recluse -COPY --from=rootfs --chown=10000:10000 /rootfs/var/lib/recluse /var/lib/recluse -COPY --chown=root:root pkg/recluse.toml /etc/recluse.toml -COPY --chown=root:root LICENSE README.md /usr/share/doc/recluse/ +COPY --chown=root:root --chmod=0755 .container/binary-${TARGETARCH}/tesor /usr/local/bin/tesor +COPY --from=rootfs --chown=10000:10000 /rootfs/var/lib/tesor /var/lib/tesor +COPY --chown=root:root pkg/tesor.toml /etc/tesor.toml +COPY --chown=root:root LICENSE README.md /usr/share/doc/tesor/ USER 10000:10000 EXPOSE 2000 -VOLUME ["/var/lib/recluse"] +VOLUME ["/var/lib/tesor"] -ENTRYPOINT ["/usr/local/bin/recluse"] -CMD ["--config=/etc/recluse.toml"] +ENTRYPOINT ["/usr/local/bin/tesor"] +CMD ["--config=/etc/tesor.toml"] diff --git a/README.md b/README.md index fa4cfee..2c51ae4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Recluse — tiny packages caching proxy +# Tesor — tiny packages caching proxy Soon humanity will go to Mars and in order for the colonists to be able to program, we will need a local mirror of packages. @@ -14,52 +14,52 @@ is **not** intended for production use. sudo apt install curl gnupg curl -fsSL https://pkg.dimidiumlabs.io/packages.gpg | sudo gpg --dearmor -o /usr/share/keyrings/dimidiumlabs.gpg -echo "deb [signed-by=/usr/share/keyrings/dimidiumlabs.gpg] https://pkg.dimidiumlabs.io/recluse/apt/ nightly main" | sudo tee /etc/apt/sources.list.d/recluse.list -sudo apt update && sudo apt install recluse +echo "deb [signed-by=/usr/share/keyrings/dimidiumlabs.gpg] https://pkg.dimidiumlabs.io/tesor/apt/ nightly main" | sudo tee /etc/apt/sources.list.d/tesor.list +sudo apt update && sudo apt install tesor -sudo systemctl enable --now recluse +sudo systemctl enable --now tesor ``` **Fedora/RHEL:** ```bash # DNF5 (Fedora 41+, RHEL 10+) -sudo dnf config-manager addrepo --from-repofile=https://pkg.dimidiumlabs.io/recluse/rpm/nightly/recluse-nightly.repo +sudo dnf config-manager addrepo --from-repofile=https://pkg.dimidiumlabs.io/tesor/rpm/nightly/tesor-nightly.repo # DNF4 (Fedora 40 and older, RHEL 8/9) -sudo curl -o /etc/yum.repos.d/recluse-nightly.repo https://pkg.dimidiumlabs.io/recluse/rpm/nightly/recluse-nightly.repo +sudo curl -o /etc/yum.repos.d/tesor-nightly.repo https://pkg.dimidiumlabs.io/tesor/rpm/nightly/tesor-nightly.repo -sudo dnf install recluse -sudo systemctl enable --now recluse +sudo dnf install tesor +sudo systemctl enable --now tesor ``` **openSUSE:** ```bash sudo rpm --import https://pkg.dimidiumlabs.io/packages.gpg -sudo zypper addrepo https://pkg.dimidiumlabs.io/recluse/rpm/nightly/ recluse-nightly +sudo zypper addrepo https://pkg.dimidiumlabs.io/tesor/rpm/nightly/ tesor-nightly sudo zypper refresh -sudo zypper install recluse +sudo zypper install tesor -sudo systemctl enable --now recluse +sudo systemctl enable --now tesor ``` ## Build from source -1. Clone repo: `git clone https://github.com/dimidiumlabs/recluse.git && cd recluse` +1. Clone repo: `git clone https://git.dimidiumlabs.io/tesor.git && cd tesor` 1. Provision the toolchain and system build dependencies: `mise bootstrap` 1. Build from source: `mise exec -- cargo build --release` 1. Install manually ```bash - sudo groupadd --system recluse - sudo useradd --system --gid recluse --no-create-home --shell /usr/sbin/nologin recluse - sudo install -m 700 -o recluse ./pkg/recluse.toml /etc/ - sudo install -m 755 -o root ./pkg/recluse.service /usr/lib/systemd/system - sudo install -m 755 -o root target/release/recluse /usr/local/bin/ + sudo groupadd --system tesor + sudo useradd --system --gid tesor --no-create-home --shell /usr/sbin/nologin tesor + sudo install -m 700 -o tesor ./pkg/tesor.toml /etc/ + sudo install -m 755 -o root ./pkg/tesor.service /usr/lib/systemd/system + sudo install -m 755 -o root target/release/tesor /usr/local/bin/ ``` -1. start systemd service: `sudo systemctl enable --now recluse` +1. start systemd service: `sudo systemctl enable --now tesor` ## Contributing @@ -95,7 +95,7 @@ Remember, AI agents should make software better, not worse. ## Licensing -Recluse source code is licensed under AGPL-3.0-or-later. Documentation is +Tesor source code is licensed under AGPL-3.0-or-later. Documentation is licensed under CC-BY-4.0. The bundled JetBrains Mono font files are licensed under OFL-1.1. diff --git a/REUSE.toml b/REUSE.toml index 717f144..ca825f7 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -10,13 +10,13 @@ path = [ "mise.lock", "pkg/*.svg", - "charts/recluse/templates/*", + "charts/tesor/templates/*", - "crates/recluse/src/assets/*.svg", - "crates/recluse/src/assets/*.ico", - "crates/recluse/src/assets/*.png", - "crates/recluse/src/assets/robots.txt", - "crates/recluse/src/assets/manifest.webmanifest", + "crates/tesor/src/assets/*.svg", + "crates/tesor/src/assets/*.ico", + "crates/tesor/src/assets/*.png", + "crates/tesor/src/assets/robots.txt", + "crates/tesor/src/assets/manifest.webmanifest", ] SPDX-FileCopyrightText = "2026 Nikolay Govorov" SPDX-License-Identifier = "AGPL-3.0-or-later" @@ -37,8 +37,8 @@ SPDX-License-Identifier = "CC-BY-3.0" [[annotations]] path = [ - "crates/recluse/src/assets/jetbrainsmono/OFL.txt", - "crates/recluse/src/assets/jetbrainsmono/*.woff2", + "crates/tesor/src/assets/jetbrainsmono/OFL.txt", + "crates/tesor/src/assets/jetbrainsmono/*.woff2", ] SPDX-FileCopyrightText = "Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)" SPDX-License-Identifier = "OFL-1.1" diff --git a/charts/recluse/Chart.yaml b/charts/recluse/Chart.yaml deleted file mode 100644 index ff40446..0000000 --- a/charts/recluse/Chart.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -apiVersion: v2 -type: application -version: 0.1.0 - -name: recluse -home: https://github.com/dimidiumlabs/recluse -description: Tiny package caching proxy diff --git a/charts/recluse/templates/_helpers.tpl b/charts/recluse/templates/_helpers.tpl deleted file mode 100644 index 4da058b..0000000 --- a/charts/recluse/templates/_helpers.tpl +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -{{- define "recluse.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{- define "recluse.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name (include "recluse.name" .) | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} - -{{- define "recluse.labels" -}} -helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }} -app.kubernetes.io/name: {{ include "recluse.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{- define "recluse.selectorLabels" -}} -app.kubernetes.io/name: {{ include "recluse.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{- define "recluse.image" -}} -{{- if .Values.image.digest -}} -{{- printf "%s@%s" .Values.image.repository .Values.image.digest -}} -{{- else -}} -{{- printf "%s:%s" .Values.image.repository (default .Chart.AppVersion .Values.image.tag) -}} -{{- end -}} -{{- end -}} - -# vim: set filetype=helm: diff --git a/charts/recluse/templates/configmap.yaml b/charts/recluse/templates/configmap.yaml deleted file mode 100644 index d5dbf18..0000000 --- a/charts/recluse/templates/configmap.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -{{- if not .Values.config.existingConfigMap }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "recluse.fullname" . }} - labels: - {{- include "recluse.labels" . | nindent 4 }} -data: - recluse.toml: | - appname = {{ .Values.config.appname | quote }} - dirname = "/var/lib/recluse" - - [server] - max_body_size = {{ .Values.config.server.maxBodySize | quote }} - max_concurrent_requests = {{ .Values.config.server.maxConcurrentRequests }} - - rate_limit_period = {{ .Values.config.server.rateLimitPeriod }} - rate_limit_burst_size = {{ .Values.config.server.rateLimitBurstSize }} - - request_timeout = {{ .Values.config.server.requestTimeout }} - shutdown_timeout = {{ .Values.config.server.shutdownTimeout }} - - [[listen]] - addr = "0.0.0.0:2000" - hostnames = {{ .Values.config.hostnames | toJson }} - - {{- range $name, $backend := .Values.config.backends }} - [backends.{{ $name }}] - enabled = {{ $backend.enabled }} - refresh_interval = {{ $backend.refreshInterval }} - - {{- end }} - [telemetry.stdout] - enabled = {{ .Values.config.log.enabled }} - log_level = {{ .Values.config.log.level | quote }} - log_format = {{ .Values.config.log.format | quote }} - {{- with .Values.config.extra }} - -{{ . | indent 4 }} - {{- end }} -{{- end }} - -# vim: set filetype=helm: diff --git a/charts/recluse/templates/deployment.yaml b/charts/recluse/templates/deployment.yaml deleted file mode 100644 index f82648c..0000000 --- a/charts/recluse/templates/deployment.yaml +++ /dev/null @@ -1,97 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "recluse.fullname" . }} - labels: - {{- include "recluse.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - strategy: - type: Recreate - selector: - matchLabels: - {{- include "recluse.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - {{- if not .Values.config.existingConfigMap }} - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - {{- end }} - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "recluse.selectorLabels" . | nindent 8 }} - {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - automountServiceAccountToken: false - terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: recluse - image: {{ include "recluse.image" . | quote }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - args: ["--config=/etc/recluse/recluse.toml"] - ports: - - name: http - containerPort: 2000 - protocol: TCP - startupProbe: - tcpSocket: - port: http - failureThreshold: 60 - periodSeconds: 5 - readinessProbe: - tcpSocket: - port: http - periodSeconds: 10 - livenessProbe: - tcpSocket: - port: http - periodSeconds: 20 - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: config - mountPath: /etc/recluse/recluse.toml - subPath: recluse.toml - readOnly: true - - name: state - mountPath: /var/lib/recluse - volumes: - - name: config - configMap: - name: {{ default (include "recluse.fullname" .) .Values.config.existingConfigMap }} - - name: state - {{- if .Values.persistence.enabled }} - persistentVolumeClaim: - claimName: {{ default (include "recluse.fullname" .) .Values.persistence.existingClaim }} - {{- else }} - emptyDir: {} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - -# vim: set filetype=helm: diff --git a/charts/recluse/templates/httproute.yaml b/charts/recluse/templates/httproute.yaml deleted file mode 100644 index 5b7d341..0000000 --- a/charts/recluse/templates/httproute.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -{{- if .Values.route.enabled }} -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: {{ include "recluse.fullname" . }} - labels: - {{- include "recluse.labels" . | nindent 4 }} -spec: - {{- with .Values.route.parentRefs }} - parentRefs: - {{- toYaml . | nindent 4 }} - {{- end }} - {{- with .Values.route.hostnames }} - hostnames: - {{- toYaml . | nindent 4 }} - {{- end }} - rules: - - backendRefs: - - name: {{ include "recluse.fullname" . }} - port: {{ .Values.service.port }} -{{- end }} - -# vim: set filetype=helm: diff --git a/charts/recluse/templates/pvc.yaml b/charts/recluse/templates/pvc.yaml deleted file mode 100644 index 2c563d1..0000000 --- a/charts/recluse/templates/pvc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "recluse.fullname" . }} - labels: - {{- include "recluse.labels" . | nindent 4 }} -spec: - accessModes: - {{- toYaml .Values.persistence.accessModes | nindent 4 }} - resources: - requests: - storage: {{ .Values.persistence.size }} - {{- with .Values.persistence.storageClass }} - storageClassName: {{ . | quote }} - {{- end }} -{{- end }} - -# vim: set filetype=helm: diff --git a/charts/recluse/templates/service.yaml b/charts/recluse/templates/service.yaml deleted file mode 100644 index 0367f03..0000000 --- a/charts/recluse/templates/service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -apiVersion: v1 -kind: Service -metadata: - name: {{ include "recluse.fullname" . }} - labels: - {{- include "recluse.labels" . | nindent 4 }} -spec: - selector: - {{- include "recluse.selectorLabels" . | nindent 4 }} - ports: - - name: http - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - -# vim: set filetype=helm: diff --git a/charts/recluse/values.yaml b/charts/recluse/values.yaml deleted file mode 100644 index d6762d8..0000000 --- a/charts/recluse/values.yaml +++ /dev/null @@ -1,78 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -replicaCount: 1 - -image: - repository: ghcr.io/dimidiumlabs/recluse - tag: "" - digest: "" - pullPolicy: IfNotPresent - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -podAnnotations: {} -podLabels: {} - -podSecurityContext: - runAsNonRoot: true - - runAsUser: 10000 - runAsGroup: 10000 - - fsGroup: 10000 - fsGroupChangePolicy: OnRootMismatch - -securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: [ALL] - readOnlyRootFilesystem: true - -service: - port: 2000 - -config: - existingConfigMap: "" - appname: recluse - hostnames: [] - server: - shutdownTimeout: 60 - requestTimeout: 30 - maxBodySize: 64 MiB - maxConcurrentRequests: 512 - rateLimitPeriod: 10 - rateLimitBurstSize: 50 - backends: - go: - enabled: true - refreshInterval: 3600 - zig: - enabled: true - refreshInterval: 3600 - log: - enabled: true - level: info - format: json - extra: "" - -persistence: - enabled: true - existingClaim: "" - accessModes: [ReadWriteOnce] - size: 20Gi - storageClass: "" - -route: - enabled: false - hostnames: [] - parentRefs: [] - -resources: {} -nodeSelector: {} -tolerations: [] -affinity: {} - -terminationGracePeriodSeconds: 70 diff --git a/charts/tesor/Chart.yaml b/charts/tesor/Chart.yaml new file mode 100644 index 0000000..9a70371 --- /dev/null +++ b/charts/tesor/Chart.yaml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +apiVersion: v2 +type: application +version: 0.1.0 + +name: tesor +home: https://git.dimidiumlabs.io/tesor +description: Tiny package caching proxy diff --git a/charts/tesor/templates/_helpers.tpl b/charts/tesor/templates/_helpers.tpl new file mode 100644 index 0000000..41b478a --- /dev/null +++ b/charts/tesor/templates/_helpers.tpl @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +{{- define "tesor.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "tesor.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name (include "tesor.name" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{- define "tesor.labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }} +app.kubernetes.io/name: {{ include "tesor.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- define "tesor.selectorLabels" -}} +app.kubernetes.io/name: {{ include "tesor.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "tesor.image" -}} +{{- if .Values.image.digest -}} +{{- printf "%s@%s" .Values.image.repository .Values.image.digest -}} +{{- else -}} +{{- printf "%s:%s" .Values.image.repository (default .Chart.AppVersion .Values.image.tag) -}} +{{- end -}} +{{- end -}} + +# vim: set filetype=helm: diff --git a/charts/tesor/templates/configmap.yaml b/charts/tesor/templates/configmap.yaml new file mode 100644 index 0000000..5dd823c --- /dev/null +++ b/charts/tesor/templates/configmap.yaml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +{{- if not .Values.config.existingConfigMap }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "tesor.fullname" . }} + labels: + {{- include "tesor.labels" . | nindent 4 }} +data: + tesor.toml: | + appname = {{ .Values.config.appname | quote }} + dirname = "/var/lib/tesor" + + [server] + max_body_size = {{ .Values.config.server.maxBodySize | quote }} + max_concurrent_requests = {{ .Values.config.server.maxConcurrentRequests }} + + rate_limit_period = {{ .Values.config.server.rateLimitPeriod }} + rate_limit_burst_size = {{ .Values.config.server.rateLimitBurstSize }} + + request_timeout = {{ .Values.config.server.requestTimeout }} + shutdown_timeout = {{ .Values.config.server.shutdownTimeout }} + + [[listen]] + addr = "0.0.0.0:2000" + hostnames = {{ .Values.config.hostnames | toJson }} + + {{- range $name, $backend := .Values.config.backends }} + [backends.{{ $name }}] + enabled = {{ $backend.enabled }} + refresh_interval = {{ $backend.refreshInterval }} + + {{- end }} + [telemetry.stdout] + enabled = {{ .Values.config.log.enabled }} + log_level = {{ .Values.config.log.level | quote }} + log_format = {{ .Values.config.log.format | quote }} + {{- with .Values.config.extra }} + +{{ . | indent 4 }} + {{- end }} +{{- end }} + +# vim: set filetype=helm: diff --git a/charts/tesor/templates/deployment.yaml b/charts/tesor/templates/deployment.yaml new file mode 100644 index 0000000..952927e --- /dev/null +++ b/charts/tesor/templates/deployment.yaml @@ -0,0 +1,97 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tesor.fullname" . }} + labels: + {{- include "tesor.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "tesor.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- if not .Values.config.existingConfigMap }} + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- end }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "tesor.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: false + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: tesor + image: {{ include "tesor.image" . | quote }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + args: ["--config=/etc/tesor/tesor.toml"] + ports: + - name: http + containerPort: 2000 + protocol: TCP + startupProbe: + tcpSocket: + port: http + failureThreshold: 60 + periodSeconds: 5 + readinessProbe: + tcpSocket: + port: http + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: http + periodSeconds: 20 + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: config + mountPath: /etc/tesor/tesor.toml + subPath: tesor.toml + readOnly: true + - name: state + mountPath: /var/lib/tesor + volumes: + - name: config + configMap: + name: {{ default (include "tesor.fullname" .) .Values.config.existingConfigMap }} + - name: state + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ default (include "tesor.fullname" .) .Values.persistence.existingClaim }} + {{- else }} + emptyDir: {} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + +# vim: set filetype=helm: diff --git a/charts/tesor/templates/httproute.yaml b/charts/tesor/templates/httproute.yaml new file mode 100644 index 0000000..63e0159 --- /dev/null +++ b/charts/tesor/templates/httproute.yaml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +{{- if .Values.route.enabled }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "tesor.fullname" . }} + labels: + {{- include "tesor.labels" . | nindent 4 }} +spec: + {{- with .Values.route.parentRefs }} + parentRefs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.route.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + - backendRefs: + - name: {{ include "tesor.fullname" . }} + port: {{ .Values.service.port }} +{{- end }} + +# vim: set filetype=helm: diff --git a/charts/tesor/templates/pvc.yaml b/charts/tesor/templates/pvc.yaml new file mode 100644 index 0000000..32a4d7d --- /dev/null +++ b/charts/tesor/templates/pvc.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "tesor.fullname" . }} + labels: + {{- include "tesor.labels" . | nindent 4 }} +spec: + accessModes: + {{- toYaml .Values.persistence.accessModes | nindent 4 }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- with .Values.persistence.storageClass }} + storageClassName: {{ . | quote }} + {{- end }} +{{- end }} + +# vim: set filetype=helm: diff --git a/charts/tesor/templates/service.yaml b/charts/tesor/templates/service.yaml new file mode 100644 index 0000000..1c173a0 --- /dev/null +++ b/charts/tesor/templates/service.yaml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tesor.fullname" . }} + labels: + {{- include "tesor.labels" . | nindent 4 }} +spec: + selector: + {{- include "tesor.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + +# vim: set filetype=helm: diff --git a/charts/tesor/values.yaml b/charts/tesor/values.yaml new file mode 100644 index 0000000..a88ae89 --- /dev/null +++ b/charts/tesor/values.yaml @@ -0,0 +1,78 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +replicaCount: 1 + +image: + repository: ghcr.io/dimidiumlabs/tesor + tag: "" + digest: "" + pullPolicy: IfNotPresent + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + + runAsUser: 10000 + runAsGroup: 10000 + + fsGroup: 10000 + fsGroupChangePolicy: OnRootMismatch + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true + +service: + port: 2000 + +config: + existingConfigMap: "" + appname: tesor + hostnames: [] + server: + shutdownTimeout: 60 + requestTimeout: 30 + maxBodySize: 64 MiB + maxConcurrentRequests: 512 + rateLimitPeriod: 10 + rateLimitBurstSize: 50 + backends: + go: + enabled: true + refreshInterval: 3600 + zig: + enabled: true + refreshInterval: 3600 + log: + enabled: true + level: info + format: json + extra: "" + +persistence: + enabled: true + existingClaim: "" + accessModes: [ReadWriteOnce] + size: 20Gi + storageClass: "" + +route: + enabled: false + hostnames: [] + parentRefs: [] + +resources: {} +nodeSelector: {} +tolerations: [] +affinity: {} + +terminationGracePeriodSeconds: 70 diff --git a/crates/recluse/Cargo.toml b/crates/recluse/Cargo.toml deleted file mode 100644 index c8e90db..0000000 --- a/crates/recluse/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -[package] -name = "recluse" -description = "recluse web-server and daemon" -version.workspace = true - -authors.workspace = true -edition.workspace = true -license.workspace = true -publish.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -base.workspace = true -repos.workspace = true - -async-trait.workspace = true -axum.workspace = true -axum-server.workspace = true -bytes.workspace = true -bytesize.workspace = true -chrono.workspace = true -crc32fast.workspace = true -hex.workspace = true -http-body-util.workspace = true -hyper.workspace = true -hyper-tls.workspace = true -hyper-util.workspace = true -maud.workspace = true -rustls.workspace = true -serde.workspace = true -serde_json.workspace = true -sqlx.workspace = true -thiserror.workspace = true -tokio.workspace = true -tokio-util.workspace = true -toml.workspace = true -tonic.workspace = true -tower.workspace = true -tower-http.workspace = true -tower_governor.workspace = true -tracing.workspace = true -tracing-opentelemetry.workspace = true -tracing-subscriber.workspace = true -url.workspace = true -uuid.workspace = true - -# telemetry -opentelemetry.workspace = true -opentelemetry-appender-tracing.workspace = true -opentelemetry-otlp.workspace = true -opentelemetry-semantic-conventions.workspace = true -opentelemetry_sdk.workspace = true - -[target.'cfg(target_os = "linux")'.dependencies] -sd-notify.workspace = true - -[dev-dependencies] -tempfile.workspace = true diff --git a/crates/recluse/build.rs b/crates/recluse/build.rs deleted file mode 100644 index 0f4e2d3..0000000 --- a/crates/recluse/build.rs +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -fn main() { - println!("cargo::rerun-if-changed=../../Cargo.lock"); - println!("cargo::rerun-if-changed=../../mise.toml"); - println!("cargo::rerun-if-changed=../../mise.lock"); - - let out_dir = std::env::var("OUT_DIR").unwrap(); - let target = std::env::var("TARGET").unwrap(); - let output = format!("{out_dir}/licenses.json"); - - let status = std::process::Command::new("mise") - .current_dir(env!("CARGO_MANIFEST_DIR")) - .args([ - "run", - "licenses-json", - "--", - "--manifest-path", - "Cargo.toml", - "--output", - &output, - "--target", - &target, - ]) - .status() - .expect("failed to run licenses-json task"); - - assert!(status.success(), "failed to generate licenses.json"); -} diff --git a/crates/recluse/src/assets/apple-touch-icon.png b/crates/recluse/src/assets/apple-touch-icon.png deleted file mode 100644 index 0f6bb614210c0d59ae108acd732f59175f0ce984..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaloCO|{#S9G0-5|_ZdRqK9P*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IX1_q`GPZ!6KiaBrZZWKIZAj0Z+ zMj(K-H&DJekagyZhBMR7ybb#>Njf$Dw!~wm(Casu6Vwt1>0w|&_q-~O)tNQqfb-!aYo`|j+`@9U3o(;x_mjFxS^hH3c?nR}=fb(r>kV^z5- W|K3?~yAd!UGI+ZBxvXNjf$Dw!~wm(Casu6Vwt1>0w|&_q-~O)tNQqfb-!aYo`|j+`@9U3o(;x_mjFxS^hH3c?nR}=fb(r>kV^z5- W|K3?~yAd!UGI+ZBxvX9gP2NHH*QEc0}645^s&_J(2a5eEU+ zgR>_yFE-eCG(E}fVa~LpWgk`v)U2GmJg)k^J;Q>h^;HZEAF6@YH?c4u|VWXxp&u`@U~q*k%;f%`@HGs3$)f8BN#jRaZIR zs*dMlP?zT+>h87;*|OLJ(g7FGTJB`?E){?S}AIBNGVtQO%)`N1FB?19Z122WQ%mvv4F FO#l|3Ajkj! diff --git a/crates/recluse/src/assets/favicon.ico b/crates/recluse/src/assets/favicon.ico deleted file mode 100644 index 093e50bda4580e7ef4100b9e057d2e8cc47b2936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32038 zcmeHPyKWRq5G@N!geD?_$&AU0wTVFPl1Hk84fHD-Mc=} zl)I>HS50s4s^+RyQ?GMQ&D7nCmenOXphJ4}h>~5<`$M8vM6_6}{Vys0HN{R(Z~bRP zZypmZmuvszCDF|bqLb8L7J1F0o?ERCdEUo;)N#w}B%k-em+E&rPU+wO_4)pgs60R45#@u})iF^% zsLwWk&MeYdrjtLfR#*Dq`IK(*!L{4ZXn#H<<2{V^F7esoWn15`7cX#vziq$p+`Mc& z|J{E6{QdF0XwCC_?pyJ-e$K~zt>2pGeB8IN&pVYj~}BjgRMB@uR-&zt8c!_U3t|S9$lkCOZG` zIXgSQE)T1pzCD=!Id>^mk^DVH`pT5vrt{}}il;}dO43pbJ@#rd-ZZE&oV7c$5m-AROm%SVpepyrABhPJF zQ{MAB_+^dd9(deJn7j_YSG|NI#lg?*w(2EbDb97n-&VcEE5)Ig+ilfLyi%O&hQF

R4Zc>LQa$i`TS-rAyti)fwd$1Wf!EtgdRpVXb%U=}XKOupthSZc ztKO;`{a$)nalmi2t-N0KR^90L($k6qeyeTe^{Th(M!%Pyz=^+ih#iO>h#iO>*r6TB zQuA+sZ1!XNol(Y_%U)a^Uu67EZL5wOq7NB=Q)_)*xBdBdTFZ2j+`qrmN`dDG>yyH( zE5DpL@eE=IVh3UeVh3UeVh3UeVh3UeVh3tFfbTX(wKdJ2u~FxSF3(wOSl}7e)--#@ zMx7hFJZG(8foD`()9e`=b#CbLoVA7po>6U0vuAA7xuMH*)*2RgMzuA~p0QEqhAz)p zYgphJ)z&n7#zvhRx;$sCVS#5~^9(9tf^?1f4Uc@yn)Jb;kehpvCOS%ysyz8;(iEH|; z`BCPSbXe!p`X$~`;+lSIev~;S9oG4@eu;OKxTfElA7xHShjl({zog46Zq+w;zt@}+ z&fNN~_>yj~xK-cW{a$lQICJZ_;!C={;#Pfg_j}DL;mobyiZAK*id*%~-S0K0gfqAP zz>nX3#ty^|#16y`#16y`#16y`#18EB4y4yR<8_(;Hv>o0v^9Hlnf^)q_q9g-dGJKP NdI0ybGrpSy{TrMyi)R1; diff --git a/crates/recluse/src/assets/favicon.svg b/crates/recluse/src/assets/favicon.svg deleted file mode 100644 index ed7cc17..0000000 --- a/crates/recluse/src/assets/favicon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/crates/recluse/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 b/crates/recluse/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 deleted file mode 100644 index 98adc803f58f81df8410f4ad433e46b773e1b8d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122452 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 diff --git a/crates/recluse/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 b/crates/recluse/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 deleted file mode 100644 index 4cd1413366e027b0929706deb3ac04baf7328526..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113700 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 z 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 Self { - Self { - shutdown_timeout: Duration::from_secs(60), - request_timeout: Duration::from_secs(30), - max_body_size: ByteSize::mb(64), - max_concurrent_requests: 512, - rate_limit_period: Duration::from_secs(10), - rate_limit_burst_size: 50, - } - } -} - -#[derive(Debug, Clone, Deserialize)] -pub struct ListenerConfig { - #[serde(deserialize_with = "deserialize_listener_addr")] - pub addr: SocketAddr, - - /// Hostnames to accept for this listener. Empty means accept all. - pub hostnames: Vec, - - /// Path to TLS certificate file (PEM format). If set, tls_key must also be set. - pub tls_crt: Option, - - /// Path to TLS private key file (PEM format). If set, tls_crt must also be set. - pub tls_key: Option, -} - -impl Default for ListenerConfig { - fn default() -> Self { - Self { - addr: "127.0.0.1:2025".parse().unwrap(), - hostnames: vec![ - String::from("[::1]"), - String::from("127.0.0.1"), - String::from("localhost"), - ], - tls_crt: None, - tls_key: None, - } - } -} - -#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -#[serde(rename_all = "lowercase")] -pub enum LogLevel { - Trace, - Debug, - Info, - Warning, - Error, -} - -#[derive(Debug, Clone, Copy, Deserialize, Default, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] -pub enum StdoutFormat { - #[default] - Pretty, - Json, -} - -#[derive(Debug, Clone, Deserialize)] -#[serde(default)] -pub struct StdoutConfig { - /// Enables sending logs to the stdout - pub enabled: bool, - - /// Controls which logs will be sent to stdout - pub log_level: LogLevel, - - /// Controls the format of logs in stdout - pub log_format: StdoutFormat, -} - -impl Default for StdoutConfig { - fn default() -> Self { - Self { - enabled: true, - log_level: LogLevel::Info, - log_format: StdoutFormat::Pretty, - } - } -} - -#[derive(Debug, Clone, Deserialize)] -#[serde(default)] -pub struct OtelcolConfig { - /// Enables sending telemetry to the otlp collector - pub enabled: bool, - - /// Send logs to OTLP at this level (None = disabled) - pub logs: bool, - - /// Send traces to OTLP - pub traces: bool, - - /// Send traces to OTLP - pub metrics: bool, - - /// OTLP endpoint (grpc:// or http://) - pub endpoint: String, - - /// Export timeout in seconds - #[serde(deserialize_with = "deserialize_duration")] - pub timeout: Duration, - - /// Controls which logs will be sent to otlp - pub log_level: LogLevel, - - /// Path to CA certificate for TLS (required for grpcs://) - pub tls_ca: Option, - - /// Path to client certificate for mTLS - pub tls_crt: Option, - - /// Path to client key for mTLS - pub tls_key: Option, - - /// HTTP headers for authentication - pub headers: HashMap, -} - -impl Default for OtelcolConfig { - fn default() -> Self { - Self { - enabled: true, - logs: true, - traces: true, - metrics: true, - timeout: Duration::from_secs(10), - endpoint: "http://localhost:4317".into(), - log_level: LogLevel::Info, - tls_ca: None, - tls_crt: None, - tls_key: None, - headers: HashMap::new(), - } - } -} - -#[derive(Debug, Clone, Deserialize, Default)] -#[serde(default)] -pub struct TelemetryConfig { - pub stdout: StdoutConfig, - pub otelcol: Option, -} - -#[derive(Debug, Clone, Deserialize, Default)] -#[serde(default)] -pub struct BackendsConfig { - pub go: GoConfig, - pub zig: ZigConfig, -} - -#[derive(Debug, Deserialize)] -#[serde(default)] -pub struct ConfigService { - appname: String, - dirname: PathBuf, - listen: Vec, - server: ServerConfig, - telemetry: TelemetryConfig, - backends: BackendsConfig, -} - -impl Default for ConfigService { - fn default() -> Self { - Self { - appname: "recluse".to_string(), - dirname: PathBuf::from("./.recluse-state"), - listen: vec![ListenerConfig::default()], - server: ServerConfig::default(), - telemetry: TelemetryConfig::default(), - backends: BackendsConfig::default(), - } - } -} - -impl ConfigService { - pub fn load(config_path: Option) -> Result { - let config = match config_path { - Some(path) => { - info!("use config file from {}", path.to_str().unwrap()); - - let content = fs::read_to_string(path)?; - toml::from_str(&content)? - } - None => { - info!("configuration file path not provided"); - Self::default() - } - }; - - config.validate() - } - - fn validate(self) -> Result { - let mut chars = self.appname.chars(); - if !chars.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') { - return Err(ConfigError::InvalidAppname(self.appname.clone())); - } - - let metadata = fs::metadata(&self.dirname).map_err(|e| { - if e.kind() == std::io::ErrorKind::NotFound { - ConfigError::DirNotFound(self.dirname.clone()) - } else { - ConfigError::Io(e) - } - })?; - - if !metadata.is_dir() { - return Err(ConfigError::NotADirectory(self.dirname.clone())); - } - - let testfile = self.dirname.join(".health"); - fs::write(&testfile, std::process::id().to_string()) - .map_err(|e| ConfigError::NotWritable(self.dirname.clone(), e))?; - fs::remove_file(&testfile)?; - - // Validate listener configuration - for listener in &self.listen { - match (&listener.tls_crt, &listener.tls_key) { - (Some(_crt), None) => { - return Err(ConfigError::TlsKeyMissing(listener.addr)); - } - (None, Some(_)) => { - return Err(ConfigError::TlsCrtMissing(listener.addr)); - } - (Some(crt), Some(key)) => { - if !crt.exists() { - return Err(ConfigError::TlsCrtNotFound(listener.addr, crt.clone())); - } - if !key.exists() { - return Err(ConfigError::TlsKeyNotFound(listener.addr, key.clone())); - } - } - (None, None) => {} - } - } - - Ok(self) - } - - pub fn appname(&self) -> &str { - &self.appname - } - - pub fn dirname(&self) -> &Path { - &self.dirname - } - - pub fn server(&self) -> &ServerConfig { - &self.server - } - - pub fn listeners(&self) -> &[ListenerConfig] { - &self.listen - } - - pub fn telemetry(&self) -> &TelemetryConfig { - &self.telemetry - } - - pub fn backends(&self) -> &BackendsConfig { - &self.backends - } -} - -#[cfg(test)] -impl ConfigService { - pub fn for_test(dirname: PathBuf) -> Self { - Self { - appname: "test".to_string(), - dirname, - server: ServerConfig::default(), - listen: vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: None, - tls_key: None, - }], - telemetry: TelemetryConfig::default(), - backends: BackendsConfig::default(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::TempDir; - - fn base_config(dir: PathBuf) -> ConfigService { - ConfigService { - appname: "valid-name".to_string(), - dirname: dir, - server: ServerConfig::default(), - listen: vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: None, - tls_key: None, - }], - telemetry: TelemetryConfig::default(), - backends: BackendsConfig::default(), - } - } - - fn write_temp_file(dir: &Path, name: &str) -> PathBuf { - let path = dir.join(name); - std::fs::write(&path, "x").unwrap(); - path - } - - mod defaults_tests { - use super::*; - - #[test] - fn test_for_test_config() { - let dir = TempDir::new().unwrap(); - let cfg = ConfigService::for_test(dir.path().to_path_buf()); - assert_eq!(cfg.appname(), "test"); - assert_eq!(cfg.dirname(), dir.path()); - } - - #[test] - fn test_listener_default() { - let cfg = ListenerConfig::default(); - assert!(cfg.hostnames.contains(&"localhost".to_string())); - } - } - - mod load_tests { - use super::*; - - #[test] - fn test_load_none_defaults() { - let temp = TempDir::new().unwrap(); - let state = temp.path().join(".recluse-state"); - std::fs::create_dir_all(&state).unwrap(); - let cwd = std::env::current_dir().unwrap(); - std::env::set_current_dir(temp.path()).unwrap(); - - let cfg = ConfigService::load(None).unwrap(); - assert_eq!(cfg.appname(), "recluse"); - - std::env::set_current_dir(cwd).unwrap(); - } - - #[test] - fn test_load_some_and_parse_error() { - let dir = TempDir::new().unwrap(); - let good = dir.path().join("good.toml"); - let bad = dir.path().join("bad.toml"); - let missing = dir.path().join("missing.toml"); - let state = dir.path().join("state"); - std::fs::create_dir_all(&state).unwrap(); - - std::fs::write( - &good, - format!( - "appname = \"ok\"\n\ - dirname = \"{}\"\n\ - listen = []\n\ - [server]\n\ - shutdown_timeout = 1\n\ - request_timeout = 1\n\ - max_body_size = \"1 MB\"\n\ - max_concurrent_requests = 1\n\ - rate_limit_period = 1\n\ - rate_limit_burst_size = 1\n\ - [telemetry]\n\ - [telemetry.stdout]\n\ - enabled = true\n\ - log_level = \"info\"\n\ - log_format = \"pretty\"\n\ - [backends]\n\ - [backends.go]\n\ - [backends.zig]\n", - state.display() - ), - ) - .unwrap(); - - std::fs::write(&bad, "not = [valid").unwrap(); - - let cfg = ConfigService::load(Some(good)).unwrap(); - assert_eq!(cfg.appname(), "ok"); - - let err = ConfigService::load(Some(missing)).unwrap_err(); - assert!(err.to_string().contains("failed to read")); - - let err = ConfigService::load(Some(bad)).unwrap_err(); - assert!(err.to_string().contains("failed to parse")); - } - } - - mod validation_tests { - use super::*; - - #[test] - fn test_validate_invalid_appname() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - cfg.appname = "bad name!".to_string(); - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("appname 'bad name!'")); - } - - #[test] - fn test_validate_dir_not_found() { - let temp = TempDir::new().unwrap(); - let missing = temp.path().join("missing-dir"); - let cfg = base_config(missing); - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("does not exist")); - } - - #[test] - fn test_validate_not_a_directory() { - let dir = TempDir::new().unwrap(); - let file_path = dir.path().join("file"); - std::fs::write(&file_path, "x").unwrap(); - let cfg = base_config(file_path); - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("is not a directory")); - } - - #[cfg(unix)] - #[test] - fn test_validate_metadata_io_error() { - use std::os::unix::fs::PermissionsExt; - - let dir = TempDir::new().unwrap(); - let child = dir.path().join("child"); - std::fs::create_dir_all(&child).unwrap(); - - let mut perms = std::fs::metadata(dir.path()).unwrap().permissions(); - perms.set_mode(0o000); - std::fs::set_permissions(dir.path(), perms).unwrap(); - - let cfg = base_config(child.clone()); - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("failed to read")); - - let mut perms = std::fs::metadata(dir.path()).unwrap().permissions(); - perms.set_mode(0o700); - std::fs::set_permissions(dir.path(), perms).unwrap(); - } - - #[cfg(unix)] - #[test] - fn test_validate_not_writable() { - use std::os::unix::fs::PermissionsExt; - - let dir = TempDir::new().unwrap(); - let mut perms = std::fs::metadata(dir.path()).unwrap().permissions(); - perms.set_mode(0o400); - std::fs::set_permissions(dir.path(), perms).unwrap(); - - let cfg = base_config(dir.path().to_path_buf()); - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("not writable")); - } - - #[test] - fn test_validate_tls_key_missing() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - cfg.listen = vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: Some(PathBuf::from("/tmp/does-not-matter.crt")), - tls_key: None, - }]; - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("tls_key is missing")); - } - - #[test] - fn test_validate_tls_crt_missing() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - cfg.listen = vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: None, - tls_key: Some(PathBuf::from("/tmp/does-not-matter.key")), - }]; - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("tls_crt is missing")); - } - - #[test] - fn test_validate_tls_files_not_found() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - cfg.listen = vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: Some(PathBuf::from("/tmp/missing.crt")), - tls_key: Some(PathBuf::from("/tmp/missing.key")), - }]; - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("TLS crtificate file not found")); - } - - #[test] - fn test_validate_tls_key_not_found() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - let crt = write_temp_file(dir.path(), "cert.pem"); - cfg.listen = vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: Some(crt), - tls_key: Some(dir.path().join("missing.key")), - }]; - let err = cfg.validate().unwrap_err(); - assert!(err.to_string().contains("TLS key file not found")); - } - - #[test] - fn test_validate_tls_files_exist() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - let crt = write_temp_file(dir.path(), "cert.pem"); - let key = write_temp_file(dir.path(), "key.pem"); - cfg.listen = vec![ListenerConfig { - addr: "127.0.0.1:0".parse().unwrap(), - hostnames: Vec::new(), - tls_crt: Some(crt), - tls_key: Some(key), - }]; - cfg.validate().unwrap(); - } - - #[test] - fn test_validate_ok_and_getters() { - let dir = TempDir::new().unwrap(); - let mut cfg = base_config(dir.path().to_path_buf()); - cfg.listen = vec![ListenerConfig::default()]; - - let cfg = cfg.validate().unwrap(); - assert_eq!(cfg.appname(), "valid-name"); - assert_eq!(cfg.dirname(), dir.path()); - let _ = cfg.server(); - let _ = cfg.listeners(); - let _ = cfg.telemetry(); - let _ = cfg.backends(); - } - } -} diff --git a/crates/recluse/src/controller_backend.rs b/crates/recluse/src/controller_backend.rs deleted file mode 100644 index f97caa1..0000000 --- a/crates/recluse/src/controller_backend.rs +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -use std::sync::Arc; - -use axum::{Router, body, extract, http, response, routing}; -use tracing::error; - -use crate::proxy; -use crate::storage; -use repos::{Backend, BackendError, BackendSpec, ResolvedFile}; - -/// Generic controller for backend HTTP handling. -pub struct BackendController { - backend: Arc>, - storage: Arc, - upstream: Arc, -} - -impl BackendController { - pub fn new( - backend: Arc>, - storage: Arc, - upstream: Arc, - ) -> Self { - Self { - backend, - storage, - upstream, - } - } - - pub fn router(self: Arc) -> Router { - Router::new() - .route("/{filename}", routing::get(Self::handle)) - .with_state(self) - } - - async fn handle( - extract::State(controller): extract::State>, - extract::Path(filename): extract::Path, - ) -> Result { - let (url, mime) = match controller.backend.resolve_file(&filename).await { - Ok(ResolvedFile::Content { data, mime }) => { - return Ok(Self::build_response(http::StatusCode::OK, data, mime)); - } - Ok(ResolvedFile::Upstream { url, mime }) => (url, mime), - Err(BackendError::NotFound) => { - error!(backend = S::ID, filename, "file not found"); - return Err(http::StatusCode::NOT_FOUND); - } - Err(e) => { - error!(backend = S::ID, filename, "error resolving file: {e}"); - return Err(http::StatusCode::INTERNAL_SERVER_ERROR); - } - }; - - match controller.storage.get(S::ID, &filename).await { - Ok(Some(entry)) => { - return Ok(Self::build_response( - http::StatusCode::OK, - entry.file_bytes.0, - mime, - )); - } - Ok(None) => {} - Err(err) => { - error!( - backend = S::ID, - filename, "failed to get file from storage: {err}" - ); - return Err(http::StatusCode::INTERNAL_SERVER_ERROR); - } - } - - let entry = controller - .upstream - .fetch(proxy::DownloadRequest { url }) - .await?; - - match controller.storage.put(S::ID, &filename, &entry.bytes).await { - Ok(()) => {} - Err(err) => { - error!( - backend = S::ID, - filename, "failed to put file to storage: {err}" - ); - return Err(http::StatusCode::INTERNAL_SERVER_ERROR); - } - } - - Ok(Self::build_response( - http::StatusCode::OK, - entry.bytes, - mime, - )) - } - - fn build_response( - status: http::StatusCode, - bytes: bytes::Bytes, - mime: repos::ContentType, - ) -> response::Response { - response::Response::builder() - .status(status) - .header(http::header::CONTENT_TYPE, mime.as_str()) - .header(http::header::CONTENT_LENGTH, bytes.len()) - .body(body::Body::from(bytes)) - .unwrap() - } -} diff --git a/crates/recluse/src/controller_web.rs b/crates/recluse/src/controller_web.rs deleted file mode 100644 index fef09ee..0000000 --- a/crates/recluse/src/controller_web.rs +++ /dev/null @@ -1,486 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -use std::future::Future; -use std::pin::Pin; -use std::sync::{Arc, LazyLock}; -use std::task::{Context, Poll}; - -use axum::body::Body; -use axum::http::{HeaderValue, Method, Request, Response, StatusCode, header}; -use axum::{extract, routing}; -use maud::{Markup, html}; -use serde::Deserialize; -use tower::{Layer, Service}; -use tracing::error; - -use base::ContentType; -use repos::{GoBackend, ZigBackend}; - -fn get_asset(path: &str) -> Option<(&'static [u8], ContentType)> { - Some(match path { - "base.css" => (include_bytes!("assets/base.css"), ContentType::TextCss), - "favicon.svg" => ( - include_bytes!("assets/favicon.svg"), - ContentType::ImageSvgXml, - ), - "favicon.ico" => ( - include_bytes!("assets/favicon.ico"), - ContentType::ImageXIcon, - ), - "favicon-192.png" | "apple-touch-icon.png" => ( - include_bytes!("assets/favicon-192.png"), - ContentType::ImagePng, - ), - "favicon-512.png" => ( - include_bytes!("assets/favicon-512.png"), - ContentType::ImagePng, - ), - "manifest.webmanifest" => ( - include_bytes!("assets/manifest.webmanifest"), - ContentType::ApplicationManifestJson, - ), - "jetbrainsmono/JetBrainsMono[wght].woff2" => ( - include_bytes!("assets/jetbrainsmono/JetBrainsMono[wght].woff2"), - ContentType::FontWoff2, - ), - "jetbrainsmono/JetBrainsMono-Italic[wght].woff2" => ( - include_bytes!("assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2"), - ContentType::FontWoff2, - ), - "robots.txt" => (include_bytes!("assets/robots.txt"), ContentType::TextPlain), - _ => return None, - }) -} - -const CSP: &str = "default-src 'self'; base-uri 'none'; img-src 'self'; font-src 'self'; style-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'"; - -#[derive(Deserialize)] -struct LicenseList { - overview: Vec, - licenses: Vec, -} - -#[derive(Deserialize)] -struct LicenseOverview { - id: String, - name: String, - count: usize, -} - -#[derive(Deserialize)] -struct LicenseEntry { - id: String, - name: String, - text: String, - first_of_kind: bool, - used_by: Vec, -} - -#[derive(Deserialize)] -struct LicenseUsedBy { - #[serde(rename = "crate")] - crate_: LicenseCrate, -} - -#[derive(Deserialize)] -#[allow(dead_code)] -struct LicenseCrate { - name: String, - version: String, - repository: Option, -} - -static LICENSES: LazyLock = LazyLock::new(|| { - let json = include_str!(concat!(env!("OUT_DIR"), "/licenses.json")); - serde_json::from_str(json).expect("failed to parse licenses.json") -}); - -/// Handles html pages rendering and static files -pub struct WebController { - zig: Option>, - go: Option>, -} - -impl WebController { - pub fn new(zig: Option>, go: Option>) -> Self { - Self { zig, go } - } -} - -impl WebController { - pub fn router(self: Arc) -> axum::Router { - axum::Router::new() - .route("/", axum::routing::get(Self::index)) - .route("/{*path}", routing::get(Self::assets)) - .route("/about/licenses", axum::routing::get(Self::licenses)) - .layer(CacheLayer) - .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( - header::CONTENT_SECURITY_POLICY, - HeaderValue::from_static(CSP), - )) - .with_state(self.clone()) - } - - async fn assets(extract::Path(path): extract::Path) -> Response { - let Some((data, content_type)) = get_asset(&path) else { - return Response::builder() - .status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .unwrap(); - }; - - let mut builder = Response::builder() - .status(StatusCode::OK) - .header(header::CONTENT_TYPE, content_type.as_str()); - - if matches!(content_type, ContentType::FontWoff2) { - builder = builder.header(header::CACHE_CONTROL, "public, max-age=31536000, immutable"); - } - - builder.body(Body::from(data)).unwrap() - } - - async fn index(extract::State(ctrl): extract::State>) -> Markup { - let zig_versions = if let Some(ref backend) = ctrl.zig { - match backend.get_releases().await { - Ok(v) => v, - Err(e) => { - error!("failed to get zig versions: {e}"); - Vec::new() - } - } - } else { - Vec::new() - }; - - let go_versions = if let Some(ref backend) = ctrl.go { - match backend.get_releases().await { - Ok(v) => v, - Err(e) => { - error!("failed to get go versions: {e}"); - Vec::new() - } - } - } else { - Vec::new() - }; - - WebController::layout( - "Recluse — tiny & opinionated packages mirror", - html! { - h1 { "Recluse — tiny & opinionated packages mirror." } - - p { - r#"This site provides a caching proxy for downloading Zig and Go installation files. - It reduces load on upstream servers and makes your infrastructure more reliable by adding redundancy."# - } - - p { - "Recluse is open source software licensed under " a href="https://www.gnu.org/licenses/agpl-3.0.html" { "AGPL-3.0" } ". " - "Source code is available on " a href="https://github.com/dimidiumlabs/recluse" { "GitHub" } ". " - "A list of dependency licenses " a href="/about/licenses" { "is available" } "." - } - - h2 { "Usage" } - - p { - "Replace official download URLs with " code { "https://pkg.earth/{tool}/{filename}" } ". " - "Files are cached automatically after the first download." - } - - h3 id="zig" { - a href="#zig" { "Zig" } - } - - @if !zig_versions.is_empty() { - details { - summary { "Available versions (" (zig_versions.len()) ")" } - - p { "You can take actual minisig public key at " a href="https://ziglang.org/download/" { "ziglang.org/download" } "." } - table { - thead { - tr { - th { "Version" } - th { "Date" } - th { "Docs" } - th { "Targets" } - } - } - tbody { - @for v in zig_versions.iter().rev() { - tr { - td { (v.version) } - td { (v.meta.date.as_deref().unwrap_or("-")) } - td { - @if let Some(ref url) = v.meta.docs { - a href=(url) { "docs" } - } - " " - @if let Some(ref url) = v.meta.std_docs { - a href=(url) { "std" } - } - " " - @if let Some(ref url) = v.meta.notes { - a href=(url) { "notes" } - } - } - td { - @for file in &v.files { - a href=(format!("/zig/{}", file.filename)) { (&file.meta.target) } - " " - } - } - } - } - } - } - } - } - - p { - "Read more about community mirrors in the " a href="https://ziglang.org/download/community-mirrors/" { "blog post" } ". " - "Information on how to deploy your own mirror is available " a href="https://github.com/ziglang/www.ziglang.org/blob/main/MIRRORS.md" { "in the documentation" } "." - } - - p { - "For simplicity, you can use tools like " a href="https://github.com/prantlf/zigup" { "prantlf/zigup" } - " and " a href="https://github.com/mlugg/setup-zig" { "mlugg/setup-zig" } "." - } - - p { - "To install manually:" - ol { - li { "download zig dist file:" br; code { "wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz" } ";" } - li { "download zig minisig file:" br; code { "wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz.minisig" } ";" } - li { "check archive integrity:" br; code { "minisign -Vm zig-x86_64-linux-0.15.1.tar.xz -P RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" } ";" } - li { "unpack archive:" br; code { "tar -xf 'zig-x86_64-linux-0.15.1.tar.xz'" } ";" } - li { "check installed zig:" br; code { "./zig-x86_64-linux-0.15.1/zig --version" } ";" } - } - } - - h3 id="go" { a href="#go" { "Go" } } - - @if !go_versions.is_empty() { - details { - summary { "Available versions (" (go_versions.len()) ")" } - - p { "You can find available versions at " a href="https://go.dev/dl/" { "go.dev/dl" } "." } - table { - thead { - tr { - th { "Version" } - th { "Stable" } - th { "Files" } - } - } - tbody { - @for v in go_versions.iter().rev() { - tr { - td { (v.version) } - td { @if v.meta.stable { "✓" } @else { "" } } - td { - @for file in &v.files { - a href=(format!("/go/{}", file.filename)) { (file.filename) } - " " - } - } - } - } - } - } - } - } - - p { "To install manually:" } - - ol { - li { "download go dist file:" br; code { "wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz" } ";" } - li { "download go sha256 file:" br; code { "wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz.sha256" } ";" } - li { "check archive integrity:" br; code { "sha256sum -c go1.23.0.linux-amd64.tar.gz.sha256" } ";" } - li { "unpack archive:" br; code { "tar -xzf go1.23.0.linux-amd64.tar.gz" } ";" } - li { "check installed go:" br; code { "./go/bin/go version" } ";" } - } - - h2 { "Privacy policy" } - - p { "This mirror is a non-profit project available on a voluntary basis. The author has no plans to fund it." } - - p { r#"Since the mirror is hosted on hardware, we collect access logs to combat bots and brute-force attacks. - The logs are used for security purposes and load planning, are not shared with third parties, - and are deleted after 30 days."# } - - p { "Third-party analytics systems are not used, same as client-side trackers." } - }, - ) - } - - async fn licenses() -> Markup { - let data = &*LICENSES; - - WebController::layout( - "Third Party Licenses", - html! { - h1 { "Licenses" } - - h2 { "Recluse" } - - p { - "Recluse is licensed under the " b { "GNU Affero General Public License v3.0 (AGPL-3.0)" } ". " - "This means you are free to use, modify, and distribute the software. " - "If you run a modified version of Recluse as a network service, " - "you must make the source code of your modifications available to its users. " - "The software is provided as-is, without warranty of any kind." - } - - p { - "Source code is available on " - a href="https://github.com/dimidiumlabs/recluse" { "GitHub" } - ". The full license text is included below." - } - - pre .license-text { (include_str!("../../../LICENSE")) } - - h2 { "Third party licenses" } - - ul { - @for ov in &data.overview { - li { a href=(format!("#{}", ov.id)) { (&ov.name) } " (" (ov.count) ")" } - } - } - - @for (i, lic) in data.licenses.iter().enumerate() { - @if lic.first_of_kind { - h3 id=(&lic.id) { (&lic.name) } - } - - section.license { - h4 id=(format!("{}-{}", lic.id, i)) { (&lic.name) } - - p { "Used by:" } - - ul .license-used-by { - @for usage in &lic.used_by { - li { - a href=( - usage.crate_.repository.as_deref() - .unwrap_or(&format!("https://crates.io/crates/{}", usage.crate_.name)) - ) { - (&usage.crate_.name) "@" (&usage.crate_.version) - } - } - } - } - - pre .license-text { (&lic.text) } - } - } - }, - ) - } - - fn layout(title: &str, content: Markup) -> Markup { - html! { - (maud::DOCTYPE) - - html lang="en" { - head { - meta charset="UTF-8"; - meta http-equiv="X-UA-Compatible" content="ie=edge"; - meta name="viewport" content="width=device-width,initial-scale=1"; - - title { (title) } - link rel="stylesheet" href="/base.css"; - - link rel="manifest" href="/manifest.webmanifest"; - link rel="icon" href="/favicon.ico" sizes="32x32"; - link rel="icon" href="/favicon.svg" type="image/svg+xml"; - link rel="apple-touch-icon" href="/apple-touch-icon.png"; - } - - body { - article { (content) } - } - } - } - } -} - -#[derive(Clone)] -struct CacheLayer; - -impl Layer for CacheLayer { - type Service = CacheService; - fn layer(&self, inner: S) -> Self::Service { - CacheService { inner } - } -} - -#[derive(Clone)] -struct CacheService { - inner: S, -} - -impl Service> for CacheService -where - S: Service, Response = Response> + Clone + Send + 'static, - S::Future: Send + 'static, - S::Error: Send + 'static, -{ - type Response = Response; - type Error = S::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, req: Request) -> Self::Future { - let method = req.method().clone(); - let if_none_match = req.headers().get(header::IF_NONE_MATCH).cloned(); - - let mut inner = self.inner.clone(); - Box::pin(async move { - let resp = inner.call(req).await?; - - if !resp.status().is_success() { - return Ok(resp); - } - - let (parts, body) = resp.into_parts(); - let bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap(); - - let mut hasher = crc32fast::Hasher::new(); - hasher.update(&bytes); - let etag = format!("\"{}\"", hex::encode(hasher.finalize().to_be_bytes())); - let etag_header = HeaderValue::from_str(&etag).unwrap(); - let cache_control = parts - .headers - .get(header::CACHE_CONTROL) - .cloned() - .unwrap_or(HeaderValue::from_static("no-cache")); - - if (method == Method::GET || method == Method::HEAD) - && if_none_match - .as_ref() - .is_some_and(|v| v.as_bytes() == etag.as_bytes()) - { - let mut resp = Response::builder() - .status(StatusCode::NOT_MODIFIED) - .body(Body::empty()) - .unwrap(); - resp.headers_mut().insert(header::ETAG, etag_header); - resp.headers_mut() - .insert(header::CACHE_CONTROL, cache_control); - return Ok(resp); - } - - let mut resp = Response::from_parts(parts, Body::from(bytes)); - resp.headers_mut().insert(header::ETAG, etag_header); - resp.headers_mut() - .entry(header::CACHE_CONTROL) - .or_insert(HeaderValue::from_static("no-cache")); - Ok(resp) - }) - } -} diff --git a/crates/recluse/src/main.rs b/crates/recluse/src/main.rs deleted file mode 100644 index a42eaee..0000000 --- a/crates/recluse/src/main.rs +++ /dev/null @@ -1,567 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -mod config; -mod proxy; -mod storage; -mod telemetry; - -mod controller_backend; -mod controller_web; - -use std::future::Future; -use std::net::{SocketAddr, TcpListener}; -use std::path::PathBuf; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; - -use axum::{ - body::Body, - extract::{self, ConnectInfo}, - http::{self, Request, Response}, -}; -use axum_server::Handle; -use axum_server::tls_rustls::RustlsConfig; -#[cfg(target_os = "linux")] -use sd_notify::NotifyState; -use tokio::signal; -use tracing::{error, info, trace}; -use tracing_subscriber::registry::LookupSpan; - -use crate::controller_backend::BackendController; -use crate::controller_web::WebController; -use repos::{Backend, BackendSpec, GoBackend, ZigBackend}; - -async fn init_backend( - backend: Backend, - index_tasks: &mut tokio::task::JoinSet<()>, - index_cancel: tokio_util::sync::CancellationToken, -) -> Option>> { - if !backend.enabled() { - return None; - } - let backend = Arc::new(backend); - let interval = backend.refresh_interval(); - if !interval.is_zero() { - index_tasks.spawn(run_index_refresh( - S::ID, - backend.clone(), - interval, - index_cancel, - )); - } - Some(backend) -} - -async fn run_index_refresh( - name: &'static str, - backend: Arc>, - interval: std::time::Duration, - cancel: tokio_util::sync::CancellationToken, -) { - let mut ticker = tokio::time::interval(interval); - ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); - - loop { - tokio::select! { - _ = cancel.cancelled() => { - info!(backend = name, "index refresh stopped"); - break; - } - _ = ticker.tick() => { - info!(backend = name, "refreshing index"); - match backend.refresh().await { - Ok(()) => info!(backend = name, "index refreshed"), - Err(e) => error!(backend = name, "index refresh failed: {e}"), - } - } - } - } -} - -const VERSION: &str = env!("CARGO_PKG_VERSION"); -const HELP: &str = "\ -Usage: recluse [--config=] - -Options: - --config= Path to config file (optional) - --help Show this help message - --version Show version -"; - -/// Contains metainfo about one server interface -#[derive(Clone)] -struct ListenerInfo { - addr: SocketAddr, - hosts: Vec, -} - -/// Request info stored in span extensions for logging -#[derive(Clone)] -struct RequestInfo { - method: http::Method, - version: http::Version, - path: http::Uri, - host: Option, - user_agent: Option, -} - -/// Key extractor for tower-governor that uses ConnectInfo to get the client IP. -#[derive(Clone)] -struct ClientIpKeyExtractor; -impl tower_governor::key_extractor::KeyExtractor for ClientIpKeyExtractor { - type Key = std::net::IpAddr; - - fn name(&self) -> &'static str { - "ClientIpKeyExtractor" - } - - fn extract(&self, req: &Request) -> Result { - req.extensions() - .get::>() - .map(|ci| ci.0.ip()) - .ok_or(tower_governor::GovernorError::UnableToExtractKey) - } -} - -#[tokio::main] -async fn main() { - let mut config_path = None; - for arg in std::env::args().skip(1) { - if arg == "--help" || arg == "-h" { - print!("{HELP}"); - return; - } - if arg == "--version" || arg == "-V" { - println!("recluse {VERSION}"); - return; - } - if let Some(path) = arg.strip_prefix("--config=") { - config_path = Some(PathBuf::from(path)); - } - } - - let config = Arc::new( - config::ConfigService::load(config_path).unwrap_or_else(|e| { - eprintln!("invalid config: {e}"); - std::process::exit(1); - }), - ); - - let mut telemetry = - telemetry::TelemetryService::init(config.telemetry(), config.appname(), VERSION); - - let storage = Arc::new(storage::StorageService::new(config.clone()).await.unwrap()); - let network = Arc::new(proxy::ProxyService::new()); - - let source = format!("recluse:{}", config.appname()); - let backends = config.backends(); - - const REQUEST_ID_HEADER: http::HeaderName = http::HeaderName::from_static("x-request-id"); - - let trace_layer = tower_http::trace::TraceLayer::new_for_http() - .make_span_with(|req: &http::Request| { - let request_id = req - .headers() - .get(&REQUEST_ID_HEADER) - .and_then(|v| v.to_str().ok()) - .unwrap_or(""); - let local_addr = req.extensions().get::().map(|a| a.addr); - let remote_addr = req - .extensions() - .get::>() - .map(|ci| ci.0.ip()); - - tracing::info_span!( - "http_request", - request_id = %request_id, - local_addr = ?local_addr, - remote_addr = ?remote_addr, - ) - }) - .on_request(|req: &Request, span: &tracing::Span| { - let info = RequestInfo { - method: req.method().clone(), - path: req.uri().clone(), - version: req.version(), - host: extract_host(req), - user_agent: req - .headers() - .get(http::header::USER_AGENT) - .and_then(|v| v.to_str().ok()) - .map(String::from), - }; - - span.with_subscriber(|(id, dispatch)| { - if let Some(reg) = dispatch.downcast_ref::() - && let Some(span_ref) = reg.span(id) - { - span_ref.extensions_mut().insert(info); - } - }); - }) - .on_response( - |res: &Response, latency: std::time::Duration, span: &tracing::Span| { - use axum::body::HttpBody as _; - - let status = res.status().as_u16(); - let content_length = res.body().size_hint().exact(); - let content_type = res - .headers() - .get(http::header::CONTENT_TYPE) - .and_then(|v| v.to_str().ok()); - - let req_info = span.with_subscriber(|(id, dispatch)| { - dispatch - .downcast_ref::() - .and_then(|reg| reg.span(id)) - .and_then(|span_ref| span_ref.extensions().get::().cloned()) - }); - - if let Some(Some(req_info)) = req_info { - info!( - method = %req_info.method, - version = ?req_info.version, - path = %req_info.path, - host = req_info.host, - user_agent = req_info.user_agent, - status, - latency = latency.as_nanos() as u64, - content_type, - content_length, - "on_response", - ); - } else { - info!( - status, - latency = latency.as_nanos() as u64, - content_type, - content_length, - "on_response", - ); - } - }, - ) - .on_failure(tower_http::trace::DefaultOnFailure::new().level(tracing::Level::ERROR)); - - let governor_config = tower_governor::governor::GovernorConfigBuilder::default() - .period(config.server().rate_limit_period) - .burst_size(config.server().rate_limit_burst_size) - .key_extractor(ClientIpKeyExtractor) - .finish() - .unwrap(); - - let mut index_tasks = tokio::task::JoinSet::new(); - let index_cancel = tokio_util::sync::CancellationToken::new(); - - let zig_backend = init_backend( - ZigBackend::new( - backends.zig.clone(), - source.clone(), - storage.clone(), - network.clone(), - ), - &mut index_tasks, - index_cancel.clone(), - ) - .await; - - let go_backend = init_backend( - GoBackend::new( - backends.go.clone(), - source.clone(), - storage.clone(), - network.clone(), - ), - &mut index_tasks, - index_cancel.clone(), - ) - .await; - - let web_controller = Arc::new(WebController::new(zig_backend.clone(), go_backend.clone())); - let mut app = axum::Router::new().merge(web_controller.router()); - - if let Some(ref backend) = zig_backend { - let ctrl = Arc::new(BackendController::new( - backend.clone(), - storage.clone(), - network.clone(), - )); - app = app.nest("/zig", ctrl.router()); - } - - if let Some(ref backend) = go_backend { - let ctrl = Arc::new(BackendController::new( - backend.clone(), - storage.clone(), - network.clone(), - )); - app = app.nest("/go", ctrl.router()); - } - - let app = app - // Opt-in layers - .layer(tower_http::compression::CompressionLayer::new()) - // request limits - .layer(HostValidationLayer) - .layer(tower_http::limit::RequestBodyLimitLayer::new( - config.server().max_body_size.as_u64() as usize, - )) - .layer(tower_http::timeout::TimeoutLayer::with_status_code( - http::StatusCode::REQUEST_TIMEOUT, - config.server().request_timeout, - )) - // logging - .layer(trace_layer) - // rate-limits - .layer(tower_governor::GovernorLayer::new(Arc::new( - governor_config, - ))) - .layer(tower::limit::ConcurrencyLimitLayer::new( - config.server().max_concurrent_requests, - )) - // global headers - .layer(tower_http::request_id::PropagateRequestIdLayer::new( - REQUEST_ID_HEADER.clone(), - )) - .layer(tower_http::request_id::SetRequestIdLayer::new( - REQUEST_ID_HEADER.clone(), - tower_http::request_id::MakeRequestUuid, - )) - .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( - http::header::SERVER, - http::HeaderValue::from_static(concat!("recluse/", env!("CARGO_PKG_VERSION"))), - )); - - let mut tasks = tokio::task::JoinSet::new(); - let handle = Handle::new(); - - for listener_config in config.listeners() { - let std_listener = TcpListener::bind(listener_config.addr).unwrap(); - std_listener.set_nonblocking(true).unwrap(); - - let addr = std_listener.local_addr().unwrap(); - - let app = app - .clone() - .layer(axum::Extension(ListenerInfo { - addr, - hosts: listener_config.hostnames.clone(), - })) - .into_make_service_with_connect_info::(); - - let handle = handle.clone(); - - let tls_enabled = - if let (Some(crt), Some(key)) = (&listener_config.tls_crt, &listener_config.tls_key) { - let rustls_config = RustlsConfig::from_pem_file(crt, key) - .await - .expect("failed to load TLS config"); - - tasks.spawn(async move { - let server = axum_server::from_tcp_rustls(std_listener, rustls_config).unwrap(); - server.handle(handle).serve(app).await.unwrap(); - }); - - true - } else { - tasks.spawn(async move { - let server = axum_server::from_tcp(std_listener).unwrap(); - server.handle(handle).serve(app).await.unwrap(); - }); - - false - }; - - info!( - "listening {} on {} (hostnames: {})", - if tls_enabled { "HTTPS" } else { "HTTP" }, - addr, - if listener_config.hostnames.is_empty() { - "*".to_string() - } else { - listener_config.hostnames.join(", ") - }, - ); - } - - let mut watchdog_ticker = tokio::time::interval(std::time::Duration::from_secs(60)); - - #[cfg(target_os = "linux")] - if sd_notify::booted().unwrap_or(false) { - sd_notify::notify(false, &[NotifyState::Ready]).ok(); - - let mut usec = 0u64; - (sd_notify::watchdog_enabled(true, &mut usec) && usec > 0).then(|| { - let interval = std::time::Duration::from_micros(usec) / 2; - info!( - interval_ms = interval.as_millis() as u64, - "watchdog enabled" - ); - watchdog_ticker = tokio::time::interval(interval); - }); - }; - - #[cfg(unix)] - let mut sigint = signal::unix::signal(signal::unix::SignalKind::interrupt()) - .expect("failed to install signal handler"); - #[cfg(windows)] - let mut sigint = signal::windows::signal(signal::windows::SignalKind::interrupt()) - .expect("failed to install signal handler"); - - #[cfg(unix)] - let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to install signal handler"); - - loop { - let watchdog = watchdog_ticker.tick(); - - #[cfg(unix)] - let sigterm = sigterm.recv(); - #[cfg(not(unix))] - let sigterm = std::future::pending::<()>(); - - tokio::select! { - _ = sigint.recv() => { - info!("received SIGINT, shutting down"); - break; - }, - _ = sigterm => { - info!("received SIGTERM, shutting down"); - break; - }, - _ = watchdog => { - trace!("server is alive"); - - #[cfg(target_os = "linux")] - sd_notify::notify(false, &[NotifyState::Watchdog]).ok(); - }, - result = tasks.join_next() => { - match result { - Some(Ok(())) => error!("listener exited unexpectedly, shutting down"), - Some(Err(e)) => error!("listener failed: {e}, shutting down"), - None => { - error!("no listeners running"); - return; - } - } - break; - }, - } - } - - #[cfg(target_os = "linux")] - sd_notify::notify(false, &[NotifyState::Stopping]).ok(); - - index_cancel.cancel(); - handle.graceful_shutdown(None); - - // Wait for all tasks to finish with timeout - let shutdown_result = tokio::time::timeout(config.server().shutdown_timeout, async { - while let Some(result) = tasks.join_next().await { - if let Err(e) = result { - error!("listener task failed: {e}"); - } - } - while let Some(result) = index_tasks.join_next().await { - if let Err(e) = result { - error!("index task failed: {e}"); - } - } - }) - .await; - - if shutdown_result.is_err() { - error!( - "shutdown timeout after {:?}, aborting remaining tasks", - config.server().shutdown_timeout, - ); - tasks.abort_all(); - index_tasks.abort_all(); - } else { - info!("shutdown complete"); - } - - telemetry.shutdown(); -} - -/// Layer that validates the Host header against configured hostnames. -#[derive(Clone)] -struct HostValidationLayer; -impl tower::Layer for HostValidationLayer { - type Service = HostValidationService; - - fn layer(&self, inner: S) -> Self::Service { - HostValidationService { inner } - } -} - -#[derive(Clone)] -struct HostValidationService { - inner: S, -} -impl tower::Service> for HostValidationService -where - S: tower::Service, Response = Response> + Clone + Send + 'static, - S::Future: Send, -{ - type Response = S::Response; - type Error = S::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, req: Request) -> Self::Future { - let interface = req.extensions().get::().cloned(); - - if let Some(iface) = interface - && !iface.hosts.is_empty() - { - let is_valid = extract_host(&req) - .map(|h| { - iface - .hosts - .iter() - .any(|allowed| allowed.eq_ignore_ascii_case(&h)) - }) - .unwrap_or(false); - - if !is_valid { - return Box::pin(async move { - Ok(Response::builder() - .status(http::StatusCode::MISDIRECTED_REQUEST) - .body(Body::empty()) - .unwrap()) - }); - } - } - - let clone = self.inner.clone(); - let mut inner = std::mem::replace(&mut self.inner, clone); - Box::pin(async move { inner.call(req).await }) - } -} - -fn extract_host(req: &Request) -> Option { - // HTTP/1.1 uses HOST header, HTTP/2 uses :authority (available via URI) - let raw = if let Some(host) = req.headers().get(http::header::HOST) { - host.to_str().ok().map(|raw| { - if let Some((host, port)) = raw.rsplit_once(':') - && port.parse::().is_ok() - && (host.ends_with(']') || !host.contains('[')) - { - host - } else { - raw - } - }) - } else { - req.uri().host() - }; - - raw.and_then(|raw| url::Host::parse(raw).ok()) - .map(|h| h.to_string().trim_end_matches('.').to_string()) -} diff --git a/crates/recluse/src/proxy.rs b/crates/recluse/src/proxy.rs deleted file mode 100644 index 16a322b..0000000 --- a/crates/recluse/src/proxy.rs +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -use bytes::Bytes; -use http_body_util::{BodyExt, Empty}; -use hyper::{Request, http}; -use hyper_tls::HttpsConnector; -use hyper_util::client::legacy::{Client, connect::HttpConnector}; -use hyper_util::rt::TokioExecutor; -use tower::ServiceExt; -use tower_http::follow_redirect::FollowRedirect; -use url::Url; - -use repos::{BackendError, BackendNetwork}; - -#[derive(Clone)] -pub struct DownloadRequest { - pub url: Url, -} - -#[derive(Clone)] -pub struct File { - pub bytes: Bytes, -} - -pub struct ProxyService { - client: FollowRedirect, Empty>>, -} - -impl ProxyService { - pub fn new() -> Self { - let https = HttpsConnector::new(); - let client = Client::builder(TokioExecutor::new()).build(https); - Self { - client: FollowRedirect::new(client), - } - } - - pub async fn fetch(&self, request: DownloadRequest) -> Result { - let request = Request::builder() - .method(http::Method::GET) - .uri(request.url.as_str()) - .header(http::header::USER_AGENT, "recluse/0.1") - .body(Empty::::new()) - .unwrap(); - - let response = self - .client - .clone() - .oneshot(request) - .await - .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)?; - - let (parts, body) = response.into_parts(); - let status = parts.status; - if !status.is_success() { - return Err(status); - } - - let bytes = body - .collect() - .await - .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)? - .to_bytes(); - - Ok(File { bytes }) - } -} - -#[async_trait::async_trait] -impl BackendNetwork for ProxyService { - async fn http_get(&self, url: &url::Url) -> Result { - self.fetch(DownloadRequest { url: url.clone() }) - .await - .map(|f| f.bytes) - .map_err(|e| BackendError::Network(e.to_string())) - } -} diff --git a/crates/recluse/src/storage.rs b/crates/recluse/src/storage.rs deleted file mode 100644 index bc751a7..0000000 --- a/crates/recluse/src/storage.rs +++ /dev/null @@ -1,1203 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -// This class stores uploaded files and associated metadata. -// Files are immutable but can be deleted. -// -// A unique file is defined by a `scope` and a `filename`. -// The filename can be any valid UTF-8 string up to 1KB -// in size and does not have to be a valid filesystem name. -// -// The list of files and their metadata are stored in a single -// SQLite database table. Small files are stored in a BLOB column -// in SQLite; for larger files, a checksum is stored in the database, -// and the file itself is stored on disk. -// -// On disk new files are written in two steps: -// - the file is written to a temporary file in the same directory; -// - a transaction is opened and a new file entry is created -// - temp file is renamed to the final filename -// - the transaction is committed. -// -// This scheme guarantees that if a record exists in the table, the file is written. -// However if the server crashes after rename but before commit, an orphan file will remain on the disk. -// To combat this, when the server starts, we check that there is an entry -// in the database for each file on the disk, and we also delete all temporary (non-renamed) files. - -use std::path::{Path, PathBuf}; -use std::sync; - -use bytes::Bytes; -use crc32fast::Hasher as Crc32Hasher; -use sqlx::encode::{Encode, IsNull}; -use sqlx::error::BoxDynError; -use sqlx::{FromRow, Pool, Sqlite, query, query_as, query_scalar, sqlite}; -use thiserror::Error; -use tokio::fs; -use tokio::io::AsyncWriteExt; -use tracing::{debug, error, instrument, warn}; -use uuid::Uuid; - -use crate::config::ConfigService; -use repos::{BackendError, BackendStorage, RawRelease, RawReleaseFile, Release}; - -const SQLITE_POOL_SIZE: u32 = 16; -const INLINE_THRESHOLD: usize = 256 * 1024; // 256 KB - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Id(pub Uuid); - -impl std::ops::Deref for Id { - type Target = Uuid; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl sqlx::Type for Id { - fn type_info() -> sqlite::SqliteTypeInfo { - as sqlx::Type>::type_info() - } - - fn compatible(ty: &sqlite::SqliteTypeInfo) -> bool { - as sqlx::Type>::compatible(ty) - } -} - -impl<'r> sqlx::decode::Decode<'r, Sqlite> for Id { - fn decode( - value: sqlite::SqliteValueRef<'r>, - ) -> Result> { - let value: Vec = as sqlx::decode::Decode>::decode(value)?; - let uuid = Uuid::from_slice(&value)?; - Ok(Id(uuid)) - } -} - -impl<'q> Encode<'q, Sqlite> for Id { - fn encode_by_ref( - &self, - buf: &mut ::ArgumentBuffer<'q>, - ) -> Result { - let value = self.0.as_bytes().to_vec(); - as Encode>::encode(value, buf) - } -} - -#[derive(Debug, Clone)] -pub struct Blob(pub Bytes); - -impl std::ops::Deref for Blob { - type Target = Bytes; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl sqlx::Type for Blob { - fn type_info() -> sqlite::SqliteTypeInfo { - as sqlx::Type>::type_info() // BLOB - } - - fn compatible(ty: &sqlite::SqliteTypeInfo) -> bool { - as sqlx::Type>::compatible(ty) - } -} - -impl<'r> sqlx::decode::Decode<'r, Sqlite> for Blob { - fn decode( - value: sqlite::SqliteValueRef<'r>, - ) -> Result> { - let slice: &'r [u8] = <&'r [u8] as sqlx::decode::Decode>::decode(value)?; - Ok(Blob(Bytes::copy_from_slice(slice))) - } -} - -#[derive(Debug, Error)] -pub enum StorageError { - #[error("file already exists: {0}/{1}")] - AlreadyExists(String, String), - - #[error("blob integrity check failed")] - IntegrityError, - - #[error("blob file missing on disk: {0}")] - BlobNotFound(PathBuf), - - #[error("failed to connect index db: {0}")] - DbError(#[from] sqlx::Error), - - #[error("io error: {0}")] - IoError(#[from] std::io::Error), -} - -/// Cached file entry (datafiles table) -#[allow(unused)] -#[derive(Debug, Clone, FromRow)] -pub struct Object { - pub id: Id, - pub scope: String, - pub created_at: chrono::DateTime, - - pub file_name: String, - pub file_size: i64, - pub file_bytes: Blob, - pub inlined: bool, -} - -impl Object { - // You cannot change this ID, this will desynchronize the records in the database and the files on the disk. - const UUID_ROOT_NAMESPACE: Uuid = Uuid::from_bytes([ - 0x8b, 0x06, 0x3c, 0x4c, 0x6b, 0x5c, 0x4a, 0x8b, 0x92, 0x8f, 0x75, 0x8b, 0x0e, 0x63, 0xc3, - 0x5d, - ]); - - fn uuid(scope: &str, file: &str) -> Id { - let scope_ns = Uuid::new_v5(&Object::UUID_ROOT_NAMESPACE, scope.as_bytes()); - Id(Uuid::new_v5(&scope_ns, file.as_bytes())) - } - - fn hash(scope: &str, file: &str, blob: &[u8]) -> Vec { - let mut hasher = Crc32Hasher::new(); - - hasher.update(b"/"); - hasher.update(scope.as_bytes()); - - hasher.update(b"/"); - hasher.update(file.as_bytes()); - - hasher.update(b"/"); - hasher.update(blob); - hasher.update(b"\0"); - - hasher.finalize().to_be_bytes().to_vec() - } -} - -/// Builder for file query SQL -pub struct FileQuery<'a> { - backend: &'a str, - version: Option<&'a str>, - filename: Option<&'a str>, - meta_null: Option<&'a str>, -} - -impl<'a> FileQuery<'a> { - pub fn new(backend: &'a str) -> Self { - Self { - backend, - version: None, - filename: None, - meta_null: None, - } - } - - pub fn version(mut self, version: &'a str) -> Self { - self.version = Some(version); - self - } - - pub fn filename(mut self, filename: &'a str) -> Self { - self.filename = Some(filename); - self - } - - pub fn where_meta_null(mut self, field: &'a str) -> Self { - self.meta_null = Some(field); - self - } - - pub fn build_sql(&self) -> String { - let mut sql = String::from( - "SELECT backend, version, filename, checksum, size, os, arch, meta FROM files WHERE backend = ?1", - ); - let mut param_idx = 1; - - if self.version.is_some() { - param_idx += 1; - sql.push_str(&format!(" AND version = ?{}", param_idx)); - } - if self.filename.is_some() { - param_idx += 1; - sql.push_str(&format!(" AND filename = ?{}", param_idx)); - } - if let Some(field) = self.meta_null { - sql.push_str(&format!( - " AND (meta IS NULL OR json_extract(meta, '$.{}') IS NULL)", - field - )); - } - - sql - } -} - -struct FileSystem { - objects: PathBuf, - database: PathBuf, -} - -impl FileSystem { - fn new(root: &Path) -> Self { - Self { - objects: root.join("objects"), - database: root.join("index.sqlite"), - } - } - - fn database(&self) -> &Path { - &self.database - } - - fn objects_root(&self) -> &Path { - &self.objects - } - - fn object(&self, scope: &str, file: &str) -> PathBuf { - let id = Object::uuid(scope, file); - let hash_hex = hex::encode(id.0.as_bytes()); - self.objects_root() - .join(&hash_hex[0..2]) - .join(&hash_hex[2..4]) - .join(&hash_hex) - } - - async fn objects_walk(&self, mut f: F) -> Result<(), StorageError> - where - F: FnMut(PathBuf) -> Fut, - Fut: std::future::Future>, - { - if !self.objects.exists() { - return Ok(()); - } - - let mut stack = vec![self.objects.to_path_buf()]; - while let Some(dir) = stack.pop() { - let mut entries = fs::read_dir(&dir).await?; - while let Some(entry) = entries.next_entry().await? { - let path = entry.path(); - let file_type = entry.file_type().await?; - - if file_type.is_dir() { - stack.push(path); - } else if file_type.is_file() { - f(path).await?; - } - } - } - - Ok(()) - } -} - -pub struct StorageService { - blobfs: FileSystem, - sqlite: Pool, -} - -impl StorageService { - pub async fn new(config: sync::Arc) -> Result { - let blobfs = FileSystem::new(config.dirname()); - - let connection = format!("sqlite:{}?mode=rwc", blobfs.database().to_str().unwrap()); - let sqlite: Pool = sqlite::SqlitePoolOptions::new() - .max_connections(SQLITE_POOL_SIZE) - .after_connect(|conn, _meta| { - Box::pin(async move { - // Connection-specific PRAGMAs (must be set on each connection) - sqlx::query("PRAGMA foreign_keys = ON;") - .execute(&mut *conn) - .await?; - sqlx::query("PRAGMA busy_timeout = 5000;") - .execute(&mut *conn) - .await?; - Ok(()) - }) - }) - .connect(&connection) - .await?; - - // WAL mode is database-wide and persists, only needs to be set once - query("PRAGMA journal_mode = WAL;").execute(&sqlite).await?; - - let storage = Self { sqlite, blobfs }; - storage.migrations().await?; - storage.doctor().await?; - - Ok(storage) - } - - /// Synchronously traverses the tree and removes temporary files. - /// Must run before the application starts. - async fn doctor(&self) -> Result<(), StorageError> { - let result = self.blobfs.objects_walk(|path| async move { - let name = match path.file_name().and_then(|name| name.to_str()) { - Some(name) => name.to_string(), - None => { - warn!(path = %path.display(), "cleanup: invalid blob name"); - let _ = fs::remove_file(path).await; - return Ok(()); - } - }; - - if name.ends_with(".part") { - warn!(path = %path.display(), "cleanup: removing temp file"); - let _ = fs::remove_file(path).await; - return Ok(()); - } - - if name.len() != 32 { - warn!(path = %path.display(), "cleanup: invalid blob name length"); - let _ = fs::remove_file(path).await; - return Ok(()); - } - - let id_bytes = match hex::decode(&name) { - Ok(bytes) => bytes, - Err(_) => { - warn!(path = %path.display(), "cleanup: invalid blob name hex"); - let _ = fs::remove_file(path).await; - return Ok(()); - } - }; - - let id = match Uuid::from_slice(&id_bytes) { - Ok(uuid) => Id(uuid), - Err(_) => { - warn!(path = %path.display(), "cleanup: invalid blob uuid"); - let _ = fs::remove_file(path).await; - return Ok(()); - } - }; - - let exists: Option = query_scalar("SELECT 1 FROM datafiles WHERE id = ?1") - .bind(id) - .fetch_optional(&self.sqlite) - .await?; - - if exists.is_none() { - warn!(path = %path.display(), "cleanup: removing orphan blob"); - let _ = fs::remove_file(path).await; - } - - Ok(()) - }); - result.await?; - - Ok(()) - } - - async fn migrations(&self) -> Result<(), StorageError> { - // Object storage table - query( - "CREATE TABLE IF NOT EXISTS datafiles( - id BLOB PRIMARY KEY CHECK (length(id) = 16), - scope TEXT NOT NULL, - created_at TEXT DEFAULT (datetime('now')), - file_name TEXT NOT NULL, - file_size INTEGER NOT NULL, - file_bytes BLOB, - inlined INTEGER NOT NULL, - UNIQUE (scope, file_name) - ) STRICT", - ) - .execute(&self.sqlite) - .await?; - - // Drop old backend-specific tables (data will be re-fetched from upstream) - query("DROP TABLE IF EXISTS go_files") - .execute(&self.sqlite) - .await?; - query("DROP TABLE IF EXISTS go_versions") - .execute(&self.sqlite) - .await?; - query("DROP TABLE IF EXISTS zig_files") - .execute(&self.sqlite) - .await?; - query("DROP TABLE IF EXISTS zig_versions") - .execute(&self.sqlite) - .await?; - - // Unified versions table - query( - "CREATE TABLE IF NOT EXISTS versions ( - backend TEXT NOT NULL, - version TEXT NOT NULL, - sort_key INTEGER NOT NULL, - meta BLOB CHECK (meta IS NULL OR (json_valid(meta) AND json_type(meta) = 'object')), - PRIMARY KEY (backend, version) - ) STRICT", - ) - .execute(&self.sqlite) - .await?; - - // Unified files table - query( - "CREATE TABLE IF NOT EXISTS files ( - backend TEXT NOT NULL, - version TEXT NOT NULL, - filename TEXT NOT NULL, - checksum TEXT NOT NULL, - size INTEGER NOT NULL, - os TEXT, - arch TEXT, - meta BLOB CHECK (meta IS NULL OR (json_valid(meta) AND json_type(meta) = 'object')), - PRIMARY KEY (backend, version, filename), - FOREIGN KEY (backend, version) REFERENCES versions(backend, version) - ) STRICT", - ) - .execute(&self.sqlite) - .await?; - - // Index for fast filename lookups - query("CREATE INDEX IF NOT EXISTS idx_files_backend_filename ON files(backend, filename)") - .execute(&self.sqlite) - .await?; - - Ok(()) - } - - #[instrument(skip(self))] - pub async fn get(&self, scope: &str, filename: &str) -> Result, StorageError> { - let file: Option = - query_as("SELECT * FROM datafiles WHERE scope = ?1 AND file_name = ?2") - .bind(scope) - .bind(filename) - .fetch_optional(&self.sqlite) - .await?; - - match file { - None => { - debug!("get: file not found"); - Ok(None) - } - Some(mut file) if !file.inlined => { - let obj = self.blobfs.object(scope, filename); - let bytes = match fs::read(&obj).await { - Ok(b) => Bytes::from(b), - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { - return Err(StorageError::BlobNotFound(obj)); - } - Err(e) => return Err(e.into()), - }; - - let hash = Object::hash(scope, filename, &bytes); - if hash != file.file_bytes.to_vec() { - return Err(StorageError::IntegrityError); - } - - file.file_bytes = Blob(bytes); - debug!(size = file.file_size, "get: loaded from disk"); - Ok(Some(file)) - } - Some(file) => { - debug!(size = file.file_size, "get: loaded inline"); - Ok(Some(file)) - } - } - } - - #[instrument(skip(self, bytes), fields(size = bytes.len()))] - pub async fn put( - &self, - scope: &str, - filename: &str, - bytes: &Bytes, - ) -> Result<(), StorageError> { - let inlined = bytes.len() <= INLINE_THRESHOLD; - - let obj = self.blobfs.object(scope, filename); - let tmp = obj.with_extension(format!("{}.part", Uuid::new_v4())); - - // write temp file - if !inlined { - if let Some(parent) = obj.parent() { - fs::create_dir_all(parent).await?; - } - - let mut file = fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(&tmp) - .await?; - - file.write_all(bytes).await?; - file.sync_all().await?; - } - - // bytes for small files or hash for large ones - let payload: Vec = if inlined { - bytes.to_vec() - } else { - Object::hash(scope, filename, bytes) - }; - - let result = async { - let mut tx = self.sqlite.begin().await?; - let id = Object::uuid(scope, filename); - - let result = query( - " - INSERT INTO datafiles ( - id, scope, file_name, file_size, file_bytes, inlined - ) VALUES ( - ?1, ?2, ?3, ?4, ?5, ?6 - ); - ", - ) - .bind(id) - .bind(scope) - .bind(filename) - .bind(bytes.len() as i64) - .bind(&payload) - .bind(inlined) - .execute(tx.as_mut()) - .await; - - match result { - Ok(_) => { - if !inlined { - fs::rename(&tmp, &obj).await?; - if let Some(parent) = obj.parent() { - let dir = fs::File::open(parent).await?; - dir.sync_all().await?; - } - } - - tx.commit().await?; - debug!("put: a new file has been commited"); - Ok(()) - } - Err(sqlx::Error::Database(ref db_err)) if db_err.is_unique_violation() => { - let existing: Option = - query_as("SELECT * FROM datafiles WHERE scope = ?1 AND file_name = ?2") - .bind(scope) - .bind(filename) - .fetch_optional(tx.as_mut()) - .await?; - - match existing { - Some(file) if file.file_bytes.to_vec() == payload => { - if !inlined { - let _ = fs::remove_file(&tmp).await; - tx.rollback().await?; - } - - debug!("put: identical file already exists"); - return Ok(()); - } - Some(_) => { - warn!("put: file already exists with different content"); - } - None => { - // unique_violation but row doesn't exist - shouldn't happen - warn!("corrupted! 'is_unique_violation' received, but data cannot be selected"); - } - } - - Err(StorageError::AlreadyExists( - scope.to_string(), - filename.to_string(), - )) - } - Err(e) => Err(e.into()), - } - }.await; - - if result.is_err() && !inlined { - let _ = fs::remove_file(&tmp).await; - } - - result - } - - /// Insert or update releases with their files in a single transaction. - pub async fn insert_releases( - &self, - releases: &[(RawRelease, Vec)], - ) -> Result<(), StorageError> { - for (release, files) in releases { - let mut tx = match self.sqlite.begin().await { - Ok(tx) => tx, - Err(e) => { - error!( - backend = release.backend, - version = release.version, - "failed to start transaction: {e}" - ); - return Err(e.into()); - } - }; - - let meta_json = release - .meta - .as_ref() - .map(|m| serde_json::to_vec(m).unwrap()); - - let result = async { - // Insert/update version - query( - "INSERT INTO versions (backend, version, sort_key, meta) - VALUES (?1, ?2, ?3, ?4) - ON CONFLICT(backend, version) DO UPDATE SET - sort_key = excluded.sort_key, - meta = excluded.meta - WHERE sort_key IS NOT excluded.sort_key OR meta IS NOT excluded.meta", - ) - .bind(&release.backend) - .bind(&release.version) - .bind(release.sort_key) - .bind(meta_json.as_deref()) - .execute(&mut *tx) - .await?; - - // Insert/update files - for file in files { - let changed = - Self::insert_file(&mut tx, &release.backend, &release.version, file) - .await?; - if changed { - warn!( - backend = release.backend, - version = release.version, - filename = file.filename, - "index file changed" - ); - } - } - - Ok::<(), StorageError>(()) - } - .await; - - match result { - Ok(()) => { - if let Err(e) = tx.commit().await { - error!( - backend = release.backend, - version = release.version, - "failed to commit release: {e}" - ); - } - } - Err(e) => { - error!( - backend = release.backend, - version = release.version, - "failed to store release, skipping: {e}" - ); - let _ = tx.rollback().await; - } - } - } - - Ok(()) - } - - /// Insert or update a single file within a transaction. - /// Returns `true` if an existing file was modified. - async fn insert_file( - tx: &mut sqlx::Transaction<'_, Sqlite>, - backend: &str, - version: &str, - file: &RawReleaseFile, - ) -> Result { - let meta_json = file.meta.as_ref().map(|m| serde_json::to_vec(m).unwrap()); - let os_str = file.os.as_ref().map(AsRef::as_ref); - let arch_str = file.arch.as_ref().map(AsRef::as_ref); - - let existed: Option<(i32,)> = - query_as("SELECT 1 FROM files WHERE backend = ?1 AND version = ?2 AND filename = ?3") - .bind(backend) - .bind(version) - .bind(&file.filename) - .fetch_optional(&mut **tx) - .await?; - - let changed: Option<(i32,)> = query_as( - "INSERT INTO files (backend, version, filename, checksum, size, os, arch, meta) - VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) - ON CONFLICT(backend, version, filename) DO UPDATE SET - checksum = excluded.checksum, - size = excluded.size, - os = excluded.os, - arch = excluded.arch - WHERE checksum IS NOT excluded.checksum - OR size IS NOT excluded.size - OR os IS NOT excluded.os - OR arch IS NOT excluded.arch - RETURNING 1", - ) - .bind(backend) - .bind(version) - .bind(&file.filename) - .bind(&file.checksum) - .bind(file.size) - .bind(os_str) - .bind(arch_str) - .bind(meta_json.as_deref()) - .fetch_optional(&mut **tx) - .await?; - - Ok(existed.is_some() && changed.is_some()) - } - - /// Execute a file query. - pub async fn query_files_filtered( - &self, - q: FileQuery<'_>, - ) -> Result, StorageError> { - let sql = q.build_sql(); - - let mut query = sqlx::query_as::< - _, - ( - String, - String, - String, - String, - i64, - Option, - Option, - Option>, - ), - >(&sql) - .bind(q.backend); - - if let Some(v) = q.version { - query = query.bind(v); - } - if let Some(f) = q.filename { - query = query.bind(f); - } - - let rows = query.fetch_all(&self.sqlite).await?; - - rows.into_iter() - .map( - |(backend, version, filename, checksum, size, os_str, arch_str, meta_bytes)| { - let meta = meta_bytes - .map(|b| serde_json::from_slice(&b)) - .transpose() - .map_err(|e| StorageError::DbError(sqlx::Error::Decode(Box::new(e))))?; - - Ok(RawReleaseFile { - backend, - version, - filename, - checksum, - size, - os: os_str.and_then(|s| s.parse().ok()), - arch: arch_str.and_then(|s| s.parse().ok()), - meta, - }) - }, - ) - .collect() - } - - /// Query all versions for a backend, ordered by sort_key. - pub async fn query_releases(&self, backend: &str) -> Result, StorageError> { - let rows = query_as::<_, (String, String, i64, Option>)>( - "SELECT backend, version, sort_key, meta FROM versions WHERE backend = ?1 ORDER BY sort_key", - ) - .bind(backend) - .fetch_all(&self.sqlite) - .await?; - - rows.into_iter() - .map(|(backend, version, sort_key, meta_bytes)| { - let meta = meta_bytes - .map(|b| serde_json::from_slice(&b)) - .transpose() - .map_err(|e| StorageError::DbError(sqlx::Error::Decode(Box::new(e))))?; - - Ok(Release { - backend, - version, - sort_key, - meta, - }) - }) - .collect() - } - - /// Update file metadata (e.g. after fetching a signature). - pub async fn update_file_meta(&self, file: &RawReleaseFile) -> Result { - let meta_json = file.meta.as_ref().map(|m| serde_json::to_vec(m).unwrap()); - - let result = query( - "UPDATE files SET meta = ?1 - WHERE backend = ?2 AND version = ?3 AND filename = ?4", - ) - .bind(meta_json.as_deref()) - .bind(&file.backend) - .bind(&file.version) - .bind(&file.filename) - .execute(&self.sqlite) - .await?; - - Ok(result.rows_affected() > 0) - } -} - -#[async_trait::async_trait] -impl BackendStorage for StorageService { - async fn insert_releases( - &self, - releases: &[(RawRelease, Vec)], - ) -> Result<(), BackendError> { - StorageService::insert_releases(self, releases) - .await - .map_err(|e| BackendError::Storage(e.to_string())) - } - - async fn query_releases(&self, backend: &str) -> Result, BackendError> { - StorageService::query_releases(self, backend) - .await - .map_err(|e| BackendError::Storage(e.to_string())) - } - - async fn query_files( - &self, - backend: &str, - version: Option<&str>, - filename: Option<&str>, - meta_null_field: Option<&str>, - ) -> Result, BackendError> { - let mut q = FileQuery::new(backend); - if let Some(v) = version { - q = q.version(v); - } - if let Some(f) = filename { - q = q.filename(f); - } - if let Some(field) = meta_null_field { - q = q.where_meta_null(field); - } - self.query_files_filtered(q) - .await - .map_err(|e| BackendError::Storage(e.to_string())) - } - - async fn update_file_meta(&self, file: &RawReleaseFile) -> Result { - StorageService::update_file_meta(self, file) - .await - .map_err(|e| BackendError::Storage(e.to_string())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::TempDir; - - const BIG_FILE_SIZE: usize = INLINE_THRESHOLD * 64; - - async fn create_storage_at(path: &Path) -> StorageService { - let config = sync::Arc::new(ConfigService::for_test(path.to_path_buf())); - StorageService::new(config).await.unwrap() - } - - async fn create_test_storage() -> (StorageService, TempDir) { - let tmp = TempDir::new().unwrap(); - let storage = create_storage_at(tmp.path()).await; - (storage, tmp) - } - - /// Count files in a directory recursively - fn count_files(dir: &Path, ext: Option<&str>) -> usize { - let mut count = 0; - if let Ok(entries) = std::fs::read_dir(dir) { - for entry in entries.flatten() { - let path = entry.path(); - if path.is_dir() { - count += count_files(&path, ext); - } else if let Some(extension) = ext { - if path.extension().is_some_and(|ext| ext == extension) { - count += 1; - } - } else { - count += 1; - } - } - } - count - } - - /// Recursively delete or corrupt all files in a directory - fn damage_blobs(dir: &Path, corrupt: bool) { - if let Ok(entries) = std::fs::read_dir(dir) { - for entry in entries.flatten() { - let path = entry.path(); - if path.is_dir() { - damage_blobs(&path, corrupt); - } else if corrupt { - if let Ok(meta) = path.metadata() { - let _ = std::fs::write(&path, vec![0x00; meta.len() as usize]); - } - } else { - let _ = std::fs::remove_file(&path); - } - } - } - } - - #[tokio::test] - async fn test_write_read_small_file() { - let (storage, _tmp) = create_test_storage().await; - - // 1 KB file - should be stored inline - let data = Bytes::from(vec![0xAB; 1024]); - storage.put("test-scope", "small.bin", &data).await.unwrap(); - - let file = storage.get("test-scope", "small.bin").await.unwrap(); - assert!(file.is_some()); - - let file = file.unwrap(); - assert!(file.inlined); - assert_eq!(file.file_bytes.0, data); - assert_eq!(file.file_size, 1024); - } - - #[tokio::test] - async fn test_write_read_large_file() { - let (storage, tmp) = create_test_storage().await; - - // Large file - should be stored on disk - let data = Bytes::from(vec![0xCD; BIG_FILE_SIZE]); - storage.put("test-scope", "large.bin", &data).await.unwrap(); - - let file = storage.get("test-scope", "large.bin").await.unwrap(); - assert!(file.is_some()); - - let file = file.unwrap(); - assert!(!file.inlined); - assert_eq!(file.file_bytes.0, data); - assert_eq!(file.file_size, (BIG_FILE_SIZE) as i64); - - // Verify file is stored on disk, not inline in SQLite - let db_size = std::fs::metadata(tmp.path().join("index.sqlite")) - .unwrap() - .len(); - assert!( - db_size < data.len() as u64, - "database ({db_size} bytes) should be smaller than file ({} bytes)", - data.len() - ); - } - - #[tokio::test] - async fn test_write_read_boundary_file() { - let (storage, _tmp) = create_test_storage().await; - - // Exactly 256 KB - should be stored inline (threshold is <=) - let data = Bytes::from(vec![0xEF; INLINE_THRESHOLD]); - storage - .put("test-scope", "boundary.bin", &data) - .await - .unwrap(); - - let file = storage.get("test-scope", "boundary.bin").await.unwrap(); - assert!(file.is_some()); - - let file = file.unwrap(); - assert!(file.inlined); - assert_eq!(file.file_bytes.0, data); - assert_eq!(file.file_size, INLINE_THRESHOLD as i64); - } - - #[tokio::test] - async fn test_read_nonexistent_file() { - let (storage, _tmp) = create_test_storage().await; - - let file = storage.get("test-scope", "nonexistent.bin").await.unwrap(); - assert!(file.is_none()); - } - - #[tokio::test] - async fn test_persistence_after_restart() { - let tmp = TempDir::new().unwrap(); - - let small_data = Bytes::from(vec![0x11; 1024]); - let large_data = Bytes::from(vec![0x22; BIG_FILE_SIZE]); - - // First "session": write files - { - let storage = create_storage_at(tmp.path()).await; - storage - .put("persist", "small.bin", &small_data) - .await - .unwrap(); - storage - .put("persist", "large.bin", &large_data) - .await - .unwrap(); - // storage is dropped here, simulating shutdown - } - - // Second "session": verify files persist - { - let storage = create_storage_at(tmp.path()).await; - - let small = storage.get("persist", "small.bin").await.unwrap(); - assert!(small.is_some()); - assert_eq!(small.unwrap().file_bytes.0, small_data); - - let large = storage.get("persist", "large.bin").await.unwrap(); - assert!(large.is_some()); - assert_eq!(large.unwrap().file_bytes.0, large_data); - } - } - - #[tokio::test] - async fn test_error_blob_not_found() { - let (storage, tmp) = create_test_storage().await; - - // Write a large file (stored on disk) - let data = Bytes::from(vec![0xAA; BIG_FILE_SIZE]); - storage - .put("test-scope", "to-delete.bin", &data) - .await - .unwrap(); - - // Delete all blob files from disk - damage_blobs(&tmp.path().join("objects"), false); - - // Reading should fail with BlobNotFound - let result = storage.get("test-scope", "to-delete.bin").await; - assert!(matches!(result, Err(StorageError::BlobNotFound(_)))); - } - - #[tokio::test] - async fn test_error_blob_corrupted() { - let (storage, tmp) = create_test_storage().await; - - // Write a large file (stored on disk) - let data = Bytes::from(vec![0xBB; BIG_FILE_SIZE]); - storage - .put("test-scope", "to-corrupt.bin", &data) - .await - .unwrap(); - - // Corrupt all blob files on disk - damage_blobs(&tmp.path().join("objects"), true); - - // Reading should fail with IntegrityError - let result = storage.get("test-scope", "to-corrupt.bin").await; - assert!(matches!(result, Err(StorageError::IntegrityError))); - } - - #[tokio::test] - async fn test_duplicate_put_same_content() { - let (storage, tmp) = create_test_storage().await; - - // Write a large file - let data = Bytes::from(vec![0xCC; BIG_FILE_SIZE]); - storage.put("test-scope", "dup.bin", &data).await.unwrap(); - - // Write the same file again with identical content - should succeed - let result = storage.put("test-scope", "dup.bin", &data).await; - assert!(result.is_ok()); - - // Verify no temp files left behind - assert_eq!(count_files(&tmp.path().join("objects"), Some("part")), 0); - - // Verify file is still readable with correct content - let file = storage.get("test-scope", "dup.bin").await.unwrap(); - assert!(file.is_some()); - assert_eq!(file.unwrap().file_bytes.0, data); - } - - #[tokio::test] - async fn test_duplicate_put_different_content() { - let (storage, tmp) = create_test_storage().await; - - // Write a large file - let data1 = Bytes::from(vec![0xDD; BIG_FILE_SIZE]); - storage.put("test-scope", "dup.bin", &data1).await.unwrap(); - - // Write the same filename with different content - should fail - let data2 = Bytes::from(vec![0xEE; BIG_FILE_SIZE]); - let result = storage.put("test-scope", "dup.bin", &data2).await; - assert!(matches!(result, Err(StorageError::AlreadyExists(_, _)))); - - // Verify no temp files left behind - assert_eq!(count_files(&tmp.path().join("objects"), Some("part")), 0); - - // Verify original file is still intact - let file = storage.get("test-scope", "dup.bin").await.unwrap(); - assert!(file.is_some()); - assert_eq!(file.unwrap().file_bytes.0, data1); - } - - #[tokio::test] - async fn test_doctor_removes_temp_files() { - let tmp = TempDir::new().unwrap(); - - // First session: write a large file - let data = Bytes::from(vec![0xAA; BIG_FILE_SIZE]); - { - let storage = create_storage_at(tmp.path()).await; - storage.put("test-scope", "valid.bin", &data).await.unwrap(); - } - - // Manually create temp files in the objects directory - let objects_dir = tmp.path().join("objects"); - std::fs::create_dir_all(objects_dir.join("ab/cd")).unwrap(); - std::fs::write(objects_dir.join("ab/cd/test.part"), b"temp1").unwrap(); - std::fs::write(objects_dir.join("ab/cd/another.12345.part"), b"temp2").unwrap(); - - assert_eq!(count_files(&objects_dir, Some("part")), 2); - - // Second session: doctor should clean up temp files - { - let storage = create_storage_at(tmp.path()).await; - - // Temp files should be gone - assert_eq!(count_files(&objects_dir, Some("part")), 0); - - // Valid file should still be readable - let file = storage.get("test-scope", "valid.bin").await.unwrap(); - assert!(file.is_some()); - assert_eq!(file.unwrap().file_bytes.0, data); - } - } - - #[tokio::test] - async fn test_doctor_removes_orphan_blobs() { - let tmp = TempDir::new().unwrap(); - - // First session: write a large file - let data = Bytes::from(vec![0xBB; BIG_FILE_SIZE]); - { - let storage = create_storage_at(tmp.path()).await; - storage.put("test-scope", "valid.bin", &data).await.unwrap(); - } - - // Count files before adding orphan - let objects_dir = tmp.path().join("objects"); - let files_before = count_files(&objects_dir, None); - assert_eq!(files_before, 1); // Only the valid blob - - // Manually create an orphan blob (valid hex name but no DB record) - std::fs::create_dir_all(objects_dir.join("de/ad")).unwrap(); - let orphan_name = "deadbeefdeadbeefdeadbeefdeadbeef"; // 32 hex chars - std::fs::write(objects_dir.join("de/ad").join(orphan_name), b"orphan").unwrap(); - - assert_eq!(count_files(&objects_dir, None), 2); - - // Second session: doctor should clean up orphan blob - { - let storage = create_storage_at(tmp.path()).await; - - // Only the valid file should remain - assert_eq!(count_files(&objects_dir, None), 1); - - // Valid file should still be readable - let file = storage.get("test-scope", "valid.bin").await.unwrap(); - assert!(file.is_some()); - assert_eq!(file.unwrap().file_bytes.0, data); - } - } -} diff --git a/crates/recluse/src/telemetry.rs b/crates/recluse/src/telemetry.rs deleted file mode 100644 index df39fa0..0000000 --- a/crates/recluse/src/telemetry.rs +++ /dev/null @@ -1,247 +0,0 @@ -// SPDX-FileCopyrightText: 2026 Nikolay Govorov -// SPDX-License-Identifier: AGPL-3.0-or-later - -use std::fmt::Debug; -use std::time::SystemTime; - -use opentelemetry::InstrumentationScope; -use opentelemetry::KeyValue; -use opentelemetry::logs::LogRecord as _; -use opentelemetry::trace::TracerProvider as _; -use opentelemetry_otlp::{ - LogExporter, Protocol, SpanExporter, WithExportConfig, WithHttpConfig, WithTonicConfig, -}; -use opentelemetry_sdk::Resource; -use opentelemetry_sdk::error::OTelSdkResult; -use opentelemetry_sdk::logs::{BatchLogProcessor, LogProcessor, SdkLogRecord, SdkLoggerProvider}; -use opentelemetry_sdk::trace::SdkTracerProvider; -use tonic::metadata::{MetadataKey, MetadataMap, MetadataValue}; -use tonic::transport::{Certificate, ClientTlsConfig, Identity}; -use tracing_subscriber::Layer; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; - -use crate::config::{LogLevel, OtelcolConfig, StdoutFormat, TelemetryConfig}; - -/// LogProcessor wrapper that sets timestamp from observed_timestamp. -/// -/// opentelemetry-appender-tracing intentionally doesn't set timestamp because -/// tracing doesn't provide it. This causes some backends to show epoch (1970-01-01). -/// See: https://github.com/open-telemetry/opentelemetry-rust/issues/1479 -#[derive(Debug)] -struct TimestampLogProcessor

(P); -impl LogProcessor for TimestampLogProcessor

{ - fn emit(&self, record: &mut SdkLogRecord, scope: &InstrumentationScope) { - if record.timestamp().is_none() { - let ts = record.observed_timestamp().unwrap_or_else(SystemTime::now); - record.set_timestamp(ts); - } - self.0.emit(record, scope); - } - - fn force_flush(&self) -> OTelSdkResult { - self.0.force_flush() - } - - fn shutdown(&self) -> OTelSdkResult { - self.0.shutdown() - } -} - -macro_rules! build_exporter { - ($Exporter:ident, $cfg:expr, $url:expr, $tls:expr, $path:expr) => { - if $cfg.endpoint.starts_with("http://") { - // with_endpoint() requires full path for HTTP protocol - $Exporter::builder() - .with_http() - .with_protocol(Protocol::HttpBinary) - .with_endpoint(format!("{}{}", $url, $path)) - .with_timeout($cfg.timeout) - .with_headers($cfg.headers.clone()) - .build() - .expect(concat!("failed to build ", stringify!($Exporter))) - } else if $cfg.endpoint.starts_with("grpc://") { - $Exporter::builder() - .with_tonic() - .with_endpoint($url) - .with_timeout($cfg.timeout) - .with_tls_config($tls) - .with_metadata(build_metadata($cfg)) - .build() - .expect(concat!("failed to build ", stringify!($Exporter))) - } else { - panic!( - "invalid OTLP endpoint: {}. Expected http:// or grpc://", - $cfg.endpoint - ) - } - }; -} - -fn build_metadata(cfg: &OtelcolConfig) -> MetadataMap { - let mut metadata = MetadataMap::new(); - for (key, value) in &cfg.headers { - if let (Ok(k), Ok(v)) = ( - key.parse::>(), - value.parse::>(), - ) { - metadata.insert(k, v); - } - } - metadata -} - -pub struct TelemetryService { - logger_provider: Option, - tracer_provider: Option, -} -impl Drop for TelemetryService { - fn drop(&mut self) { - self.shutdown(); - } -} -impl TelemetryService { - pub fn init(config: &TelemetryConfig, service_name: &str, service_version: &str) -> Self { - let env_filter = match std::env::var_os("RECLUSE_LOG") { - Some(val) => tracing_subscriber::EnvFilter::try_new(val.to_string_lossy()) - .expect("Invalid RECLUSE_LOG"), - None => tracing_subscriber::EnvFilter::new(Self::log_level_to_filter( - config.stdout.log_level, - )), - }; - - let mut tracer_provider = None; - let mut logger_provider = None; - - let stdout = config - .stdout - .enabled - .then(|| match config.stdout.log_format { - StdoutFormat::Json => tracing_subscriber::fmt::layer().json().boxed(), - StdoutFormat::Pretty => tracing_subscriber::fmt::layer().pretty().boxed(), - }); - - let (otel_logs, otel_traces) = match &config.otelcol { - Some(cfg) if cfg.enabled => { - let tls = Self::build_tls(cfg); - let url = Self::parse_endpoint(&cfg.endpoint); - let resource = Resource::builder() - .with_service_name(service_name.to_string()) - .with_attribute(KeyValue::new( - opentelemetry_semantic_conventions::attribute::SERVICE_VERSION, - service_version.to_string(), - )) - .build(); - - let logs = cfg.logs.then(|| { - let exporter = - build_exporter!(LogExporter, cfg, url.clone(), tls.clone(), "/v1/logs"); - let provider = SdkLoggerProvider::builder() - .with_resource(resource.clone()) - .with_log_processor(TimestampLogProcessor( - BatchLogProcessor::builder(exporter).build(), - )) - .build(); - let layer = - opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new( - &provider, - ) - .with_filter(Self::to_level_filter(cfg.log_level)); - logger_provider = Some(provider); - layer - }); - - let traces = cfg.traces.then(|| { - let provider = SdkTracerProvider::builder() - .with_resource(resource.clone()) - .with_batch_exporter(build_exporter!( - SpanExporter, - cfg, - url.clone(), - tls.clone(), - "/v1/traces" - )) - .build(); - let layer = tracing_opentelemetry::layer() - .with_tracer(provider.tracer(service_name.to_string())); - tracer_provider = Some(provider); - layer - }); - - (logs, traces) - } - _ => (None, None), - }; - - tracing_subscriber::registry() - .with(stdout) - .with(otel_logs) - .with(otel_traces) - .with(env_filter) - .init(); - - Self { - tracer_provider, - logger_provider, - } - } - - fn parse_endpoint(endpoint: &str) -> String { - if endpoint.starts_with("http://") { - endpoint.to_string() - } else if let Some(rest) = endpoint.strip_prefix("grpc://") { - format!("https://{rest}") - } else { - panic!("invalid OTLP endpoint: {endpoint}. Expected http:// or grpc://") - } - } - - fn build_tls(cfg: &OtelcolConfig) -> ClientTlsConfig { - let mut tls = ClientTlsConfig::new().with_native_roots(); - if let Some(path) = &cfg.tls_ca { - tls = tls.ca_certificate(Certificate::from_pem( - std::fs::read_to_string(path).expect("failed to read CA"), - )); - } - if let (Some(crt), Some(key)) = (&cfg.tls_crt, &cfg.tls_key) { - tls = tls.identity(Identity::from_pem( - std::fs::read_to_string(crt).expect("failed to read cert"), - std::fs::read_to_string(key).expect("failed to read key"), - )); - } - tls - } - - fn to_level_filter(level: LogLevel) -> tracing_subscriber::filter::LevelFilter { - match level { - LogLevel::Trace => tracing_subscriber::filter::LevelFilter::TRACE, - LogLevel::Debug => tracing_subscriber::filter::LevelFilter::DEBUG, - LogLevel::Info => tracing_subscriber::filter::LevelFilter::INFO, - LogLevel::Warning => tracing_subscriber::filter::LevelFilter::WARN, - LogLevel::Error => tracing_subscriber::filter::LevelFilter::ERROR, - } - } - - fn log_level_to_filter(level: LogLevel) -> &'static str { - match level { - LogLevel::Trace => "trace", - LogLevel::Debug => "debug", - LogLevel::Info => "info", - LogLevel::Warning => "warn", - LogLevel::Error => "error", - } - } - - pub fn shutdown(&mut self) { - if let Some(p) = self.tracer_provider.take() - && let Err(e) = p.shutdown() - { - tracing::error!("failed to shutdown tracer provider: {e}"); - } - if let Some(p) = self.logger_provider.take() - && let Err(e) = p.shutdown() - { - tracing::error!("failed to shutdown logger provider: {e}"); - } - } -} diff --git a/crates/repos/src/go.rs b/crates/repos/src/go.rs index eb1a928..8b08f78 100644 --- a/crates/repos/src/go.rs +++ b/crates/repos/src/go.rs @@ -487,10 +487,10 @@ mod tests_go_filename { upstream: Url::parse("https://dl.google.com/go/").unwrap(), ..Default::default() }; - let url = t.upstream_url(&config, "recluse:test").unwrap(); + let url = t.upstream_url(&config, "tesor:test").unwrap(); assert_eq!( url.as_str(), - "https://dl.google.com/go/go1.25.6.linux-amd64.tar.gz?source=recluse%3Atest" + "https://dl.google.com/go/go1.25.6.linux-amd64.tar.gz?source=tesor%3Atest" ); } diff --git a/crates/tesor/Cargo.toml b/crates/tesor/Cargo.toml new file mode 100644 index 0000000..908295d --- /dev/null +++ b/crates/tesor/Cargo.toml @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +[package] +name = "tesor" +description = "tesor web-server and daemon" +version.workspace = true + +authors.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +base.workspace = true +repos.workspace = true + +async-trait.workspace = true +axum.workspace = true +axum-server.workspace = true +bytes.workspace = true +bytesize.workspace = true +chrono.workspace = true +crc32fast.workspace = true +hex.workspace = true +http-body-util.workspace = true +hyper.workspace = true +hyper-tls.workspace = true +hyper-util.workspace = true +maud.workspace = true +rustls.workspace = true +serde.workspace = true +serde_json.workspace = true +sqlx.workspace = true +thiserror.workspace = true +tokio.workspace = true +tokio-util.workspace = true +toml.workspace = true +tonic.workspace = true +tower.workspace = true +tower-http.workspace = true +tower_governor.workspace = true +tracing.workspace = true +tracing-opentelemetry.workspace = true +tracing-subscriber.workspace = true +url.workspace = true +uuid.workspace = true + +# telemetry +opentelemetry.workspace = true +opentelemetry-appender-tracing.workspace = true +opentelemetry-otlp.workspace = true +opentelemetry-semantic-conventions.workspace = true +opentelemetry_sdk.workspace = true + +[target.'cfg(target_os = "linux")'.dependencies] +sd-notify.workspace = true + +[dev-dependencies] +tempfile.workspace = true diff --git a/crates/tesor/build.rs b/crates/tesor/build.rs new file mode 100644 index 0000000..0f4e2d3 --- /dev/null +++ b/crates/tesor/build.rs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + println!("cargo::rerun-if-changed=../../Cargo.lock"); + println!("cargo::rerun-if-changed=../../mise.toml"); + println!("cargo::rerun-if-changed=../../mise.lock"); + + let out_dir = std::env::var("OUT_DIR").unwrap(); + let target = std::env::var("TARGET").unwrap(); + let output = format!("{out_dir}/licenses.json"); + + let status = std::process::Command::new("mise") + .current_dir(env!("CARGO_MANIFEST_DIR")) + .args([ + "run", + "licenses-json", + "--", + "--manifest-path", + "Cargo.toml", + "--output", + &output, + "--target", + &target, + ]) + .status() + .expect("failed to run licenses-json task"); + + assert!(status.success(), "failed to generate licenses.json"); +} diff --git a/crates/tesor/src/assets/apple-touch-icon.png b/crates/tesor/src/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0f6bb614210c0d59ae108acd732f59175f0ce984 GIT binary patch literal 715 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE4M+yv$zcaloCO|{#S9G0-5|_ZdRqK9P*AeO zHKHUqKdq!Zu_%?Hyu4g5GcUV1Ik6yBFTW^#_B$IX1_q`GPZ!6KiaBrZZWKIZAj0Z+ zMj(K-H&DJekagyZhBMR7ybb#>Njf$Dw!~wm(Casu6Vwt1>0w|&_q-~O)tNQqfb-!aYo`|j+`@9U3o(;x_mjFxS^hH3c?nR}=fb(r>kV^z5- W|K3?~yAd!UGI+ZBxvXNjf$Dw!~wm(Casu6Vwt1>0w|&_q-~O)tNQqfb-!aYo`|j+`@9U3o(;x_mjFxS^hH3c?nR}=fb(r>kV^z5- W|K3?~yAd!UGI+ZBxvX9gP2NHH*QEc0}645^s&_J(2a5eEU+ zgR>_yFE-eCG(E}fVa~LpWgk`v)U2GmJg)k^J;Q>h^;HZEAF6@YH?c4u|VWXxp&u`@U~q*k%;f%`@HGs3$)f8BN#jRaZIR zs*dMlP?zT+>h87;*|OLJ(g7FGTJB`?E){?S}AIBNGVtQO%)`N1FB?19Z122WQ%mvv4F FO#l|3Ajkj! literal 0 HcmV?d00001 diff --git a/crates/tesor/src/assets/favicon.ico b/crates/tesor/src/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..093e50bda4580e7ef4100b9e057d2e8cc47b2936 GIT binary patch literal 32038 zcmeHPyKWRq5G@N!geD?_$&AU0wTVFPl1Hk84fHD-Mc=} zl)I>HS50s4s^+RyQ?GMQ&D7nCmenOXphJ4}h>~5<`$M8vM6_6}{Vys0HN{R(Z~bRP zZypmZmuvszCDF|bqLb8L7J1F0o?ERCdEUo;)N#w}B%k-em+E&rPU+wO_4)pgs60R45#@u})iF^% zsLwWk&MeYdrjtLfR#*Dq`IK(*!L{4ZXn#H<<2{V^F7esoWn15`7cX#vziq$p+`Mc& z|J{E6{QdF0XwCC_?pyJ-e$K~zt>2pGeB8IN&pVYj~}BjgRMB@uR-&zt8c!_U3t|S9$lkCOZG` zIXgSQE)T1pzCD=!Id>^mk^DVH`pT5vrt{}}il;}dO43pbJ@#rd-ZZE&oV7c$5m-AROm%SVpepyrABhPJF zQ{MAB_+^dd9(deJn7j_YSG|NI#lg?*w(2EbDb97n-&VcEE5)Ig+ilfLyi%O&hQF

R4Zc>LQa$i`TS-rAyti)fwd$1Wf!EtgdRpVXb%U=}XKOupthSZc ztKO;`{a$)nalmi2t-N0KR^90L($k6qeyeTe^{Th(M!%Pyz=^+ih#iO>h#iO>*r6TB zQuA+sZ1!XNol(Y_%U)a^Uu67EZL5wOq7NB=Q)_)*xBdBdTFZ2j+`qrmN`dDG>yyH( zE5DpL@eE=IVh3UeVh3UeVh3UeVh3UeVh3tFfbTX(wKdJ2u~FxSF3(wOSl}7e)--#@ zMx7hFJZG(8foD`()9e`=b#CbLoVA7po>6U0vuAA7xuMH*)*2RgMzuA~p0QEqhAz)p zYgphJ)z&n7#zvhRx;$sCVS#5~^9(9tf^?1f4Uc@yn)Jb;kehpvCOS%ysyz8;(iEH|; z`BCPSbXe!p`X$~`;+lSIev~;S9oG4@eu;OKxTfElA7xHShjl({zog46Zq+w;zt@}+ z&fNN~_>yj~xK-cW{a$lQICJZ_;!C={;#Pfg_j}DL;mobyiZAK*id*%~-S0K0gfqAP zz>nX3#ty^|#16y`#16y`#16y`#18EB4y4yR<8_(;Hv>o0v^9Hlnf^)q_q9g-dGJKP NdI0ybGrpSy{TrMyi)R1; literal 0 HcmV?d00001 diff --git a/crates/tesor/src/assets/favicon.svg b/crates/tesor/src/assets/favicon.svg new file mode 100644 index 0000000..ed7cc17 --- /dev/null +++ b/crates/tesor/src/assets/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/crates/tesor/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 b/crates/tesor/src/assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2 new file mode 100644 index 0000000000000000000000000000000000000000..98adc803f58f81df8410f4ad433e46b773e1b8d5 GIT binary patch literal 122452 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 literal 0 HcmV?d00001 diff --git a/crates/tesor/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 b/crates/tesor/src/assets/jetbrainsmono/JetBrainsMono[wght].woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4cd1413366e027b0929706deb3ac04baf7328526 GIT binary patch literal 113700 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 z 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 Self { + Self { + shutdown_timeout: Duration::from_secs(60), + request_timeout: Duration::from_secs(30), + max_body_size: ByteSize::mb(64), + max_concurrent_requests: 512, + rate_limit_period: Duration::from_secs(10), + rate_limit_burst_size: 50, + } + } +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ListenerConfig { + #[serde(deserialize_with = "deserialize_listener_addr")] + pub addr: SocketAddr, + + /// Hostnames to accept for this listener. Empty means accept all. + pub hostnames: Vec, + + /// Path to TLS certificate file (PEM format). If set, tls_key must also be set. + pub tls_crt: Option, + + /// Path to TLS private key file (PEM format). If set, tls_crt must also be set. + pub tls_key: Option, +} + +impl Default for ListenerConfig { + fn default() -> Self { + Self { + addr: "127.0.0.1:2025".parse().unwrap(), + hostnames: vec![ + String::from("[::1]"), + String::from("127.0.0.1"), + String::from("localhost"), + ], + tls_crt: None, + tls_key: None, + } + } +} + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "lowercase")] +pub enum LogLevel { + Trace, + Debug, + Info, + Warning, + Error, +} + +#[derive(Debug, Clone, Copy, Deserialize, Default, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum StdoutFormat { + #[default] + Pretty, + Json, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(default)] +pub struct StdoutConfig { + /// Enables sending logs to the stdout + pub enabled: bool, + + /// Controls which logs will be sent to stdout + pub log_level: LogLevel, + + /// Controls the format of logs in stdout + pub log_format: StdoutFormat, +} + +impl Default for StdoutConfig { + fn default() -> Self { + Self { + enabled: true, + log_level: LogLevel::Info, + log_format: StdoutFormat::Pretty, + } + } +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(default)] +pub struct OtelcolConfig { + /// Enables sending telemetry to the otlp collector + pub enabled: bool, + + /// Send logs to OTLP at this level (None = disabled) + pub logs: bool, + + /// Send traces to OTLP + pub traces: bool, + + /// Send traces to OTLP + pub metrics: bool, + + /// OTLP endpoint (grpc:// or http://) + pub endpoint: String, + + /// Export timeout in seconds + #[serde(deserialize_with = "deserialize_duration")] + pub timeout: Duration, + + /// Controls which logs will be sent to otlp + pub log_level: LogLevel, + + /// Path to CA certificate for TLS (required for grpcs://) + pub tls_ca: Option, + + /// Path to client certificate for mTLS + pub tls_crt: Option, + + /// Path to client key for mTLS + pub tls_key: Option, + + /// HTTP headers for authentication + pub headers: HashMap, +} + +impl Default for OtelcolConfig { + fn default() -> Self { + Self { + enabled: true, + logs: true, + traces: true, + metrics: true, + timeout: Duration::from_secs(10), + endpoint: "http://localhost:4317".into(), + log_level: LogLevel::Info, + tls_ca: None, + tls_crt: None, + tls_key: None, + headers: HashMap::new(), + } + } +} + +#[derive(Debug, Clone, Deserialize, Default)] +#[serde(default)] +pub struct TelemetryConfig { + pub stdout: StdoutConfig, + pub otelcol: Option, +} + +#[derive(Debug, Clone, Deserialize, Default)] +#[serde(default)] +pub struct BackendsConfig { + pub go: GoConfig, + pub zig: ZigConfig, +} + +#[derive(Debug, Deserialize)] +#[serde(default)] +pub struct ConfigService { + appname: String, + dirname: PathBuf, + listen: Vec, + server: ServerConfig, + telemetry: TelemetryConfig, + backends: BackendsConfig, +} + +impl Default for ConfigService { + fn default() -> Self { + Self { + appname: "tesor".to_string(), + dirname: PathBuf::from("./.tesor-state"), + listen: vec![ListenerConfig::default()], + server: ServerConfig::default(), + telemetry: TelemetryConfig::default(), + backends: BackendsConfig::default(), + } + } +} + +impl ConfigService { + pub fn load(config_path: Option) -> Result { + let config = match config_path { + Some(path) => { + info!("use config file from {}", path.to_str().unwrap()); + + let content = fs::read_to_string(path)?; + toml::from_str(&content)? + } + None => { + info!("configuration file path not provided"); + Self::default() + } + }; + + config.validate() + } + + fn validate(self) -> Result { + let mut chars = self.appname.chars(); + if !chars.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') { + return Err(ConfigError::InvalidAppname(self.appname.clone())); + } + + let metadata = fs::metadata(&self.dirname).map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + ConfigError::DirNotFound(self.dirname.clone()) + } else { + ConfigError::Io(e) + } + })?; + + if !metadata.is_dir() { + return Err(ConfigError::NotADirectory(self.dirname.clone())); + } + + let testfile = self.dirname.join(".health"); + fs::write(&testfile, std::process::id().to_string()) + .map_err(|e| ConfigError::NotWritable(self.dirname.clone(), e))?; + fs::remove_file(&testfile)?; + + // Validate listener configuration + for listener in &self.listen { + match (&listener.tls_crt, &listener.tls_key) { + (Some(_crt), None) => { + return Err(ConfigError::TlsKeyMissing(listener.addr)); + } + (None, Some(_)) => { + return Err(ConfigError::TlsCrtMissing(listener.addr)); + } + (Some(crt), Some(key)) => { + if !crt.exists() { + return Err(ConfigError::TlsCrtNotFound(listener.addr, crt.clone())); + } + if !key.exists() { + return Err(ConfigError::TlsKeyNotFound(listener.addr, key.clone())); + } + } + (None, None) => {} + } + } + + Ok(self) + } + + pub fn appname(&self) -> &str { + &self.appname + } + + pub fn dirname(&self) -> &Path { + &self.dirname + } + + pub fn server(&self) -> &ServerConfig { + &self.server + } + + pub fn listeners(&self) -> &[ListenerConfig] { + &self.listen + } + + pub fn telemetry(&self) -> &TelemetryConfig { + &self.telemetry + } + + pub fn backends(&self) -> &BackendsConfig { + &self.backends + } +} + +#[cfg(test)] +impl ConfigService { + pub fn for_test(dirname: PathBuf) -> Self { + Self { + appname: "test".to_string(), + dirname, + server: ServerConfig::default(), + listen: vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: None, + tls_key: None, + }], + telemetry: TelemetryConfig::default(), + backends: BackendsConfig::default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + fn base_config(dir: PathBuf) -> ConfigService { + ConfigService { + appname: "valid-name".to_string(), + dirname: dir, + server: ServerConfig::default(), + listen: vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: None, + tls_key: None, + }], + telemetry: TelemetryConfig::default(), + backends: BackendsConfig::default(), + } + } + + fn write_temp_file(dir: &Path, name: &str) -> PathBuf { + let path = dir.join(name); + std::fs::write(&path, "x").unwrap(); + path + } + + mod defaults_tests { + use super::*; + + #[test] + fn test_for_test_config() { + let dir = TempDir::new().unwrap(); + let cfg = ConfigService::for_test(dir.path().to_path_buf()); + assert_eq!(cfg.appname(), "test"); + assert_eq!(cfg.dirname(), dir.path()); + } + + #[test] + fn test_listener_default() { + let cfg = ListenerConfig::default(); + assert!(cfg.hostnames.contains(&"localhost".to_string())); + } + } + + mod load_tests { + use super::*; + + #[test] + fn test_load_none_defaults() { + let temp = TempDir::new().unwrap(); + let state = temp.path().join(".tesor-state"); + std::fs::create_dir_all(&state).unwrap(); + let cwd = std::env::current_dir().unwrap(); + std::env::set_current_dir(temp.path()).unwrap(); + + let cfg = ConfigService::load(None).unwrap(); + assert_eq!(cfg.appname(), "tesor"); + + std::env::set_current_dir(cwd).unwrap(); + } + + #[test] + fn test_load_some_and_parse_error() { + let dir = TempDir::new().unwrap(); + let good = dir.path().join("good.toml"); + let bad = dir.path().join("bad.toml"); + let missing = dir.path().join("missing.toml"); + let state = dir.path().join("state"); + std::fs::create_dir_all(&state).unwrap(); + + std::fs::write( + &good, + format!( + "appname = \"ok\"\n\ + dirname = \"{}\"\n\ + listen = []\n\ + [server]\n\ + shutdown_timeout = 1\n\ + request_timeout = 1\n\ + max_body_size = \"1 MB\"\n\ + max_concurrent_requests = 1\n\ + rate_limit_period = 1\n\ + rate_limit_burst_size = 1\n\ + [telemetry]\n\ + [telemetry.stdout]\n\ + enabled = true\n\ + log_level = \"info\"\n\ + log_format = \"pretty\"\n\ + [backends]\n\ + [backends.go]\n\ + [backends.zig]\n", + state.display() + ), + ) + .unwrap(); + + std::fs::write(&bad, "not = [valid").unwrap(); + + let cfg = ConfigService::load(Some(good)).unwrap(); + assert_eq!(cfg.appname(), "ok"); + + let err = ConfigService::load(Some(missing)).unwrap_err(); + assert!(err.to_string().contains("failed to read")); + + let err = ConfigService::load(Some(bad)).unwrap_err(); + assert!(err.to_string().contains("failed to parse")); + } + } + + mod validation_tests { + use super::*; + + #[test] + fn test_validate_invalid_appname() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + cfg.appname = "bad name!".to_string(); + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("appname 'bad name!'")); + } + + #[test] + fn test_validate_dir_not_found() { + let temp = TempDir::new().unwrap(); + let missing = temp.path().join("missing-dir"); + let cfg = base_config(missing); + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("does not exist")); + } + + #[test] + fn test_validate_not_a_directory() { + let dir = TempDir::new().unwrap(); + let file_path = dir.path().join("file"); + std::fs::write(&file_path, "x").unwrap(); + let cfg = base_config(file_path); + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("is not a directory")); + } + + #[cfg(unix)] + #[test] + fn test_validate_metadata_io_error() { + use std::os::unix::fs::PermissionsExt; + + let dir = TempDir::new().unwrap(); + let child = dir.path().join("child"); + std::fs::create_dir_all(&child).unwrap(); + + let mut perms = std::fs::metadata(dir.path()).unwrap().permissions(); + perms.set_mode(0o000); + std::fs::set_permissions(dir.path(), perms).unwrap(); + + let cfg = base_config(child.clone()); + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("failed to read")); + + let mut perms = std::fs::metadata(dir.path()).unwrap().permissions(); + perms.set_mode(0o700); + std::fs::set_permissions(dir.path(), perms).unwrap(); + } + + #[cfg(unix)] + #[test] + fn test_validate_not_writable() { + use std::os::unix::fs::PermissionsExt; + + let dir = TempDir::new().unwrap(); + let mut perms = std::fs::metadata(dir.path()).unwrap().permissions(); + perms.set_mode(0o400); + std::fs::set_permissions(dir.path(), perms).unwrap(); + + let cfg = base_config(dir.path().to_path_buf()); + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("not writable")); + } + + #[test] + fn test_validate_tls_key_missing() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + cfg.listen = vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: Some(PathBuf::from("/tmp/does-not-matter.crt")), + tls_key: None, + }]; + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("tls_key is missing")); + } + + #[test] + fn test_validate_tls_crt_missing() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + cfg.listen = vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: None, + tls_key: Some(PathBuf::from("/tmp/does-not-matter.key")), + }]; + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("tls_crt is missing")); + } + + #[test] + fn test_validate_tls_files_not_found() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + cfg.listen = vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: Some(PathBuf::from("/tmp/missing.crt")), + tls_key: Some(PathBuf::from("/tmp/missing.key")), + }]; + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("TLS crtificate file not found")); + } + + #[test] + fn test_validate_tls_key_not_found() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + let crt = write_temp_file(dir.path(), "cert.pem"); + cfg.listen = vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: Some(crt), + tls_key: Some(dir.path().join("missing.key")), + }]; + let err = cfg.validate().unwrap_err(); + assert!(err.to_string().contains("TLS key file not found")); + } + + #[test] + fn test_validate_tls_files_exist() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + let crt = write_temp_file(dir.path(), "cert.pem"); + let key = write_temp_file(dir.path(), "key.pem"); + cfg.listen = vec![ListenerConfig { + addr: "127.0.0.1:0".parse().unwrap(), + hostnames: Vec::new(), + tls_crt: Some(crt), + tls_key: Some(key), + }]; + cfg.validate().unwrap(); + } + + #[test] + fn test_validate_ok_and_getters() { + let dir = TempDir::new().unwrap(); + let mut cfg = base_config(dir.path().to_path_buf()); + cfg.listen = vec![ListenerConfig::default()]; + + let cfg = cfg.validate().unwrap(); + assert_eq!(cfg.appname(), "valid-name"); + assert_eq!(cfg.dirname(), dir.path()); + let _ = cfg.server(); + let _ = cfg.listeners(); + let _ = cfg.telemetry(); + let _ = cfg.backends(); + } + } +} diff --git a/crates/tesor/src/controller_backend.rs b/crates/tesor/src/controller_backend.rs new file mode 100644 index 0000000..f97caa1 --- /dev/null +++ b/crates/tesor/src/controller_backend.rs @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; + +use axum::{Router, body, extract, http, response, routing}; +use tracing::error; + +use crate::proxy; +use crate::storage; +use repos::{Backend, BackendError, BackendSpec, ResolvedFile}; + +/// Generic controller for backend HTTP handling. +pub struct BackendController { + backend: Arc>, + storage: Arc, + upstream: Arc, +} + +impl BackendController { + pub fn new( + backend: Arc>, + storage: Arc, + upstream: Arc, + ) -> Self { + Self { + backend, + storage, + upstream, + } + } + + pub fn router(self: Arc) -> Router { + Router::new() + .route("/{filename}", routing::get(Self::handle)) + .with_state(self) + } + + async fn handle( + extract::State(controller): extract::State>, + extract::Path(filename): extract::Path, + ) -> Result { + let (url, mime) = match controller.backend.resolve_file(&filename).await { + Ok(ResolvedFile::Content { data, mime }) => { + return Ok(Self::build_response(http::StatusCode::OK, data, mime)); + } + Ok(ResolvedFile::Upstream { url, mime }) => (url, mime), + Err(BackendError::NotFound) => { + error!(backend = S::ID, filename, "file not found"); + return Err(http::StatusCode::NOT_FOUND); + } + Err(e) => { + error!(backend = S::ID, filename, "error resolving file: {e}"); + return Err(http::StatusCode::INTERNAL_SERVER_ERROR); + } + }; + + match controller.storage.get(S::ID, &filename).await { + Ok(Some(entry)) => { + return Ok(Self::build_response( + http::StatusCode::OK, + entry.file_bytes.0, + mime, + )); + } + Ok(None) => {} + Err(err) => { + error!( + backend = S::ID, + filename, "failed to get file from storage: {err}" + ); + return Err(http::StatusCode::INTERNAL_SERVER_ERROR); + } + } + + let entry = controller + .upstream + .fetch(proxy::DownloadRequest { url }) + .await?; + + match controller.storage.put(S::ID, &filename, &entry.bytes).await { + Ok(()) => {} + Err(err) => { + error!( + backend = S::ID, + filename, "failed to put file to storage: {err}" + ); + return Err(http::StatusCode::INTERNAL_SERVER_ERROR); + } + } + + Ok(Self::build_response( + http::StatusCode::OK, + entry.bytes, + mime, + )) + } + + fn build_response( + status: http::StatusCode, + bytes: bytes::Bytes, + mime: repos::ContentType, + ) -> response::Response { + response::Response::builder() + .status(status) + .header(http::header::CONTENT_TYPE, mime.as_str()) + .header(http::header::CONTENT_LENGTH, bytes.len()) + .body(body::Body::from(bytes)) + .unwrap() + } +} diff --git a/crates/tesor/src/controller_web.rs b/crates/tesor/src/controller_web.rs new file mode 100644 index 0000000..125295e --- /dev/null +++ b/crates/tesor/src/controller_web.rs @@ -0,0 +1,486 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::future::Future; +use std::pin::Pin; +use std::sync::{Arc, LazyLock}; +use std::task::{Context, Poll}; + +use axum::body::Body; +use axum::http::{HeaderValue, Method, Request, Response, StatusCode, header}; +use axum::{extract, routing}; +use maud::{Markup, html}; +use serde::Deserialize; +use tower::{Layer, Service}; +use tracing::error; + +use base::ContentType; +use repos::{GoBackend, ZigBackend}; + +fn get_asset(path: &str) -> Option<(&'static [u8], ContentType)> { + Some(match path { + "base.css" => (include_bytes!("assets/base.css"), ContentType::TextCss), + "favicon.svg" => ( + include_bytes!("assets/favicon.svg"), + ContentType::ImageSvgXml, + ), + "favicon.ico" => ( + include_bytes!("assets/favicon.ico"), + ContentType::ImageXIcon, + ), + "favicon-192.png" | "apple-touch-icon.png" => ( + include_bytes!("assets/favicon-192.png"), + ContentType::ImagePng, + ), + "favicon-512.png" => ( + include_bytes!("assets/favicon-512.png"), + ContentType::ImagePng, + ), + "manifest.webmanifest" => ( + include_bytes!("assets/manifest.webmanifest"), + ContentType::ApplicationManifestJson, + ), + "jetbrainsmono/JetBrainsMono[wght].woff2" => ( + include_bytes!("assets/jetbrainsmono/JetBrainsMono[wght].woff2"), + ContentType::FontWoff2, + ), + "jetbrainsmono/JetBrainsMono-Italic[wght].woff2" => ( + include_bytes!("assets/jetbrainsmono/JetBrainsMono-Italic[wght].woff2"), + ContentType::FontWoff2, + ), + "robots.txt" => (include_bytes!("assets/robots.txt"), ContentType::TextPlain), + _ => return None, + }) +} + +const CSP: &str = "default-src 'self'; base-uri 'none'; img-src 'self'; font-src 'self'; style-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'"; + +#[derive(Deserialize)] +struct LicenseList { + overview: Vec, + licenses: Vec, +} + +#[derive(Deserialize)] +struct LicenseOverview { + id: String, + name: String, + count: usize, +} + +#[derive(Deserialize)] +struct LicenseEntry { + id: String, + name: String, + text: String, + first_of_kind: bool, + used_by: Vec, +} + +#[derive(Deserialize)] +struct LicenseUsedBy { + #[serde(rename = "crate")] + crate_: LicenseCrate, +} + +#[derive(Deserialize)] +#[allow(dead_code)] +struct LicenseCrate { + name: String, + version: String, + repository: Option, +} + +static LICENSES: LazyLock = LazyLock::new(|| { + let json = include_str!(concat!(env!("OUT_DIR"), "/licenses.json")); + serde_json::from_str(json).expect("failed to parse licenses.json") +}); + +/// Handles html pages rendering and static files +pub struct WebController { + zig: Option>, + go: Option>, +} + +impl WebController { + pub fn new(zig: Option>, go: Option>) -> Self { + Self { zig, go } + } +} + +impl WebController { + pub fn router(self: Arc) -> axum::Router { + axum::Router::new() + .route("/", axum::routing::get(Self::index)) + .route("/{*path}", routing::get(Self::assets)) + .route("/about/licenses", axum::routing::get(Self::licenses)) + .layer(CacheLayer) + .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( + header::CONTENT_SECURITY_POLICY, + HeaderValue::from_static(CSP), + )) + .with_state(self.clone()) + } + + async fn assets(extract::Path(path): extract::Path) -> Response { + let Some((data, content_type)) = get_asset(&path) else { + return Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .unwrap(); + }; + + let mut builder = Response::builder() + .status(StatusCode::OK) + .header(header::CONTENT_TYPE, content_type.as_str()); + + if matches!(content_type, ContentType::FontWoff2) { + builder = builder.header(header::CACHE_CONTROL, "public, max-age=31536000, immutable"); + } + + builder.body(Body::from(data)).unwrap() + } + + async fn index(extract::State(ctrl): extract::State>) -> Markup { + let zig_versions = if let Some(ref backend) = ctrl.zig { + match backend.get_releases().await { + Ok(v) => v, + Err(e) => { + error!("failed to get zig versions: {e}"); + Vec::new() + } + } + } else { + Vec::new() + }; + + let go_versions = if let Some(ref backend) = ctrl.go { + match backend.get_releases().await { + Ok(v) => v, + Err(e) => { + error!("failed to get go versions: {e}"); + Vec::new() + } + } + } else { + Vec::new() + }; + + WebController::layout( + "Tesor — tiny & opinionated packages mirror", + html! { + h1 { "Tesor — tiny & opinionated packages mirror." } + + p { + r#"This site provides a caching proxy for downloading Zig and Go installation files. + It reduces load on upstream servers and makes your infrastructure more reliable by adding redundancy."# + } + + p { + "Tesor is open source software licensed under " a href="https://www.gnu.org/licenses/agpl-3.0.html" { "AGPL-3.0" } ". " + "Source code is available on " a href="https://git.dimidiumlabs.io/tesor" { "Dimidium Labs Git" } ". " + "A list of dependency licenses " a href="/about/licenses" { "is available" } "." + } + + h2 { "Usage" } + + p { + "Replace official download URLs with " code { "https://pkg.earth/{tool}/{filename}" } ". " + "Files are cached automatically after the first download." + } + + h3 id="zig" { + a href="#zig" { "Zig" } + } + + @if !zig_versions.is_empty() { + details { + summary { "Available versions (" (zig_versions.len()) ")" } + + p { "You can take actual minisig public key at " a href="https://ziglang.org/download/" { "ziglang.org/download" } "." } + table { + thead { + tr { + th { "Version" } + th { "Date" } + th { "Docs" } + th { "Targets" } + } + } + tbody { + @for v in zig_versions.iter().rev() { + tr { + td { (v.version) } + td { (v.meta.date.as_deref().unwrap_or("-")) } + td { + @if let Some(ref url) = v.meta.docs { + a href=(url) { "docs" } + } + " " + @if let Some(ref url) = v.meta.std_docs { + a href=(url) { "std" } + } + " " + @if let Some(ref url) = v.meta.notes { + a href=(url) { "notes" } + } + } + td { + @for file in &v.files { + a href=(format!("/zig/{}", file.filename)) { (&file.meta.target) } + " " + } + } + } + } + } + } + } + } + + p { + "Read more about community mirrors in the " a href="https://ziglang.org/download/community-mirrors/" { "blog post" } ". " + "Information on how to deploy your own mirror is available " a href="https://github.com/ziglang/www.ziglang.org/blob/main/MIRRORS.md" { "in the documentation" } "." + } + + p { + "For simplicity, you can use tools like " a href="https://github.com/prantlf/zigup" { "prantlf/zigup" } + " and " a href="https://github.com/mlugg/setup-zig" { "mlugg/setup-zig" } "." + } + + p { + "To install manually:" + ol { + li { "download zig dist file:" br; code { "wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz" } ";" } + li { "download zig minisig file:" br; code { "wget https://pkg.earth/zig/zig-x86_64-linux-0.15.1.tar.xz.minisig" } ";" } + li { "check archive integrity:" br; code { "minisign -Vm zig-x86_64-linux-0.15.1.tar.xz -P RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" } ";" } + li { "unpack archive:" br; code { "tar -xf 'zig-x86_64-linux-0.15.1.tar.xz'" } ";" } + li { "check installed zig:" br; code { "./zig-x86_64-linux-0.15.1/zig --version" } ";" } + } + } + + h3 id="go" { a href="#go" { "Go" } } + + @if !go_versions.is_empty() { + details { + summary { "Available versions (" (go_versions.len()) ")" } + + p { "You can find available versions at " a href="https://go.dev/dl/" { "go.dev/dl" } "." } + table { + thead { + tr { + th { "Version" } + th { "Stable" } + th { "Files" } + } + } + tbody { + @for v in go_versions.iter().rev() { + tr { + td { (v.version) } + td { @if v.meta.stable { "✓" } @else { "" } } + td { + @for file in &v.files { + a href=(format!("/go/{}", file.filename)) { (file.filename) } + " " + } + } + } + } + } + } + } + } + + p { "To install manually:" } + + ol { + li { "download go dist file:" br; code { "wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz" } ";" } + li { "download go sha256 file:" br; code { "wget https://pkg.earth/go/go1.23.0.linux-amd64.tar.gz.sha256" } ";" } + li { "check archive integrity:" br; code { "sha256sum -c go1.23.0.linux-amd64.tar.gz.sha256" } ";" } + li { "unpack archive:" br; code { "tar -xzf go1.23.0.linux-amd64.tar.gz" } ";" } + li { "check installed go:" br; code { "./go/bin/go version" } ";" } + } + + h2 { "Privacy policy" } + + p { "This mirror is a non-profit project available on a voluntary basis. The author has no plans to fund it." } + + p { r#"Since the mirror is hosted on hardware, we collect access logs to combat bots and brute-force attacks. + The logs are used for security purposes and load planning, are not shared with third parties, + and are deleted after 30 days."# } + + p { "Third-party analytics systems are not used, same as client-side trackers." } + }, + ) + } + + async fn licenses() -> Markup { + let data = &*LICENSES; + + WebController::layout( + "Third Party Licenses", + html! { + h1 { "Licenses" } + + h2 { "Tesor" } + + p { + "Tesor is licensed under the " b { "GNU Affero General Public License v3.0 (AGPL-3.0)" } ". " + "This means you are free to use, modify, and distribute the software. " + "If you run a modified version of Tesor as a network service, " + "you must make the source code of your modifications available to its users. " + "The software is provided as-is, without warranty of any kind." + } + + p { + "Source code is available on " + a href="https://git.dimidiumlabs.io/tesor" { "Dimidium Labs Git" } + ". The full license text is included below." + } + + pre .license-text { (include_str!("../../../LICENSE")) } + + h2 { "Third party licenses" } + + ul { + @for ov in &data.overview { + li { a href=(format!("#{}", ov.id)) { (&ov.name) } " (" (ov.count) ")" } + } + } + + @for (i, lic) in data.licenses.iter().enumerate() { + @if lic.first_of_kind { + h3 id=(&lic.id) { (&lic.name) } + } + + section.license { + h4 id=(format!("{}-{}", lic.id, i)) { (&lic.name) } + + p { "Used by:" } + + ul .license-used-by { + @for usage in &lic.used_by { + li { + a href=( + usage.crate_.repository.as_deref() + .unwrap_or(&format!("https://crates.io/crates/{}", usage.crate_.name)) + ) { + (&usage.crate_.name) "@" (&usage.crate_.version) + } + } + } + } + + pre .license-text { (&lic.text) } + } + } + }, + ) + } + + fn layout(title: &str, content: Markup) -> Markup { + html! { + (maud::DOCTYPE) + + html lang="en" { + head { + meta charset="UTF-8"; + meta http-equiv="X-UA-Compatible" content="ie=edge"; + meta name="viewport" content="width=device-width,initial-scale=1"; + + title { (title) } + link rel="stylesheet" href="/base.css"; + + link rel="manifest" href="/manifest.webmanifest"; + link rel="icon" href="/favicon.ico" sizes="32x32"; + link rel="icon" href="/favicon.svg" type="image/svg+xml"; + link rel="apple-touch-icon" href="/apple-touch-icon.png"; + } + + body { + article { (content) } + } + } + } + } +} + +#[derive(Clone)] +struct CacheLayer; + +impl Layer for CacheLayer { + type Service = CacheService; + fn layer(&self, inner: S) -> Self::Service { + CacheService { inner } + } +} + +#[derive(Clone)] +struct CacheService { + inner: S, +} + +impl Service> for CacheService +where + S: Service, Response = Response> + Clone + Send + 'static, + S::Future: Send + 'static, + S::Error: Send + 'static, +{ + type Response = Response; + type Error = S::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let method = req.method().clone(); + let if_none_match = req.headers().get(header::IF_NONE_MATCH).cloned(); + + let mut inner = self.inner.clone(); + Box::pin(async move { + let resp = inner.call(req).await?; + + if !resp.status().is_success() { + return Ok(resp); + } + + let (parts, body) = resp.into_parts(); + let bytes = axum::body::to_bytes(body, usize::MAX).await.unwrap(); + + let mut hasher = crc32fast::Hasher::new(); + hasher.update(&bytes); + let etag = format!("\"{}\"", hex::encode(hasher.finalize().to_be_bytes())); + let etag_header = HeaderValue::from_str(&etag).unwrap(); + let cache_control = parts + .headers + .get(header::CACHE_CONTROL) + .cloned() + .unwrap_or(HeaderValue::from_static("no-cache")); + + if (method == Method::GET || method == Method::HEAD) + && if_none_match + .as_ref() + .is_some_and(|v| v.as_bytes() == etag.as_bytes()) + { + let mut resp = Response::builder() + .status(StatusCode::NOT_MODIFIED) + .body(Body::empty()) + .unwrap(); + resp.headers_mut().insert(header::ETAG, etag_header); + resp.headers_mut() + .insert(header::CACHE_CONTROL, cache_control); + return Ok(resp); + } + + let mut resp = Response::from_parts(parts, Body::from(bytes)); + resp.headers_mut().insert(header::ETAG, etag_header); + resp.headers_mut() + .entry(header::CACHE_CONTROL) + .or_insert(HeaderValue::from_static("no-cache")); + Ok(resp) + }) + } +} diff --git a/crates/tesor/src/main.rs b/crates/tesor/src/main.rs new file mode 100644 index 0000000..1029eca --- /dev/null +++ b/crates/tesor/src/main.rs @@ -0,0 +1,567 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +mod config; +mod proxy; +mod storage; +mod telemetry; + +mod controller_backend; +mod controller_web; + +use std::future::Future; +use std::net::{SocketAddr, TcpListener}; +use std::path::PathBuf; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use axum::{ + body::Body, + extract::{self, ConnectInfo}, + http::{self, Request, Response}, +}; +use axum_server::Handle; +use axum_server::tls_rustls::RustlsConfig; +#[cfg(target_os = "linux")] +use sd_notify::NotifyState; +use tokio::signal; +use tracing::{error, info, trace}; +use tracing_subscriber::registry::LookupSpan; + +use crate::controller_backend::BackendController; +use crate::controller_web::WebController; +use repos::{Backend, BackendSpec, GoBackend, ZigBackend}; + +async fn init_backend( + backend: Backend, + index_tasks: &mut tokio::task::JoinSet<()>, + index_cancel: tokio_util::sync::CancellationToken, +) -> Option>> { + if !backend.enabled() { + return None; + } + let backend = Arc::new(backend); + let interval = backend.refresh_interval(); + if !interval.is_zero() { + index_tasks.spawn(run_index_refresh( + S::ID, + backend.clone(), + interval, + index_cancel, + )); + } + Some(backend) +} + +async fn run_index_refresh( + name: &'static str, + backend: Arc>, + interval: std::time::Duration, + cancel: tokio_util::sync::CancellationToken, +) { + let mut ticker = tokio::time::interval(interval); + ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + + loop { + tokio::select! { + _ = cancel.cancelled() => { + info!(backend = name, "index refresh stopped"); + break; + } + _ = ticker.tick() => { + info!(backend = name, "refreshing index"); + match backend.refresh().await { + Ok(()) => info!(backend = name, "index refreshed"), + Err(e) => error!(backend = name, "index refresh failed: {e}"), + } + } + } + } +} + +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const HELP: &str = "\ +Usage: tesor [--config=] + +Options: + --config= Path to config file (optional) + --help Show this help message + --version Show version +"; + +/// Contains metainfo about one server interface +#[derive(Clone)] +struct ListenerInfo { + addr: SocketAddr, + hosts: Vec, +} + +/// Request info stored in span extensions for logging +#[derive(Clone)] +struct RequestInfo { + method: http::Method, + version: http::Version, + path: http::Uri, + host: Option, + user_agent: Option, +} + +/// Key extractor for tower-governor that uses ConnectInfo to get the client IP. +#[derive(Clone)] +struct ClientIpKeyExtractor; +impl tower_governor::key_extractor::KeyExtractor for ClientIpKeyExtractor { + type Key = std::net::IpAddr; + + fn name(&self) -> &'static str { + "ClientIpKeyExtractor" + } + + fn extract(&self, req: &Request) -> Result { + req.extensions() + .get::>() + .map(|ci| ci.0.ip()) + .ok_or(tower_governor::GovernorError::UnableToExtractKey) + } +} + +#[tokio::main] +async fn main() { + let mut config_path = None; + for arg in std::env::args().skip(1) { + if arg == "--help" || arg == "-h" { + print!("{HELP}"); + return; + } + if arg == "--version" || arg == "-V" { + println!("tesor {VERSION}"); + return; + } + if let Some(path) = arg.strip_prefix("--config=") { + config_path = Some(PathBuf::from(path)); + } + } + + let config = Arc::new( + config::ConfigService::load(config_path).unwrap_or_else(|e| { + eprintln!("invalid config: {e}"); + std::process::exit(1); + }), + ); + + let mut telemetry = + telemetry::TelemetryService::init(config.telemetry(), config.appname(), VERSION); + + let storage = Arc::new(storage::StorageService::new(config.clone()).await.unwrap()); + let network = Arc::new(proxy::ProxyService::new()); + + let source = format!("tesor:{}", config.appname()); + let backends = config.backends(); + + const REQUEST_ID_HEADER: http::HeaderName = http::HeaderName::from_static("x-request-id"); + + let trace_layer = tower_http::trace::TraceLayer::new_for_http() + .make_span_with(|req: &http::Request| { + let request_id = req + .headers() + .get(&REQUEST_ID_HEADER) + .and_then(|v| v.to_str().ok()) + .unwrap_or(""); + let local_addr = req.extensions().get::().map(|a| a.addr); + let remote_addr = req + .extensions() + .get::>() + .map(|ci| ci.0.ip()); + + tracing::info_span!( + "http_request", + request_id = %request_id, + local_addr = ?local_addr, + remote_addr = ?remote_addr, + ) + }) + .on_request(|req: &Request, span: &tracing::Span| { + let info = RequestInfo { + method: req.method().clone(), + path: req.uri().clone(), + version: req.version(), + host: extract_host(req), + user_agent: req + .headers() + .get(http::header::USER_AGENT) + .and_then(|v| v.to_str().ok()) + .map(String::from), + }; + + span.with_subscriber(|(id, dispatch)| { + if let Some(reg) = dispatch.downcast_ref::() + && let Some(span_ref) = reg.span(id) + { + span_ref.extensions_mut().insert(info); + } + }); + }) + .on_response( + |res: &Response, latency: std::time::Duration, span: &tracing::Span| { + use axum::body::HttpBody as _; + + let status = res.status().as_u16(); + let content_length = res.body().size_hint().exact(); + let content_type = res + .headers() + .get(http::header::CONTENT_TYPE) + .and_then(|v| v.to_str().ok()); + + let req_info = span.with_subscriber(|(id, dispatch)| { + dispatch + .downcast_ref::() + .and_then(|reg| reg.span(id)) + .and_then(|span_ref| span_ref.extensions().get::().cloned()) + }); + + if let Some(Some(req_info)) = req_info { + info!( + method = %req_info.method, + version = ?req_info.version, + path = %req_info.path, + host = req_info.host, + user_agent = req_info.user_agent, + status, + latency = latency.as_nanos() as u64, + content_type, + content_length, + "on_response", + ); + } else { + info!( + status, + latency = latency.as_nanos() as u64, + content_type, + content_length, + "on_response", + ); + } + }, + ) + .on_failure(tower_http::trace::DefaultOnFailure::new().level(tracing::Level::ERROR)); + + let governor_config = tower_governor::governor::GovernorConfigBuilder::default() + .period(config.server().rate_limit_period) + .burst_size(config.server().rate_limit_burst_size) + .key_extractor(ClientIpKeyExtractor) + .finish() + .unwrap(); + + let mut index_tasks = tokio::task::JoinSet::new(); + let index_cancel = tokio_util::sync::CancellationToken::new(); + + let zig_backend = init_backend( + ZigBackend::new( + backends.zig.clone(), + source.clone(), + storage.clone(), + network.clone(), + ), + &mut index_tasks, + index_cancel.clone(), + ) + .await; + + let go_backend = init_backend( + GoBackend::new( + backends.go.clone(), + source.clone(), + storage.clone(), + network.clone(), + ), + &mut index_tasks, + index_cancel.clone(), + ) + .await; + + let web_controller = Arc::new(WebController::new(zig_backend.clone(), go_backend.clone())); + let mut app = axum::Router::new().merge(web_controller.router()); + + if let Some(ref backend) = zig_backend { + let ctrl = Arc::new(BackendController::new( + backend.clone(), + storage.clone(), + network.clone(), + )); + app = app.nest("/zig", ctrl.router()); + } + + if let Some(ref backend) = go_backend { + let ctrl = Arc::new(BackendController::new( + backend.clone(), + storage.clone(), + network.clone(), + )); + app = app.nest("/go", ctrl.router()); + } + + let app = app + // Opt-in layers + .layer(tower_http::compression::CompressionLayer::new()) + // request limits + .layer(HostValidationLayer) + .layer(tower_http::limit::RequestBodyLimitLayer::new( + config.server().max_body_size.as_u64() as usize, + )) + .layer(tower_http::timeout::TimeoutLayer::with_status_code( + http::StatusCode::REQUEST_TIMEOUT, + config.server().request_timeout, + )) + // logging + .layer(trace_layer) + // rate-limits + .layer(tower_governor::GovernorLayer::new(Arc::new( + governor_config, + ))) + .layer(tower::limit::ConcurrencyLimitLayer::new( + config.server().max_concurrent_requests, + )) + // global headers + .layer(tower_http::request_id::PropagateRequestIdLayer::new( + REQUEST_ID_HEADER.clone(), + )) + .layer(tower_http::request_id::SetRequestIdLayer::new( + REQUEST_ID_HEADER.clone(), + tower_http::request_id::MakeRequestUuid, + )) + .layer(tower_http::set_header::SetResponseHeaderLayer::overriding( + http::header::SERVER, + http::HeaderValue::from_static(concat!("tesor/", env!("CARGO_PKG_VERSION"))), + )); + + let mut tasks = tokio::task::JoinSet::new(); + let handle = Handle::new(); + + for listener_config in config.listeners() { + let std_listener = TcpListener::bind(listener_config.addr).unwrap(); + std_listener.set_nonblocking(true).unwrap(); + + let addr = std_listener.local_addr().unwrap(); + + let app = app + .clone() + .layer(axum::Extension(ListenerInfo { + addr, + hosts: listener_config.hostnames.clone(), + })) + .into_make_service_with_connect_info::(); + + let handle = handle.clone(); + + let tls_enabled = + if let (Some(crt), Some(key)) = (&listener_config.tls_crt, &listener_config.tls_key) { + let rustls_config = RustlsConfig::from_pem_file(crt, key) + .await + .expect("failed to load TLS config"); + + tasks.spawn(async move { + let server = axum_server::from_tcp_rustls(std_listener, rustls_config).unwrap(); + server.handle(handle).serve(app).await.unwrap(); + }); + + true + } else { + tasks.spawn(async move { + let server = axum_server::from_tcp(std_listener).unwrap(); + server.handle(handle).serve(app).await.unwrap(); + }); + + false + }; + + info!( + "listening {} on {} (hostnames: {})", + if tls_enabled { "HTTPS" } else { "HTTP" }, + addr, + if listener_config.hostnames.is_empty() { + "*".to_string() + } else { + listener_config.hostnames.join(", ") + }, + ); + } + + let mut watchdog_ticker = tokio::time::interval(std::time::Duration::from_secs(60)); + + #[cfg(target_os = "linux")] + if sd_notify::booted().unwrap_or(false) { + sd_notify::notify(false, &[NotifyState::Ready]).ok(); + + let mut usec = 0u64; + (sd_notify::watchdog_enabled(true, &mut usec) && usec > 0).then(|| { + let interval = std::time::Duration::from_micros(usec) / 2; + info!( + interval_ms = interval.as_millis() as u64, + "watchdog enabled" + ); + watchdog_ticker = tokio::time::interval(interval); + }); + }; + + #[cfg(unix)] + let mut sigint = signal::unix::signal(signal::unix::SignalKind::interrupt()) + .expect("failed to install signal handler"); + #[cfg(windows)] + let mut sigint = signal::windows::signal(signal::windows::SignalKind::interrupt()) + .expect("failed to install signal handler"); + + #[cfg(unix)] + let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler"); + + loop { + let watchdog = watchdog_ticker.tick(); + + #[cfg(unix)] + let sigterm = sigterm.recv(); + #[cfg(not(unix))] + let sigterm = std::future::pending::<()>(); + + tokio::select! { + _ = sigint.recv() => { + info!("received SIGINT, shutting down"); + break; + }, + _ = sigterm => { + info!("received SIGTERM, shutting down"); + break; + }, + _ = watchdog => { + trace!("server is alive"); + + #[cfg(target_os = "linux")] + sd_notify::notify(false, &[NotifyState::Watchdog]).ok(); + }, + result = tasks.join_next() => { + match result { + Some(Ok(())) => error!("listener exited unexpectedly, shutting down"), + Some(Err(e)) => error!("listener failed: {e}, shutting down"), + None => { + error!("no listeners running"); + return; + } + } + break; + }, + } + } + + #[cfg(target_os = "linux")] + sd_notify::notify(false, &[NotifyState::Stopping]).ok(); + + index_cancel.cancel(); + handle.graceful_shutdown(None); + + // Wait for all tasks to finish with timeout + let shutdown_result = tokio::time::timeout(config.server().shutdown_timeout, async { + while let Some(result) = tasks.join_next().await { + if let Err(e) = result { + error!("listener task failed: {e}"); + } + } + while let Some(result) = index_tasks.join_next().await { + if let Err(e) = result { + error!("index task failed: {e}"); + } + } + }) + .await; + + if shutdown_result.is_err() { + error!( + "shutdown timeout after {:?}, aborting remaining tasks", + config.server().shutdown_timeout, + ); + tasks.abort_all(); + index_tasks.abort_all(); + } else { + info!("shutdown complete"); + } + + telemetry.shutdown(); +} + +/// Layer that validates the Host header against configured hostnames. +#[derive(Clone)] +struct HostValidationLayer; +impl tower::Layer for HostValidationLayer { + type Service = HostValidationService; + + fn layer(&self, inner: S) -> Self::Service { + HostValidationService { inner } + } +} + +#[derive(Clone)] +struct HostValidationService { + inner: S, +} +impl tower::Service> for HostValidationService +where + S: tower::Service, Response = Response> + Clone + Send + 'static, + S::Future: Send, +{ + type Response = S::Response; + type Error = S::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let interface = req.extensions().get::().cloned(); + + if let Some(iface) = interface + && !iface.hosts.is_empty() + { + let is_valid = extract_host(&req) + .map(|h| { + iface + .hosts + .iter() + .any(|allowed| allowed.eq_ignore_ascii_case(&h)) + }) + .unwrap_or(false); + + if !is_valid { + return Box::pin(async move { + Ok(Response::builder() + .status(http::StatusCode::MISDIRECTED_REQUEST) + .body(Body::empty()) + .unwrap()) + }); + } + } + + let clone = self.inner.clone(); + let mut inner = std::mem::replace(&mut self.inner, clone); + Box::pin(async move { inner.call(req).await }) + } +} + +fn extract_host(req: &Request) -> Option { + // HTTP/1.1 uses HOST header, HTTP/2 uses :authority (available via URI) + let raw = if let Some(host) = req.headers().get(http::header::HOST) { + host.to_str().ok().map(|raw| { + if let Some((host, port)) = raw.rsplit_once(':') + && port.parse::().is_ok() + && (host.ends_with(']') || !host.contains('[')) + { + host + } else { + raw + } + }) + } else { + req.uri().host() + }; + + raw.and_then(|raw| url::Host::parse(raw).ok()) + .map(|h| h.to_string().trim_end_matches('.').to_string()) +} diff --git a/crates/tesor/src/proxy.rs b/crates/tesor/src/proxy.rs new file mode 100644 index 0000000..5e98d23 --- /dev/null +++ b/crates/tesor/src/proxy.rs @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +use bytes::Bytes; +use http_body_util::{BodyExt, Empty}; +use hyper::{Request, http}; +use hyper_tls::HttpsConnector; +use hyper_util::client::legacy::{Client, connect::HttpConnector}; +use hyper_util::rt::TokioExecutor; +use tower::ServiceExt; +use tower_http::follow_redirect::FollowRedirect; +use url::Url; + +use repos::{BackendError, BackendNetwork}; + +#[derive(Clone)] +pub struct DownloadRequest { + pub url: Url, +} + +#[derive(Clone)] +pub struct File { + pub bytes: Bytes, +} + +pub struct ProxyService { + client: FollowRedirect, Empty>>, +} + +impl ProxyService { + pub fn new() -> Self { + let https = HttpsConnector::new(); + let client = Client::builder(TokioExecutor::new()).build(https); + Self { + client: FollowRedirect::new(client), + } + } + + pub async fn fetch(&self, request: DownloadRequest) -> Result { + let request = Request::builder() + .method(http::Method::GET) + .uri(request.url.as_str()) + .header(http::header::USER_AGENT, "tesor/0.1") + .body(Empty::::new()) + .unwrap(); + + let response = self + .client + .clone() + .oneshot(request) + .await + .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)?; + + let (parts, body) = response.into_parts(); + let status = parts.status; + if !status.is_success() { + return Err(status); + } + + let bytes = body + .collect() + .await + .map_err(|_| http::StatusCode::GATEWAY_TIMEOUT)? + .to_bytes(); + + Ok(File { bytes }) + } +} + +#[async_trait::async_trait] +impl BackendNetwork for ProxyService { + async fn http_get(&self, url: &url::Url) -> Result { + self.fetch(DownloadRequest { url: url.clone() }) + .await + .map(|f| f.bytes) + .map_err(|e| BackendError::Network(e.to_string())) + } +} diff --git a/crates/tesor/src/storage.rs b/crates/tesor/src/storage.rs new file mode 100644 index 0000000..bc751a7 --- /dev/null +++ b/crates/tesor/src/storage.rs @@ -0,0 +1,1203 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +// This class stores uploaded files and associated metadata. +// Files are immutable but can be deleted. +// +// A unique file is defined by a `scope` and a `filename`. +// The filename can be any valid UTF-8 string up to 1KB +// in size and does not have to be a valid filesystem name. +// +// The list of files and their metadata are stored in a single +// SQLite database table. Small files are stored in a BLOB column +// in SQLite; for larger files, a checksum is stored in the database, +// and the file itself is stored on disk. +// +// On disk new files are written in two steps: +// - the file is written to a temporary file in the same directory; +// - a transaction is opened and a new file entry is created +// - temp file is renamed to the final filename +// - the transaction is committed. +// +// This scheme guarantees that if a record exists in the table, the file is written. +// However if the server crashes after rename but before commit, an orphan file will remain on the disk. +// To combat this, when the server starts, we check that there is an entry +// in the database for each file on the disk, and we also delete all temporary (non-renamed) files. + +use std::path::{Path, PathBuf}; +use std::sync; + +use bytes::Bytes; +use crc32fast::Hasher as Crc32Hasher; +use sqlx::encode::{Encode, IsNull}; +use sqlx::error::BoxDynError; +use sqlx::{FromRow, Pool, Sqlite, query, query_as, query_scalar, sqlite}; +use thiserror::Error; +use tokio::fs; +use tokio::io::AsyncWriteExt; +use tracing::{debug, error, instrument, warn}; +use uuid::Uuid; + +use crate::config::ConfigService; +use repos::{BackendError, BackendStorage, RawRelease, RawReleaseFile, Release}; + +const SQLITE_POOL_SIZE: u32 = 16; +const INLINE_THRESHOLD: usize = 256 * 1024; // 256 KB + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Id(pub Uuid); + +impl std::ops::Deref for Id { + type Target = Uuid; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl sqlx::Type for Id { + fn type_info() -> sqlite::SqliteTypeInfo { + as sqlx::Type>::type_info() + } + + fn compatible(ty: &sqlite::SqliteTypeInfo) -> bool { + as sqlx::Type>::compatible(ty) + } +} + +impl<'r> sqlx::decode::Decode<'r, Sqlite> for Id { + fn decode( + value: sqlite::SqliteValueRef<'r>, + ) -> Result> { + let value: Vec = as sqlx::decode::Decode>::decode(value)?; + let uuid = Uuid::from_slice(&value)?; + Ok(Id(uuid)) + } +} + +impl<'q> Encode<'q, Sqlite> for Id { + fn encode_by_ref( + &self, + buf: &mut ::ArgumentBuffer<'q>, + ) -> Result { + let value = self.0.as_bytes().to_vec(); + as Encode>::encode(value, buf) + } +} + +#[derive(Debug, Clone)] +pub struct Blob(pub Bytes); + +impl std::ops::Deref for Blob { + type Target = Bytes; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl sqlx::Type for Blob { + fn type_info() -> sqlite::SqliteTypeInfo { + as sqlx::Type>::type_info() // BLOB + } + + fn compatible(ty: &sqlite::SqliteTypeInfo) -> bool { + as sqlx::Type>::compatible(ty) + } +} + +impl<'r> sqlx::decode::Decode<'r, Sqlite> for Blob { + fn decode( + value: sqlite::SqliteValueRef<'r>, + ) -> Result> { + let slice: &'r [u8] = <&'r [u8] as sqlx::decode::Decode>::decode(value)?; + Ok(Blob(Bytes::copy_from_slice(slice))) + } +} + +#[derive(Debug, Error)] +pub enum StorageError { + #[error("file already exists: {0}/{1}")] + AlreadyExists(String, String), + + #[error("blob integrity check failed")] + IntegrityError, + + #[error("blob file missing on disk: {0}")] + BlobNotFound(PathBuf), + + #[error("failed to connect index db: {0}")] + DbError(#[from] sqlx::Error), + + #[error("io error: {0}")] + IoError(#[from] std::io::Error), +} + +/// Cached file entry (datafiles table) +#[allow(unused)] +#[derive(Debug, Clone, FromRow)] +pub struct Object { + pub id: Id, + pub scope: String, + pub created_at: chrono::DateTime, + + pub file_name: String, + pub file_size: i64, + pub file_bytes: Blob, + pub inlined: bool, +} + +impl Object { + // You cannot change this ID, this will desynchronize the records in the database and the files on the disk. + const UUID_ROOT_NAMESPACE: Uuid = Uuid::from_bytes([ + 0x8b, 0x06, 0x3c, 0x4c, 0x6b, 0x5c, 0x4a, 0x8b, 0x92, 0x8f, 0x75, 0x8b, 0x0e, 0x63, 0xc3, + 0x5d, + ]); + + fn uuid(scope: &str, file: &str) -> Id { + let scope_ns = Uuid::new_v5(&Object::UUID_ROOT_NAMESPACE, scope.as_bytes()); + Id(Uuid::new_v5(&scope_ns, file.as_bytes())) + } + + fn hash(scope: &str, file: &str, blob: &[u8]) -> Vec { + let mut hasher = Crc32Hasher::new(); + + hasher.update(b"/"); + hasher.update(scope.as_bytes()); + + hasher.update(b"/"); + hasher.update(file.as_bytes()); + + hasher.update(b"/"); + hasher.update(blob); + hasher.update(b"\0"); + + hasher.finalize().to_be_bytes().to_vec() + } +} + +/// Builder for file query SQL +pub struct FileQuery<'a> { + backend: &'a str, + version: Option<&'a str>, + filename: Option<&'a str>, + meta_null: Option<&'a str>, +} + +impl<'a> FileQuery<'a> { + pub fn new(backend: &'a str) -> Self { + Self { + backend, + version: None, + filename: None, + meta_null: None, + } + } + + pub fn version(mut self, version: &'a str) -> Self { + self.version = Some(version); + self + } + + pub fn filename(mut self, filename: &'a str) -> Self { + self.filename = Some(filename); + self + } + + pub fn where_meta_null(mut self, field: &'a str) -> Self { + self.meta_null = Some(field); + self + } + + pub fn build_sql(&self) -> String { + let mut sql = String::from( + "SELECT backend, version, filename, checksum, size, os, arch, meta FROM files WHERE backend = ?1", + ); + let mut param_idx = 1; + + if self.version.is_some() { + param_idx += 1; + sql.push_str(&format!(" AND version = ?{}", param_idx)); + } + if self.filename.is_some() { + param_idx += 1; + sql.push_str(&format!(" AND filename = ?{}", param_idx)); + } + if let Some(field) = self.meta_null { + sql.push_str(&format!( + " AND (meta IS NULL OR json_extract(meta, '$.{}') IS NULL)", + field + )); + } + + sql + } +} + +struct FileSystem { + objects: PathBuf, + database: PathBuf, +} + +impl FileSystem { + fn new(root: &Path) -> Self { + Self { + objects: root.join("objects"), + database: root.join("index.sqlite"), + } + } + + fn database(&self) -> &Path { + &self.database + } + + fn objects_root(&self) -> &Path { + &self.objects + } + + fn object(&self, scope: &str, file: &str) -> PathBuf { + let id = Object::uuid(scope, file); + let hash_hex = hex::encode(id.0.as_bytes()); + self.objects_root() + .join(&hash_hex[0..2]) + .join(&hash_hex[2..4]) + .join(&hash_hex) + } + + async fn objects_walk(&self, mut f: F) -> Result<(), StorageError> + where + F: FnMut(PathBuf) -> Fut, + Fut: std::future::Future>, + { + if !self.objects.exists() { + return Ok(()); + } + + let mut stack = vec![self.objects.to_path_buf()]; + while let Some(dir) = stack.pop() { + let mut entries = fs::read_dir(&dir).await?; + while let Some(entry) = entries.next_entry().await? { + let path = entry.path(); + let file_type = entry.file_type().await?; + + if file_type.is_dir() { + stack.push(path); + } else if file_type.is_file() { + f(path).await?; + } + } + } + + Ok(()) + } +} + +pub struct StorageService { + blobfs: FileSystem, + sqlite: Pool, +} + +impl StorageService { + pub async fn new(config: sync::Arc) -> Result { + let blobfs = FileSystem::new(config.dirname()); + + let connection = format!("sqlite:{}?mode=rwc", blobfs.database().to_str().unwrap()); + let sqlite: Pool = sqlite::SqlitePoolOptions::new() + .max_connections(SQLITE_POOL_SIZE) + .after_connect(|conn, _meta| { + Box::pin(async move { + // Connection-specific PRAGMAs (must be set on each connection) + sqlx::query("PRAGMA foreign_keys = ON;") + .execute(&mut *conn) + .await?; + sqlx::query("PRAGMA busy_timeout = 5000;") + .execute(&mut *conn) + .await?; + Ok(()) + }) + }) + .connect(&connection) + .await?; + + // WAL mode is database-wide and persists, only needs to be set once + query("PRAGMA journal_mode = WAL;").execute(&sqlite).await?; + + let storage = Self { sqlite, blobfs }; + storage.migrations().await?; + storage.doctor().await?; + + Ok(storage) + } + + /// Synchronously traverses the tree and removes temporary files. + /// Must run before the application starts. + async fn doctor(&self) -> Result<(), StorageError> { + let result = self.blobfs.objects_walk(|path| async move { + let name = match path.file_name().and_then(|name| name.to_str()) { + Some(name) => name.to_string(), + None => { + warn!(path = %path.display(), "cleanup: invalid blob name"); + let _ = fs::remove_file(path).await; + return Ok(()); + } + }; + + if name.ends_with(".part") { + warn!(path = %path.display(), "cleanup: removing temp file"); + let _ = fs::remove_file(path).await; + return Ok(()); + } + + if name.len() != 32 { + warn!(path = %path.display(), "cleanup: invalid blob name length"); + let _ = fs::remove_file(path).await; + return Ok(()); + } + + let id_bytes = match hex::decode(&name) { + Ok(bytes) => bytes, + Err(_) => { + warn!(path = %path.display(), "cleanup: invalid blob name hex"); + let _ = fs::remove_file(path).await; + return Ok(()); + } + }; + + let id = match Uuid::from_slice(&id_bytes) { + Ok(uuid) => Id(uuid), + Err(_) => { + warn!(path = %path.display(), "cleanup: invalid blob uuid"); + let _ = fs::remove_file(path).await; + return Ok(()); + } + }; + + let exists: Option = query_scalar("SELECT 1 FROM datafiles WHERE id = ?1") + .bind(id) + .fetch_optional(&self.sqlite) + .await?; + + if exists.is_none() { + warn!(path = %path.display(), "cleanup: removing orphan blob"); + let _ = fs::remove_file(path).await; + } + + Ok(()) + }); + result.await?; + + Ok(()) + } + + async fn migrations(&self) -> Result<(), StorageError> { + // Object storage table + query( + "CREATE TABLE IF NOT EXISTS datafiles( + id BLOB PRIMARY KEY CHECK (length(id) = 16), + scope TEXT NOT NULL, + created_at TEXT DEFAULT (datetime('now')), + file_name TEXT NOT NULL, + file_size INTEGER NOT NULL, + file_bytes BLOB, + inlined INTEGER NOT NULL, + UNIQUE (scope, file_name) + ) STRICT", + ) + .execute(&self.sqlite) + .await?; + + // Drop old backend-specific tables (data will be re-fetched from upstream) + query("DROP TABLE IF EXISTS go_files") + .execute(&self.sqlite) + .await?; + query("DROP TABLE IF EXISTS go_versions") + .execute(&self.sqlite) + .await?; + query("DROP TABLE IF EXISTS zig_files") + .execute(&self.sqlite) + .await?; + query("DROP TABLE IF EXISTS zig_versions") + .execute(&self.sqlite) + .await?; + + // Unified versions table + query( + "CREATE TABLE IF NOT EXISTS versions ( + backend TEXT NOT NULL, + version TEXT NOT NULL, + sort_key INTEGER NOT NULL, + meta BLOB CHECK (meta IS NULL OR (json_valid(meta) AND json_type(meta) = 'object')), + PRIMARY KEY (backend, version) + ) STRICT", + ) + .execute(&self.sqlite) + .await?; + + // Unified files table + query( + "CREATE TABLE IF NOT EXISTS files ( + backend TEXT NOT NULL, + version TEXT NOT NULL, + filename TEXT NOT NULL, + checksum TEXT NOT NULL, + size INTEGER NOT NULL, + os TEXT, + arch TEXT, + meta BLOB CHECK (meta IS NULL OR (json_valid(meta) AND json_type(meta) = 'object')), + PRIMARY KEY (backend, version, filename), + FOREIGN KEY (backend, version) REFERENCES versions(backend, version) + ) STRICT", + ) + .execute(&self.sqlite) + .await?; + + // Index for fast filename lookups + query("CREATE INDEX IF NOT EXISTS idx_files_backend_filename ON files(backend, filename)") + .execute(&self.sqlite) + .await?; + + Ok(()) + } + + #[instrument(skip(self))] + pub async fn get(&self, scope: &str, filename: &str) -> Result, StorageError> { + let file: Option = + query_as("SELECT * FROM datafiles WHERE scope = ?1 AND file_name = ?2") + .bind(scope) + .bind(filename) + .fetch_optional(&self.sqlite) + .await?; + + match file { + None => { + debug!("get: file not found"); + Ok(None) + } + Some(mut file) if !file.inlined => { + let obj = self.blobfs.object(scope, filename); + let bytes = match fs::read(&obj).await { + Ok(b) => Bytes::from(b), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Err(StorageError::BlobNotFound(obj)); + } + Err(e) => return Err(e.into()), + }; + + let hash = Object::hash(scope, filename, &bytes); + if hash != file.file_bytes.to_vec() { + return Err(StorageError::IntegrityError); + } + + file.file_bytes = Blob(bytes); + debug!(size = file.file_size, "get: loaded from disk"); + Ok(Some(file)) + } + Some(file) => { + debug!(size = file.file_size, "get: loaded inline"); + Ok(Some(file)) + } + } + } + + #[instrument(skip(self, bytes), fields(size = bytes.len()))] + pub async fn put( + &self, + scope: &str, + filename: &str, + bytes: &Bytes, + ) -> Result<(), StorageError> { + let inlined = bytes.len() <= INLINE_THRESHOLD; + + let obj = self.blobfs.object(scope, filename); + let tmp = obj.with_extension(format!("{}.part", Uuid::new_v4())); + + // write temp file + if !inlined { + if let Some(parent) = obj.parent() { + fs::create_dir_all(parent).await?; + } + + let mut file = fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(&tmp) + .await?; + + file.write_all(bytes).await?; + file.sync_all().await?; + } + + // bytes for small files or hash for large ones + let payload: Vec = if inlined { + bytes.to_vec() + } else { + Object::hash(scope, filename, bytes) + }; + + let result = async { + let mut tx = self.sqlite.begin().await?; + let id = Object::uuid(scope, filename); + + let result = query( + " + INSERT INTO datafiles ( + id, scope, file_name, file_size, file_bytes, inlined + ) VALUES ( + ?1, ?2, ?3, ?4, ?5, ?6 + ); + ", + ) + .bind(id) + .bind(scope) + .bind(filename) + .bind(bytes.len() as i64) + .bind(&payload) + .bind(inlined) + .execute(tx.as_mut()) + .await; + + match result { + Ok(_) => { + if !inlined { + fs::rename(&tmp, &obj).await?; + if let Some(parent) = obj.parent() { + let dir = fs::File::open(parent).await?; + dir.sync_all().await?; + } + } + + tx.commit().await?; + debug!("put: a new file has been commited"); + Ok(()) + } + Err(sqlx::Error::Database(ref db_err)) if db_err.is_unique_violation() => { + let existing: Option = + query_as("SELECT * FROM datafiles WHERE scope = ?1 AND file_name = ?2") + .bind(scope) + .bind(filename) + .fetch_optional(tx.as_mut()) + .await?; + + match existing { + Some(file) if file.file_bytes.to_vec() == payload => { + if !inlined { + let _ = fs::remove_file(&tmp).await; + tx.rollback().await?; + } + + debug!("put: identical file already exists"); + return Ok(()); + } + Some(_) => { + warn!("put: file already exists with different content"); + } + None => { + // unique_violation but row doesn't exist - shouldn't happen + warn!("corrupted! 'is_unique_violation' received, but data cannot be selected"); + } + } + + Err(StorageError::AlreadyExists( + scope.to_string(), + filename.to_string(), + )) + } + Err(e) => Err(e.into()), + } + }.await; + + if result.is_err() && !inlined { + let _ = fs::remove_file(&tmp).await; + } + + result + } + + /// Insert or update releases with their files in a single transaction. + pub async fn insert_releases( + &self, + releases: &[(RawRelease, Vec)], + ) -> Result<(), StorageError> { + for (release, files) in releases { + let mut tx = match self.sqlite.begin().await { + Ok(tx) => tx, + Err(e) => { + error!( + backend = release.backend, + version = release.version, + "failed to start transaction: {e}" + ); + return Err(e.into()); + } + }; + + let meta_json = release + .meta + .as_ref() + .map(|m| serde_json::to_vec(m).unwrap()); + + let result = async { + // Insert/update version + query( + "INSERT INTO versions (backend, version, sort_key, meta) + VALUES (?1, ?2, ?3, ?4) + ON CONFLICT(backend, version) DO UPDATE SET + sort_key = excluded.sort_key, + meta = excluded.meta + WHERE sort_key IS NOT excluded.sort_key OR meta IS NOT excluded.meta", + ) + .bind(&release.backend) + .bind(&release.version) + .bind(release.sort_key) + .bind(meta_json.as_deref()) + .execute(&mut *tx) + .await?; + + // Insert/update files + for file in files { + let changed = + Self::insert_file(&mut tx, &release.backend, &release.version, file) + .await?; + if changed { + warn!( + backend = release.backend, + version = release.version, + filename = file.filename, + "index file changed" + ); + } + } + + Ok::<(), StorageError>(()) + } + .await; + + match result { + Ok(()) => { + if let Err(e) = tx.commit().await { + error!( + backend = release.backend, + version = release.version, + "failed to commit release: {e}" + ); + } + } + Err(e) => { + error!( + backend = release.backend, + version = release.version, + "failed to store release, skipping: {e}" + ); + let _ = tx.rollback().await; + } + } + } + + Ok(()) + } + + /// Insert or update a single file within a transaction. + /// Returns `true` if an existing file was modified. + async fn insert_file( + tx: &mut sqlx::Transaction<'_, Sqlite>, + backend: &str, + version: &str, + file: &RawReleaseFile, + ) -> Result { + let meta_json = file.meta.as_ref().map(|m| serde_json::to_vec(m).unwrap()); + let os_str = file.os.as_ref().map(AsRef::as_ref); + let arch_str = file.arch.as_ref().map(AsRef::as_ref); + + let existed: Option<(i32,)> = + query_as("SELECT 1 FROM files WHERE backend = ?1 AND version = ?2 AND filename = ?3") + .bind(backend) + .bind(version) + .bind(&file.filename) + .fetch_optional(&mut **tx) + .await?; + + let changed: Option<(i32,)> = query_as( + "INSERT INTO files (backend, version, filename, checksum, size, os, arch, meta) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) + ON CONFLICT(backend, version, filename) DO UPDATE SET + checksum = excluded.checksum, + size = excluded.size, + os = excluded.os, + arch = excluded.arch + WHERE checksum IS NOT excluded.checksum + OR size IS NOT excluded.size + OR os IS NOT excluded.os + OR arch IS NOT excluded.arch + RETURNING 1", + ) + .bind(backend) + .bind(version) + .bind(&file.filename) + .bind(&file.checksum) + .bind(file.size) + .bind(os_str) + .bind(arch_str) + .bind(meta_json.as_deref()) + .fetch_optional(&mut **tx) + .await?; + + Ok(existed.is_some() && changed.is_some()) + } + + /// Execute a file query. + pub async fn query_files_filtered( + &self, + q: FileQuery<'_>, + ) -> Result, StorageError> { + let sql = q.build_sql(); + + let mut query = sqlx::query_as::< + _, + ( + String, + String, + String, + String, + i64, + Option, + Option, + Option>, + ), + >(&sql) + .bind(q.backend); + + if let Some(v) = q.version { + query = query.bind(v); + } + if let Some(f) = q.filename { + query = query.bind(f); + } + + let rows = query.fetch_all(&self.sqlite).await?; + + rows.into_iter() + .map( + |(backend, version, filename, checksum, size, os_str, arch_str, meta_bytes)| { + let meta = meta_bytes + .map(|b| serde_json::from_slice(&b)) + .transpose() + .map_err(|e| StorageError::DbError(sqlx::Error::Decode(Box::new(e))))?; + + Ok(RawReleaseFile { + backend, + version, + filename, + checksum, + size, + os: os_str.and_then(|s| s.parse().ok()), + arch: arch_str.and_then(|s| s.parse().ok()), + meta, + }) + }, + ) + .collect() + } + + /// Query all versions for a backend, ordered by sort_key. + pub async fn query_releases(&self, backend: &str) -> Result, StorageError> { + let rows = query_as::<_, (String, String, i64, Option>)>( + "SELECT backend, version, sort_key, meta FROM versions WHERE backend = ?1 ORDER BY sort_key", + ) + .bind(backend) + .fetch_all(&self.sqlite) + .await?; + + rows.into_iter() + .map(|(backend, version, sort_key, meta_bytes)| { + let meta = meta_bytes + .map(|b| serde_json::from_slice(&b)) + .transpose() + .map_err(|e| StorageError::DbError(sqlx::Error::Decode(Box::new(e))))?; + + Ok(Release { + backend, + version, + sort_key, + meta, + }) + }) + .collect() + } + + /// Update file metadata (e.g. after fetching a signature). + pub async fn update_file_meta(&self, file: &RawReleaseFile) -> Result { + let meta_json = file.meta.as_ref().map(|m| serde_json::to_vec(m).unwrap()); + + let result = query( + "UPDATE files SET meta = ?1 + WHERE backend = ?2 AND version = ?3 AND filename = ?4", + ) + .bind(meta_json.as_deref()) + .bind(&file.backend) + .bind(&file.version) + .bind(&file.filename) + .execute(&self.sqlite) + .await?; + + Ok(result.rows_affected() > 0) + } +} + +#[async_trait::async_trait] +impl BackendStorage for StorageService { + async fn insert_releases( + &self, + releases: &[(RawRelease, Vec)], + ) -> Result<(), BackendError> { + StorageService::insert_releases(self, releases) + .await + .map_err(|e| BackendError::Storage(e.to_string())) + } + + async fn query_releases(&self, backend: &str) -> Result, BackendError> { + StorageService::query_releases(self, backend) + .await + .map_err(|e| BackendError::Storage(e.to_string())) + } + + async fn query_files( + &self, + backend: &str, + version: Option<&str>, + filename: Option<&str>, + meta_null_field: Option<&str>, + ) -> Result, BackendError> { + let mut q = FileQuery::new(backend); + if let Some(v) = version { + q = q.version(v); + } + if let Some(f) = filename { + q = q.filename(f); + } + if let Some(field) = meta_null_field { + q = q.where_meta_null(field); + } + self.query_files_filtered(q) + .await + .map_err(|e| BackendError::Storage(e.to_string())) + } + + async fn update_file_meta(&self, file: &RawReleaseFile) -> Result { + StorageService::update_file_meta(self, file) + .await + .map_err(|e| BackendError::Storage(e.to_string())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + const BIG_FILE_SIZE: usize = INLINE_THRESHOLD * 64; + + async fn create_storage_at(path: &Path) -> StorageService { + let config = sync::Arc::new(ConfigService::for_test(path.to_path_buf())); + StorageService::new(config).await.unwrap() + } + + async fn create_test_storage() -> (StorageService, TempDir) { + let tmp = TempDir::new().unwrap(); + let storage = create_storage_at(tmp.path()).await; + (storage, tmp) + } + + /// Count files in a directory recursively + fn count_files(dir: &Path, ext: Option<&str>) -> usize { + let mut count = 0; + if let Ok(entries) = std::fs::read_dir(dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_dir() { + count += count_files(&path, ext); + } else if let Some(extension) = ext { + if path.extension().is_some_and(|ext| ext == extension) { + count += 1; + } + } else { + count += 1; + } + } + } + count + } + + /// Recursively delete or corrupt all files in a directory + fn damage_blobs(dir: &Path, corrupt: bool) { + if let Ok(entries) = std::fs::read_dir(dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_dir() { + damage_blobs(&path, corrupt); + } else if corrupt { + if let Ok(meta) = path.metadata() { + let _ = std::fs::write(&path, vec![0x00; meta.len() as usize]); + } + } else { + let _ = std::fs::remove_file(&path); + } + } + } + } + + #[tokio::test] + async fn test_write_read_small_file() { + let (storage, _tmp) = create_test_storage().await; + + // 1 KB file - should be stored inline + let data = Bytes::from(vec![0xAB; 1024]); + storage.put("test-scope", "small.bin", &data).await.unwrap(); + + let file = storage.get("test-scope", "small.bin").await.unwrap(); + assert!(file.is_some()); + + let file = file.unwrap(); + assert!(file.inlined); + assert_eq!(file.file_bytes.0, data); + assert_eq!(file.file_size, 1024); + } + + #[tokio::test] + async fn test_write_read_large_file() { + let (storage, tmp) = create_test_storage().await; + + // Large file - should be stored on disk + let data = Bytes::from(vec![0xCD; BIG_FILE_SIZE]); + storage.put("test-scope", "large.bin", &data).await.unwrap(); + + let file = storage.get("test-scope", "large.bin").await.unwrap(); + assert!(file.is_some()); + + let file = file.unwrap(); + assert!(!file.inlined); + assert_eq!(file.file_bytes.0, data); + assert_eq!(file.file_size, (BIG_FILE_SIZE) as i64); + + // Verify file is stored on disk, not inline in SQLite + let db_size = std::fs::metadata(tmp.path().join("index.sqlite")) + .unwrap() + .len(); + assert!( + db_size < data.len() as u64, + "database ({db_size} bytes) should be smaller than file ({} bytes)", + data.len() + ); + } + + #[tokio::test] + async fn test_write_read_boundary_file() { + let (storage, _tmp) = create_test_storage().await; + + // Exactly 256 KB - should be stored inline (threshold is <=) + let data = Bytes::from(vec![0xEF; INLINE_THRESHOLD]); + storage + .put("test-scope", "boundary.bin", &data) + .await + .unwrap(); + + let file = storage.get("test-scope", "boundary.bin").await.unwrap(); + assert!(file.is_some()); + + let file = file.unwrap(); + assert!(file.inlined); + assert_eq!(file.file_bytes.0, data); + assert_eq!(file.file_size, INLINE_THRESHOLD as i64); + } + + #[tokio::test] + async fn test_read_nonexistent_file() { + let (storage, _tmp) = create_test_storage().await; + + let file = storage.get("test-scope", "nonexistent.bin").await.unwrap(); + assert!(file.is_none()); + } + + #[tokio::test] + async fn test_persistence_after_restart() { + let tmp = TempDir::new().unwrap(); + + let small_data = Bytes::from(vec![0x11; 1024]); + let large_data = Bytes::from(vec![0x22; BIG_FILE_SIZE]); + + // First "session": write files + { + let storage = create_storage_at(tmp.path()).await; + storage + .put("persist", "small.bin", &small_data) + .await + .unwrap(); + storage + .put("persist", "large.bin", &large_data) + .await + .unwrap(); + // storage is dropped here, simulating shutdown + } + + // Second "session": verify files persist + { + let storage = create_storage_at(tmp.path()).await; + + let small = storage.get("persist", "small.bin").await.unwrap(); + assert!(small.is_some()); + assert_eq!(small.unwrap().file_bytes.0, small_data); + + let large = storage.get("persist", "large.bin").await.unwrap(); + assert!(large.is_some()); + assert_eq!(large.unwrap().file_bytes.0, large_data); + } + } + + #[tokio::test] + async fn test_error_blob_not_found() { + let (storage, tmp) = create_test_storage().await; + + // Write a large file (stored on disk) + let data = Bytes::from(vec![0xAA; BIG_FILE_SIZE]); + storage + .put("test-scope", "to-delete.bin", &data) + .await + .unwrap(); + + // Delete all blob files from disk + damage_blobs(&tmp.path().join("objects"), false); + + // Reading should fail with BlobNotFound + let result = storage.get("test-scope", "to-delete.bin").await; + assert!(matches!(result, Err(StorageError::BlobNotFound(_)))); + } + + #[tokio::test] + async fn test_error_blob_corrupted() { + let (storage, tmp) = create_test_storage().await; + + // Write a large file (stored on disk) + let data = Bytes::from(vec![0xBB; BIG_FILE_SIZE]); + storage + .put("test-scope", "to-corrupt.bin", &data) + .await + .unwrap(); + + // Corrupt all blob files on disk + damage_blobs(&tmp.path().join("objects"), true); + + // Reading should fail with IntegrityError + let result = storage.get("test-scope", "to-corrupt.bin").await; + assert!(matches!(result, Err(StorageError::IntegrityError))); + } + + #[tokio::test] + async fn test_duplicate_put_same_content() { + let (storage, tmp) = create_test_storage().await; + + // Write a large file + let data = Bytes::from(vec![0xCC; BIG_FILE_SIZE]); + storage.put("test-scope", "dup.bin", &data).await.unwrap(); + + // Write the same file again with identical content - should succeed + let result = storage.put("test-scope", "dup.bin", &data).await; + assert!(result.is_ok()); + + // Verify no temp files left behind + assert_eq!(count_files(&tmp.path().join("objects"), Some("part")), 0); + + // Verify file is still readable with correct content + let file = storage.get("test-scope", "dup.bin").await.unwrap(); + assert!(file.is_some()); + assert_eq!(file.unwrap().file_bytes.0, data); + } + + #[tokio::test] + async fn test_duplicate_put_different_content() { + let (storage, tmp) = create_test_storage().await; + + // Write a large file + let data1 = Bytes::from(vec![0xDD; BIG_FILE_SIZE]); + storage.put("test-scope", "dup.bin", &data1).await.unwrap(); + + // Write the same filename with different content - should fail + let data2 = Bytes::from(vec![0xEE; BIG_FILE_SIZE]); + let result = storage.put("test-scope", "dup.bin", &data2).await; + assert!(matches!(result, Err(StorageError::AlreadyExists(_, _)))); + + // Verify no temp files left behind + assert_eq!(count_files(&tmp.path().join("objects"), Some("part")), 0); + + // Verify original file is still intact + let file = storage.get("test-scope", "dup.bin").await.unwrap(); + assert!(file.is_some()); + assert_eq!(file.unwrap().file_bytes.0, data1); + } + + #[tokio::test] + async fn test_doctor_removes_temp_files() { + let tmp = TempDir::new().unwrap(); + + // First session: write a large file + let data = Bytes::from(vec![0xAA; BIG_FILE_SIZE]); + { + let storage = create_storage_at(tmp.path()).await; + storage.put("test-scope", "valid.bin", &data).await.unwrap(); + } + + // Manually create temp files in the objects directory + let objects_dir = tmp.path().join("objects"); + std::fs::create_dir_all(objects_dir.join("ab/cd")).unwrap(); + std::fs::write(objects_dir.join("ab/cd/test.part"), b"temp1").unwrap(); + std::fs::write(objects_dir.join("ab/cd/another.12345.part"), b"temp2").unwrap(); + + assert_eq!(count_files(&objects_dir, Some("part")), 2); + + // Second session: doctor should clean up temp files + { + let storage = create_storage_at(tmp.path()).await; + + // Temp files should be gone + assert_eq!(count_files(&objects_dir, Some("part")), 0); + + // Valid file should still be readable + let file = storage.get("test-scope", "valid.bin").await.unwrap(); + assert!(file.is_some()); + assert_eq!(file.unwrap().file_bytes.0, data); + } + } + + #[tokio::test] + async fn test_doctor_removes_orphan_blobs() { + let tmp = TempDir::new().unwrap(); + + // First session: write a large file + let data = Bytes::from(vec![0xBB; BIG_FILE_SIZE]); + { + let storage = create_storage_at(tmp.path()).await; + storage.put("test-scope", "valid.bin", &data).await.unwrap(); + } + + // Count files before adding orphan + let objects_dir = tmp.path().join("objects"); + let files_before = count_files(&objects_dir, None); + assert_eq!(files_before, 1); // Only the valid blob + + // Manually create an orphan blob (valid hex name but no DB record) + std::fs::create_dir_all(objects_dir.join("de/ad")).unwrap(); + let orphan_name = "deadbeefdeadbeefdeadbeefdeadbeef"; // 32 hex chars + std::fs::write(objects_dir.join("de/ad").join(orphan_name), b"orphan").unwrap(); + + assert_eq!(count_files(&objects_dir, None), 2); + + // Second session: doctor should clean up orphan blob + { + let storage = create_storage_at(tmp.path()).await; + + // Only the valid file should remain + assert_eq!(count_files(&objects_dir, None), 1); + + // Valid file should still be readable + let file = storage.get("test-scope", "valid.bin").await.unwrap(); + assert!(file.is_some()); + assert_eq!(file.unwrap().file_bytes.0, data); + } + } +} diff --git a/crates/tesor/src/telemetry.rs b/crates/tesor/src/telemetry.rs new file mode 100644 index 0000000..9036924 --- /dev/null +++ b/crates/tesor/src/telemetry.rs @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2026 Nikolay Govorov +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::fmt::Debug; +use std::time::SystemTime; + +use opentelemetry::InstrumentationScope; +use opentelemetry::KeyValue; +use opentelemetry::logs::LogRecord as _; +use opentelemetry::trace::TracerProvider as _; +use opentelemetry_otlp::{ + LogExporter, Protocol, SpanExporter, WithExportConfig, WithHttpConfig, WithTonicConfig, +}; +use opentelemetry_sdk::Resource; +use opentelemetry_sdk::error::OTelSdkResult; +use opentelemetry_sdk::logs::{BatchLogProcessor, LogProcessor, SdkLogRecord, SdkLoggerProvider}; +use opentelemetry_sdk::trace::SdkTracerProvider; +use tonic::metadata::{MetadataKey, MetadataMap, MetadataValue}; +use tonic::transport::{Certificate, ClientTlsConfig, Identity}; +use tracing_subscriber::Layer; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; + +use crate::config::{LogLevel, OtelcolConfig, StdoutFormat, TelemetryConfig}; + +/// LogProcessor wrapper that sets timestamp from observed_timestamp. +/// +/// opentelemetry-appender-tracing intentionally doesn't set timestamp because +/// tracing doesn't provide it. This causes some backends to show epoch (1970-01-01). +/// See: https://github.com/open-telemetry/opentelemetry-rust/issues/1479 +#[derive(Debug)] +struct TimestampLogProcessor

(P); +impl LogProcessor for TimestampLogProcessor

{ + fn emit(&self, record: &mut SdkLogRecord, scope: &InstrumentationScope) { + if record.timestamp().is_none() { + let ts = record.observed_timestamp().unwrap_or_else(SystemTime::now); + record.set_timestamp(ts); + } + self.0.emit(record, scope); + } + + fn force_flush(&self) -> OTelSdkResult { + self.0.force_flush() + } + + fn shutdown(&self) -> OTelSdkResult { + self.0.shutdown() + } +} + +macro_rules! build_exporter { + ($Exporter:ident, $cfg:expr, $url:expr, $tls:expr, $path:expr) => { + if $cfg.endpoint.starts_with("http://") { + // with_endpoint() requires full path for HTTP protocol + $Exporter::builder() + .with_http() + .with_protocol(Protocol::HttpBinary) + .with_endpoint(format!("{}{}", $url, $path)) + .with_timeout($cfg.timeout) + .with_headers($cfg.headers.clone()) + .build() + .expect(concat!("failed to build ", stringify!($Exporter))) + } else if $cfg.endpoint.starts_with("grpc://") { + $Exporter::builder() + .with_tonic() + .with_endpoint($url) + .with_timeout($cfg.timeout) + .with_tls_config($tls) + .with_metadata(build_metadata($cfg)) + .build() + .expect(concat!("failed to build ", stringify!($Exporter))) + } else { + panic!( + "invalid OTLP endpoint: {}. Expected http:// or grpc://", + $cfg.endpoint + ) + } + }; +} + +fn build_metadata(cfg: &OtelcolConfig) -> MetadataMap { + let mut metadata = MetadataMap::new(); + for (key, value) in &cfg.headers { + if let (Ok(k), Ok(v)) = ( + key.parse::>(), + value.parse::>(), + ) { + metadata.insert(k, v); + } + } + metadata +} + +pub struct TelemetryService { + logger_provider: Option, + tracer_provider: Option, +} +impl Drop for TelemetryService { + fn drop(&mut self) { + self.shutdown(); + } +} +impl TelemetryService { + pub fn init(config: &TelemetryConfig, service_name: &str, service_version: &str) -> Self { + let env_filter = match std::env::var_os("TESOR_LOG") { + Some(val) => tracing_subscriber::EnvFilter::try_new(val.to_string_lossy()) + .expect("Invalid TESOR_LOG"), + None => tracing_subscriber::EnvFilter::new(Self::log_level_to_filter( + config.stdout.log_level, + )), + }; + + let mut tracer_provider = None; + let mut logger_provider = None; + + let stdout = config + .stdout + .enabled + .then(|| match config.stdout.log_format { + StdoutFormat::Json => tracing_subscriber::fmt::layer().json().boxed(), + StdoutFormat::Pretty => tracing_subscriber::fmt::layer().pretty().boxed(), + }); + + let (otel_logs, otel_traces) = match &config.otelcol { + Some(cfg) if cfg.enabled => { + let tls = Self::build_tls(cfg); + let url = Self::parse_endpoint(&cfg.endpoint); + let resource = Resource::builder() + .with_service_name(service_name.to_string()) + .with_attribute(KeyValue::new( + opentelemetry_semantic_conventions::attribute::SERVICE_VERSION, + service_version.to_string(), + )) + .build(); + + let logs = cfg.logs.then(|| { + let exporter = + build_exporter!(LogExporter, cfg, url.clone(), tls.clone(), "/v1/logs"); + let provider = SdkLoggerProvider::builder() + .with_resource(resource.clone()) + .with_log_processor(TimestampLogProcessor( + BatchLogProcessor::builder(exporter).build(), + )) + .build(); + let layer = + opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new( + &provider, + ) + .with_filter(Self::to_level_filter(cfg.log_level)); + logger_provider = Some(provider); + layer + }); + + let traces = cfg.traces.then(|| { + let provider = SdkTracerProvider::builder() + .with_resource(resource.clone()) + .with_batch_exporter(build_exporter!( + SpanExporter, + cfg, + url.clone(), + tls.clone(), + "/v1/traces" + )) + .build(); + let layer = tracing_opentelemetry::layer() + .with_tracer(provider.tracer(service_name.to_string())); + tracer_provider = Some(provider); + layer + }); + + (logs, traces) + } + _ => (None, None), + }; + + tracing_subscriber::registry() + .with(stdout) + .with(otel_logs) + .with(otel_traces) + .with(env_filter) + .init(); + + Self { + tracer_provider, + logger_provider, + } + } + + fn parse_endpoint(endpoint: &str) -> String { + if endpoint.starts_with("http://") { + endpoint.to_string() + } else if let Some(rest) = endpoint.strip_prefix("grpc://") { + format!("https://{rest}") + } else { + panic!("invalid OTLP endpoint: {endpoint}. Expected http:// or grpc://") + } + } + + fn build_tls(cfg: &OtelcolConfig) -> ClientTlsConfig { + let mut tls = ClientTlsConfig::new().with_native_roots(); + if let Some(path) = &cfg.tls_ca { + tls = tls.ca_certificate(Certificate::from_pem( + std::fs::read_to_string(path).expect("failed to read CA"), + )); + } + if let (Some(crt), Some(key)) = (&cfg.tls_crt, &cfg.tls_key) { + tls = tls.identity(Identity::from_pem( + std::fs::read_to_string(crt).expect("failed to read cert"), + std::fs::read_to_string(key).expect("failed to read key"), + )); + } + tls + } + + fn to_level_filter(level: LogLevel) -> tracing_subscriber::filter::LevelFilter { + match level { + LogLevel::Trace => tracing_subscriber::filter::LevelFilter::TRACE, + LogLevel::Debug => tracing_subscriber::filter::LevelFilter::DEBUG, + LogLevel::Info => tracing_subscriber::filter::LevelFilter::INFO, + LogLevel::Warning => tracing_subscriber::filter::LevelFilter::WARN, + LogLevel::Error => tracing_subscriber::filter::LevelFilter::ERROR, + } + } + + fn log_level_to_filter(level: LogLevel) -> &'static str { + match level { + LogLevel::Trace => "trace", + LogLevel::Debug => "debug", + LogLevel::Info => "info", + LogLevel::Warning => "warn", + LogLevel::Error => "error", + } + } + + pub fn shutdown(&mut self) { + if let Some(p) = self.tracer_provider.take() + && let Err(e) = p.shutdown() + { + tracing::error!("failed to shutdown tracer provider: {e}"); + } + if let Some(p) = self.logger_provider.take() + && let Err(e) = p.shutdown() + { + tracing::error!("failed to shutdown logger provider: {e}"); + } + } +} diff --git a/deny.toml b/deny.toml index b0b87c8..3cd37da 100644 --- a/deny.toml +++ b/deny.toml @@ -25,7 +25,7 @@ exceptions = [ { crate = "base", allow = ["AGPL-3.0-or-later"] }, { crate = "repos", allow = ["AGPL-3.0-or-later"] }, { crate = "smoke", allow = ["AGPL-3.0-or-later"] }, - { crate = "recluse", allow = ["AGPL-3.0-or-later"] }, + { crate = "tesor", allow = ["AGPL-3.0-or-later"] }, ] unused-allowed-license = "allow" diff --git a/mise.toml b/mise.toml index 20e8d95..142594c 100644 --- a/mise.toml +++ b/mise.toml @@ -36,7 +36,7 @@ shellcheck = "0.11.0" [task_config] dir = "{{cwd}}" includes = [ - "git::https://github.com/dimidiumlabs/platform.git//tasks?ref=159ce265b95d655342c378d9aaa7dc14dbf1dfa4", + "git::https://git.dimidiumlabs.io/platform.git//tasks?ref=159ce265b95d655342c378d9aaa7dc14dbf1dfa4", ] [tasks.fmt] diff --git a/nfpm.yaml b/nfpm.yaml index c53abb5..5704fcb 100644 --- a/nfpm.yaml +++ b/nfpm.yaml @@ -4,7 +4,7 @@ # yaml-language-server: $schema=https://nfpm.goreleaser.com/schema.json # vim: set ts=2 sw=2 tw=0 fo=cnqoj -name: recluse +name: tesor arch: ${ARCH} version: ${VERSION} license: AGPL-3.0-or-later @@ -14,34 +14,34 @@ description: Tiny packages caching proxy contents: - src: ./LICENSE - dst: /usr/share/doc/recluse/LICENSE + dst: /usr/share/doc/tesor/LICENSE - src: ./README.md - dst: /usr/share/doc/recluse/README.md + dst: /usr/share/doc/tesor/README.md - - src: target/release/recluse - dst: /usr/local/bin/recluse + - src: target/release/tesor + dst: /usr/local/bin/tesor file_info: mode: 0755 - - src: pkg/recluse.toml - dst: /etc/recluse.toml + - src: pkg/tesor.toml + dst: /etc/tesor.toml type: config|noreplace file_info: mode: 0640 owner: root - group: recluse + group: tesor - - src: pkg/recluse.service - dst: /usr/lib/systemd/system/recluse.service + - src: pkg/tesor.service + dst: /usr/lib/systemd/system/tesor.service file_info: mode: 0644 - - dst: /var/lib/recluse + - dst: /var/lib/tesor type: dir file_info: mode: 0755 owner: root - group: recluse + group: tesor scripts: diff --git a/pkg/recluse.service b/pkg/recluse.service deleted file mode 100644 index f3ab476..0000000 --- a/pkg/recluse.service +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -[Unit] -Description=recluse server (tiny packages caching proxy) -Requires=network-online.target -After=time-sync.target network-online.target remote-fs.target nss-lookup.target -Wants=time-sync.target - -[Service] -Type=notify -User=recluse -Group=recluse -Restart=always -RestartSec=30 -WatchdogSec=30 -NotifyAccess=main -ExecPaths=/usr/local/bin/recluse /usr/lib -ExecStart=/usr/local/bin/recluse --config=/etc/recluse.toml -LimitCORE=infinity -LimitNOFILE=500000 -AmbientCapabilities=CAP_NET_BIND_SERVICE - -# %p is resolved to the systemd unit name -LogsDirectory=%p -StateDirectory=%p -CacheDirectory=%p -RuntimeDirectory=%p - -UMask=0077 -LockPersonality=yes -NoNewPrivileges=yes -PrivateDevices=yes -PrivateTmp=true -ProcSubset=pid -ProtectClock=yes -ProtectControlGroups=yes -ProtectHome=yes -ProtectHostname=yes -ProtectKernelLogs=yes -ProtectKernelModules=yes -ProtectKernelTunables=yes -ProtectProc=invisible -ProtectSystem=strict -RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX -RestrictNamespaces=yes -RestrictSUIDSGID=yes - -[Install] -# service should not start from the rescue shell (rescue.target). -WantedBy=multi-user.target diff --git a/pkg/recluse.toml b/pkg/recluse.toml deleted file mode 100644 index 751672b..0000000 --- a/pkg/recluse.toml +++ /dev/null @@ -1,59 +0,0 @@ -# SPDX-FileCopyrightText: 2026 Nikolay Govorov -# SPDX-License-Identifier: AGPL-3.0-or-later - -# recluse will dutifully indicate its user agent and protocols (for example, source for zig). -# Specify your name if you want your instance to be identified. -appname="recluse" - -# Path to the directory for storing state (indexes, caches, statistics). -# Must be a writable directory. -dirname="/var/lib/recluse" - -# Common server settings -[server] -shutdown_timeout = 60 # seconds -request_timeout = 30 # seconds -max_body_size = "64 MiB" -max_concurrent_requests = 512 -rate_limit_period = 10 -rate_limit_burst_size = 50 - -# Listeners configuration. Each listener has an address and optional hostnames. -# Empty hostnames means accept all requests (catch-all). -[[listen]] -addr = "0.0.0.0:2025" -hostnames = ["localhost", "127.0.0.1", "::1"] - -# For HTTPS, set tls_cert and tls_key to PEM file paths. -# tls_crt = "/etc/recluse/cert.pem" -# tls_key = "/etc/recluse/key.pem" - -# Telemetry configuration -# Logs to stdout (pretty or json format) -[telemetry.stdout] -enabled = true -log_level = "info" # trace, debug, info, warning, error -log_format = "pretty" # pretty, json - -# OpenTelemetry Collector (OTLP) -# Uncomment to enable sending logs and traces to an OTLP collector -# [telemetry.otelcol] -# enabled = true -# logs = true -# traces = true -# endpoint = "http://localhost:4318" # HTTP protocol (no TLS) -# # endpoint = "grpc://localhost:4317" # gRPC protocol (requires TLS) -# timeout = 10 # seconds -# log_level = "info" -# -# # For gRPC with TLS, specify certificates: -# # tls_ca = "/etc/recluse/otel-ca.crt" -# # tls_crt = "/etc/recluse/otel-client.crt" # for mTLS -# # tls_key = "/etc/recluse/otel-client.key" # for mTLS -# -# # HTTP headers for authentication: -# # [telemetry.otelcol.headers] -# # Authorization = "Bearer " - -# Environment variable RECLUSE_LOG overrides log_level settings. -# Example: RECLUSE_LOG=debug,sqlx=warn diff --git a/pkg/scripts/postinstall.sh b/pkg/scripts/postinstall.sh index 204f44a..6e391fc 100644 --- a/pkg/scripts/postinstall.sh +++ b/pkg/scripts/postinstall.sh @@ -4,7 +4,7 @@ set -e -if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ] && [ -f /usr/lib/systemd/system/recluse.service ]; then +if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ] && [ -f /usr/lib/systemd/system/tesor.service ]; then /bin/systemctl daemon-reload - /bin/systemctl enable recluse + /bin/systemctl enable tesor fi diff --git a/pkg/scripts/preinstall.sh b/pkg/scripts/preinstall.sh index 3330200..175eaf1 100644 --- a/pkg/scripts/preinstall.sh +++ b/pkg/scripts/preinstall.sh @@ -4,13 +4,13 @@ set -e -RECLUSE_USER=${RECLUSE_USER:-recluse} -RECLUSE_GROUP=${RECLUSE_GROUP:-${RECLUSE_USER}} +TESOR_USER=${TESOR_USER:-tesor} +TESOR_GROUP=${TESOR_GROUP:-${TESOR_USER}} -if ! getent group "$RECLUSE_GROUP" >/dev/null; then - groupadd --system "$RECLUSE_GROUP" +if ! getent group "$TESOR_GROUP" >/dev/null; then + groupadd --system "$TESOR_GROUP" fi -if ! getent passwd "$RECLUSE_USER" >/dev/null; then - useradd --system --gid "$RECLUSE_GROUP" --no-create-home --shell /usr/sbin/nologin "$RECLUSE_USER" +if ! getent passwd "$TESOR_USER" >/dev/null; then + useradd --system --gid "$TESOR_GROUP" --no-create-home --shell /usr/sbin/nologin "$TESOR_USER" fi diff --git a/pkg/scripts/preremove.sh b/pkg/scripts/preremove.sh index 3c16f62..9cd6599 100644 --- a/pkg/scripts/preremove.sh +++ b/pkg/scripts/preremove.sh @@ -4,7 +4,7 @@ set -e -if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ] && [ -f /usr/lib/systemd/system/recluse.service ]; then - /bin/systemctl stop recluse.service || true - /bin/systemctl disable recluse.service || true +if [ -x "/bin/systemctl" ] && [ -d /run/systemd/system ] && [ -f /usr/lib/systemd/system/tesor.service ]; then + /bin/systemctl stop tesor.service || true + /bin/systemctl disable tesor.service || true fi diff --git a/pkg/tesor.service b/pkg/tesor.service new file mode 100644 index 0000000..c2d463d --- /dev/null +++ b/pkg/tesor.service @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +[Unit] +Description=tesor server (tiny packages caching proxy) +Requires=network-online.target +After=time-sync.target network-online.target remote-fs.target nss-lookup.target +Wants=time-sync.target + +[Service] +Type=notify +User=tesor +Group=tesor +Restart=always +RestartSec=30 +WatchdogSec=30 +NotifyAccess=main +ExecPaths=/usr/local/bin/tesor /usr/lib +ExecStart=/usr/local/bin/tesor --config=/etc/tesor.toml +LimitCORE=infinity +LimitNOFILE=500000 +AmbientCapabilities=CAP_NET_BIND_SERVICE + +# %p is resolved to the systemd unit name +LogsDirectory=%p +StateDirectory=%p +CacheDirectory=%p +RuntimeDirectory=%p + +UMask=0077 +LockPersonality=yes +NoNewPrivileges=yes +PrivateDevices=yes +PrivateTmp=true +ProcSubset=pid +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectProc=invisible +ProtectSystem=strict +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX +RestrictNamespaces=yes +RestrictSUIDSGID=yes + +[Install] +# service should not start from the rescue shell (rescue.target). +WantedBy=multi-user.target diff --git a/pkg/tesor.toml b/pkg/tesor.toml new file mode 100644 index 0000000..7296086 --- /dev/null +++ b/pkg/tesor.toml @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2026 Nikolay Govorov +# SPDX-License-Identifier: AGPL-3.0-or-later + +# tesor will dutifully indicate its user agent and protocols (for example, source for zig). +# Specify your name if you want your instance to be identified. +appname="tesor" + +# Path to the directory for storing state (indexes, caches, statistics). +# Must be a writable directory. +dirname="/var/lib/tesor" + +# Common server settings +[server] +shutdown_timeout = 60 # seconds +request_timeout = 30 # seconds +max_body_size = "64 MiB" +max_concurrent_requests = 512 +rate_limit_period = 10 +rate_limit_burst_size = 50 + +# Listeners configuration. Each listener has an address and optional hostnames. +# Empty hostnames means accept all requests (catch-all). +[[listen]] +addr = "0.0.0.0:2025" +hostnames = ["localhost", "127.0.0.1", "::1"] + +# For HTTPS, set tls_cert and tls_key to PEM file paths. +# tls_crt = "/etc/tesor/cert.pem" +# tls_key = "/etc/tesor/key.pem" + +# Telemetry configuration +# Logs to stdout (pretty or json format) +[telemetry.stdout] +enabled = true +log_level = "info" # trace, debug, info, warning, error +log_format = "pretty" # pretty, json + +# OpenTelemetry Collector (OTLP) +# Uncomment to enable sending logs and traces to an OTLP collector +# [telemetry.otelcol] +# enabled = true +# logs = true +# traces = true +# endpoint = "http://localhost:4318" # HTTP protocol (no TLS) +# # endpoint = "grpc://localhost:4317" # gRPC protocol (requires TLS) +# timeout = 10 # seconds +# log_level = "info" +# +# # For gRPC with TLS, specify certificates: +# # tls_ca = "/etc/tesor/otel-ca.crt" +# # tls_crt = "/etc/tesor/otel-client.crt" # for mTLS +# # tls_key = "/etc/tesor/otel-client.key" # for mTLS +# +# # HTTP headers for authentication: +# # [telemetry.otelcol.headers] +# # Authorization = "Bearer " + +# Environment variable TESOR_LOG overrides log_level settings. +# Example: TESOR_LOG=debug,sqlx=warn diff --git a/tests/smoke/run-local.sh b/tests/smoke/run-local.sh index 22b5a72..4b72441 100755 --- a/tests/smoke/run-local.sh +++ b/tests/smoke/run-local.sh @@ -2,25 +2,25 @@ # SPDX-FileCopyrightText: 2026 Nikolay Govorov # SPDX-License-Identifier: AGPL-3.0-or-later -# Starts recluse in a temporary directory, runs smoke tests, cleans up. -# Expects a prebuilt binary at target/release/recluse (run `cargo build --release` first). +# Starts tesor in a temporary directory, runs smoke tests, cleans up. +# Expects a prebuilt binary at target/release/tesor (run `cargo build --release` first). # # Usage: # ./tests/smoke/run-local.sh # default port 2025 -# RECLUSE_PORT=9999 ./tests/smoke/run-local.sh +# TESOR_PORT=9999 ./tests/smoke/run-local.sh set -euo pipefail -BIN="target/release/recluse" -PORT="${RECLUSE_PORT:-2025}" +BIN="target/release/tesor" +PORT="${TESOR_PORT:-2025}" BASE_URL="http://127.0.0.1:${PORT}" TMPDIR="$(mktemp -d)" -LOGFILE="$TMPDIR/recluse.log" +LOGFILE="$TMPDIR/tesor.log" trap 'kill "$PID" 2>/dev/null; wait "$PID" 2>/dev/null; rm -rf "$TMPDIR"' EXIT # Write minimal config -cat > "$TMPDIR/recluse.toml" < "$TMPDIR/tesor.toml" <"$LOGFILE" 2>&1 & +"$BIN" --config="$TMPDIR/tesor.toml" >"$LOGFILE" 2>&1 & PID=$! # Wait for index to load (log shows "index refreshed" for each backend) @@ -58,7 +58,7 @@ for i in $(seq 1 120); do break fi if ! kill -0 "$PID" 2>/dev/null; then - echo "recluse exited unexpectedly. Server log:" + echo "tesor exited unexpectedly. Server log:" cat "$LOGFILE" exit 1 fi @@ -72,4 +72,4 @@ if ! grep -q "index refreshed" "$LOGFILE" 2>/dev/null; then fi # Run smoke tests -RECLUSE_URL="${BASE_URL}" cargo run -p smoke +TESOR_URL="${BASE_URL}" cargo run -p smoke diff --git a/tests/smoke/src/main.rs b/tests/smoke/src/main.rs index f23a233..ca109f9 100644 --- a/tests/smoke/src/main.rs +++ b/tests/smoke/src/main.rs @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: 2026 Nikolay Govorov // SPDX-License-Identifier: AGPL-3.0-or-later -// Black-box smoke tests for Recluse. +// Black-box smoke tests for Tesor. // -// Downloads archives from Recluse and upstream, compares sha256 hashes, +// Downloads archives from Tesor and upstream, compares sha256 hashes, // verifies that minisig signatures (Zig) and sha256 checksums (Go) match upstream. // // Usage: -// RECLUSE_URL=https://pkg.earth cargo run -p smoke +// TESOR_URL=https://pkg.earth cargo run -p smoke use sha2::{Digest, Sha256}; @@ -198,8 +198,8 @@ fn test_web(r: &mut Runner, base_url: &str) { r.fail(name, &format!("status {status}, expected 200")); } else if !ct.contains("text/html") { r.fail(name, &format!("content-type: {ct}")); - } else if !body.contains("Recluse") { - r.fail(name, "body missing \"Recluse\""); + } else if !body.contains("Tesor") { + r.fail(name, "body missing \"Tesor\""); } else { r.ok(name); } @@ -274,11 +274,11 @@ fn test_go(r: &mut Runner, base_url: &str) { r.fetch_bytes(&format!("{base_url}/go/{file}")), r.fetch_bytes(&format!("https://go.dev/dl/{file}")), ) { - (Ok(recluse), Ok(upstream)) => { - let zh = sha256hex(&recluse); + (Ok(tesor), Ok(upstream)) => { + let zh = sha256hex(&tesor); let uh = sha256hex(&upstream); if zh != uh { - r.fail(&name, &format!("recluse={zh} upstream={uh}")); + r.fail(&name, &format!("tesor={zh} upstream={uh}")); } else { r.ok(&name); } @@ -332,11 +332,11 @@ fn test_zig(r: &mut Runner, base_url: &str) { r.fetch_bytes(&format!("{base_url}/zig/{}", entry.file)), r.fetch_bytes(&zig_upstream_url(entry.file, entry.version)), ) { - (Ok(recluse), Ok(upstream)) => { - let zh = sha256hex(&recluse); + (Ok(tesor), Ok(upstream)) => { + let zh = sha256hex(&tesor); let uh = sha256hex(&upstream); if zh != uh { - r.fail(&name, &format!("recluse={zh} upstream={uh}")); + r.fail(&name, &format!("tesor={zh} upstream={uh}")); } else { r.ok(&name); } @@ -348,11 +348,11 @@ fn test_zig(r: &mut Runner, base_url: &str) { // Signature: compare .minisig with upstream { let name = format!("zig: {}.minisig matches upstream", entry.file); - let recluse_url = format!("{base_url}/zig/{}.minisig", entry.file); + let tesor_url = format!("{base_url}/zig/{}.minisig", entry.file); let upstream_url = format!("{}.minisig", zig_upstream_url(entry.file, entry.version)); - match (r.fetch_bytes(&recluse_url), r.fetch_bytes(&upstream_url)) { - (Ok(recluse), Ok(upstream)) => { - if recluse != upstream { + match (r.fetch_bytes(&tesor_url), r.fetch_bytes(&upstream_url)) { + (Ok(tesor), Ok(upstream)) => { + if tesor != upstream { r.fail(&name, "minisig content differs from upstream"); } else { r.ok(&name); @@ -374,11 +374,11 @@ fn test_zig(r: &mut Runner, base_url: &str) { } fn main() { - let base_url = match std::env::var("RECLUSE_URL") { + let base_url = match std::env::var("TESOR_URL") { Ok(url) => url, Err(_) => { - eprintln!("Error: RECLUSE_URL environment variable is not set."); - eprintln!("Usage: RECLUSE_URL=https://pkg.earth cargo run -p smoke"); + eprintln!("Error: TESOR_URL environment variable is not set."); + eprintln!("Usage: TESOR_URL=https://pkg.earth cargo run -p smoke"); std::process::exit(1); } }; -- Gilti