This document is the canonical security note for Brainarr releases. It describes the data flows, the threat model we design for, and the concrete controls we use in code and CI. A deeper, operations‑focused guide with examples lives in docs/SECURITY.md.
- Brainarr requires Lidarr 3.0.0.4855+ on the plugins/nightly branch. See README for exact wording and CI source of truth.
- Trust boundary: the plugin runs inside the Lidarr host process; Brainarr does not load or execute external code.
- Inputs: library metadata from Lidarr; user‑provided configuration (provider URLs, API keys, timeouts); optional network responses from configured AI providers.
- Outputs: network requests only to the configured provider(s) and standard logs to Lidarr’s logging sinks. No analytics or phone‑home calls are built in.
- Secrets: API keys live in Lidarr’s plugin configuration storage. Operators may also use environment variables or an external secret store (see docs/SECURITY.md). Brainarr never hard‑codes keys.
- Local‑first by default: Ollama and LM Studio operate without sending data off‑host.
- When using cloud providers, requests go over HTTPS using the platform TLS stack; the plugin does not disable certificate validation.
- Timeouts and retries are provider‑scoped; calls are budgeted so the UI stays responsive.
- Deterministic planning and stable ordering across platforms (Windows/Linux) to keep results reproducible.
- Cancellation tokens propagate through provider calls to avoid “zombie” work after timeouts.
- Input and output payloads are strictly JSON; serializers are configured to avoid unsafe polymorphic deserialization.
- Gitea is the authoritative CI surface (
.gitea/workflows/ci.yml). - The
lintjob runs Common's shared plugin lint runner, including version-contract and docs consistency checks. - The
verifyjob builds against real Lidarr assemblies extracted from the pinned plugins-branch Docker image and runs the deterministic test suite. - CodeQL, SBOM attachment, and Cosign signing are release/security enhancements to restore or run manually; they are not active PR gates in this repo's current Gitea-only workflow.
If you believe you've found a vulnerability, open a Security issue or email the maintainers via the GitHub security contact on the repository. Please do not file sensitive details in public issues.
Out-of-scope: vulnerabilities in third-party AI providers or models; issues in Lidarr itself; operator misconfiguration outside of Brainarr's documented settings.
For releases that publish verification artifacts, use:
Brainarr-<version>.zip— plugin packageBrainarr-<version>.zip.sha256— SHA‑256 checksumBrainarr-<version>.zip.sig— Sigstore Cosign signature (keyless)
Verify integrity and provenance:
Linux/macOS
VERSION=v1.3.2
curl -LO https://github.com/RicherTunes/Brainarr/releases/download/$VERSION/Brainarr-$VERSION.zip
curl -LO https://github.com/RicherTunes/Brainarr/releases/download/$VERSION/Brainarr-$VERSION.zip.sha256
curl -LO https://github.com/RicherTunes/Brainarr/releases/download/$VERSION/Brainarr-$VERSION.zip.sig
# 1) Checksum
sha256sum -c Brainarr-$VERSION.zip.sha256
# 2) Cosign keyless signature (certificate embedded in signature)
cosign verify-blob \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--certificate-identity-regexp "^https://github.com/RicherTunes/Brainarr/.+" \
--signature Brainarr-$VERSION.zip.sig \
Brainarr-$VERSION.zipWindows PowerShell
$Version = "v1.3.2"
Invoke-WebRequest -OutFile "Brainarr-$Version.zip" "https://github.com/RicherTunes/Brainarr/releases/download/$Version/Brainarr-$Version.zip"
Invoke-WebRequest -OutFile "Brainarr-$Version.zip.sha256" "https://github.com/RicherTunes/Brainarr/releases/download/$Version/Brainarr-$Version.zip.sha256"
Invoke-WebRequest -OutFile "Brainarr-$Version.zip.sig" "https://github.com/RicherTunes/Brainarr/releases/download/$Version/Brainarr-$Version.zip.sig"
# 1) Checksum
Get-FileHash "Brainarr-$Version.zip" -Algorithm SHA256 | ForEach-Object {
$expected = Get-Content "Brainarr-$Version.zip.sha256" | Select-Object -First 1
if ($_.Hash.ToLower() -ne $expected.Split(' ')[0].ToLower()) { throw "SHA256 mismatch" } else { "Checksum OK" }
}
# 2) Cosign keyless signature
cosign verify-blob `
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" `
--certificate-identity-regexp "^https://github.com/RicherTunes/Brainarr/.+" `
--signature "Brainarr-$Version.zip.sig" `
"Brainarr-$Version.zip"Explanation
- Historical signed releases used Sigstore "keyless" signing from GitHub Actions OIDC. Cosign validates against GitHub's issuer and requires the certificate identity to match this repository.
- No public keys to fetch or rotate; trust anchors are distributed by Sigstore/TUF and managed by Cosign.