Skip to content

Commit ebcc9cb

Browse files
authored
Merge pull request #35 from pullboxapp/develop
release: promote LibGen 1.0.0 and Anna's Archive 1.0.2
2 parents 376e78f + 52a13cc commit ebcc9cb

67 files changed

Lines changed: 6351 additions & 167 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/scripts/resolve-provider-release.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
r"(?:(?:a|b|rc)(?:0|[1-9][0-9]*))?"
1717
)
1818
TAG = re.compile(
19-
rf"(?P<provider>getcomics|annas-archive|synthetic)-v"
19+
rf"(?P<provider>getcomics|annas-archive|libgen|synthetic)-v"
2020
rf"(?P<version>{RELEASE_VERSION.pattern})"
2121
)
2222
OWNER = re.compile(r"[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?")
@@ -59,6 +59,13 @@ class ProviderRelease(NamedTuple):
5959
title="Pullbox Anna's Archive Direct Download Provider",
6060
description="Optional Anna's Archive discovery provider for Pullbox direct downloads",
6161
),
62+
"libgen": ProviderDefinition(
63+
dockerfile="docker/Dockerfile.libgen",
64+
version_file="providers/libgen/pyproject.toml",
65+
image_name="pullbox-provider-libgen",
66+
title="Pullbox Library Genesis Direct Download Provider",
67+
description="Optional LibGen discovery provider for Pullbox direct downloads",
68+
),
6269
"synthetic": ProviderDefinition(
6370
dockerfile="docker/Dockerfile.synthetic",
6471
version_file="pyproject.toml",

.github/scripts/select-latest-provider-release.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import re
99
from pathlib import Path
1010

11-
PROVIDERS = {"getcomics", "annas-archive", "synthetic"}
11+
PROVIDERS = {"getcomics", "annas-archive", "libgen", "synthetic"}
1212
STABLE_VERSION = r"(?P<major>0|[1-9][0-9]*)\.(?P<minor>0|[1-9][0-9]*)\.(?P<patch>0|[1-9][0-9]*)"
1313

1414

.github/security/container-vulnerability-baseline.json

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"schema_version": 1,
3-
"reviewed_on": "2026-08-23",
4-
"expires_on": "2026-11-01",
5-
"base_image": "python:3.14-slim@sha256:cea0e6040540fb2b965b6e7fb5ffa00871e632eef63719f0ea54bca189ce14a6",
3+
"reviewed_on": "2026-08-26",
4+
"expires_on": "2026-11-26",
5+
"base_image": "python:3.14-slim@sha256:83ff1d245a3d57d04152252d3ef9cb361494d0b3395abd65a5ebe91c401c8e83",
66
"images": {
77
"synthetic": "python-3.14-slim",
88
"getcomics": "python-3.14-slim",
9-
"annas-archive": "python-3.14-slim"
9+
"annas-archive": "python-3.14-slim",
10+
"libgen": "python-3.14-slim"
1011
},
1112
"profiles": {
1213
"python-3.14-slim": [
@@ -88,24 +89,6 @@
8889
"severity": "High",
8990
"rationale": "Inherited from the pinned Python 3.14 Debian base; no supported-line fix is currently available."
9091
},
91-
{
92-
"id": "CVE-2026-11940",
93-
"package": "python",
94-
"severity": "High",
95-
"rationale": "Inherited from Python 3.14; the available fix is outside the supported Python line."
96-
},
97-
{
98-
"id": "CVE-2026-11972",
99-
"package": "python",
100-
"severity": "High",
101-
"rationale": "Inherited from Python 3.14; the available fix is outside the supported Python line."
102-
},
103-
{
104-
"id": "CVE-2026-15308",
105-
"package": "python",
106-
"severity": "High",
107-
"rationale": "Inherited from Python 3.14; the available fix is outside the supported Python line."
108-
},
10992
{
11093
"id": "CVE-2026-41992",
11194
"package": "gzip",
@@ -189,24 +172,6 @@
189172
"package": "perl-base",
190173
"severity": "High",
191174
"rationale": "Inherited from the pinned Python 3.14 Debian base; no supported-line fix is currently available."
192-
},
193-
{
194-
"id": "CVE-2026-14456",
195-
"package": "libssl3t64",
196-
"severity": "High",
197-
"rationale": "Inherited from the pinned Python 3.14 Debian base. The affected QUIC listener code is not enabled or used by these HTTP-only provider images, and no supported-line upstream fix is currently available."
198-
},
199-
{
200-
"id": "CVE-2026-14456",
201-
"package": "openssl",
202-
"severity": "High",
203-
"rationale": "Inherited from the pinned Python 3.14 Debian base. The affected QUIC listener code is not enabled or used by these HTTP-only provider images, and no supported-line upstream fix is currently available."
204-
},
205-
{
206-
"id": "CVE-2026-14456",
207-
"package": "openssl-provider-legacy",
208-
"severity": "High",
209-
"rationale": "Inherited from the pinned Python 3.14 Debian base. The affected QUIC listener code is not enabled or used by these HTTP-only provider images, and no supported-line upstream fix is currently available."
210175
}
211176
]
212177
}

.github/workflows/container-security.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ jobs:
4949
- synthetic
5050
- getcomics
5151
- annas-archive
52+
- libgen
5253
include:
5354
- provider: synthetic
5455
dockerfile: docker/Dockerfile.synthetic
5556
- provider: getcomics
5657
dockerfile: docker/Dockerfile.getcomics
5758
- provider: annas-archive
5859
dockerfile: docker/Dockerfile.annas-archive
60+
- provider: libgen
61+
dockerfile: docker/Dockerfile.libgen
5962
env:
6063
IMAGE: pullbox-provider-${{ matrix.provider }}:security-${{ github.run_id }}
6164
steps:
@@ -133,13 +136,16 @@ jobs:
133136
- synthetic
134137
- getcomics
135138
- annas-archive
139+
- libgen
136140
include:
137141
- provider: synthetic
138142
dockerfile: docker/Dockerfile.synthetic
139143
- provider: getcomics
140144
dockerfile: docker/Dockerfile.getcomics
141145
- provider: annas-archive
142146
dockerfile: docker/Dockerfile.annas-archive
147+
- provider: libgen
148+
dockerfile: docker/Dockerfile.libgen
143149
steps:
144150
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
145151
- name: Set up QEMU

.github/workflows/provider-latest.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ on:
1717
options:
1818
- getcomics
1919
- annas-archive
20+
- libgen
2021
- synthetic
2122

2223
permissions:
@@ -46,7 +47,7 @@ jobs:
4647
REQUESTED_PROVIDER: ${{ github.event.client_payload.provider || inputs.provider }}
4748
run: |
4849
case "${REQUESTED_PROVIDER}" in
49-
getcomics|annas-archive|synthetic) ;;
50+
getcomics|annas-archive|libgen|synthetic) ;;
5051
*)
5152
echo "::error::Unsupported provider reconciliation request: ${REQUESTED_PROVIDER}"
5253
exit 1

.github/workflows/provider-release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
tags:
1111
- "getcomics-v*"
1212
- "annas-archive-v*"
13+
- "libgen-v*"
1314
- "synthetic-v*"
1415
workflow_dispatch:
1516
inputs:
@@ -20,6 +21,7 @@ on:
2021
options:
2122
- getcomics
2223
- annas-archive
24+
- libgen
2325
- synthetic
2426

2527
permissions:

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ jobs:
8181
case "${PROVIDER}" in
8282
getcomics) RELEASE_NAME="GetComics Provider" ;;
8383
annas-archive) RELEASE_NAME="Anna's Archive Provider" ;;
84+
libgen) RELEASE_NAME="Library Genesis Provider" ;;
8485
synthetic) RELEASE_NAME="Synthetic Provider" ;;
8586
*)
8687
echo "::error::Unexpected provider in trusted release metadata: ${PROVIDER}"

.gitignore

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,8 @@ ipython_config.py
9494
# install all needed dependencies.
9595
# Pipfile.lock
9696

97-
# UV
98-
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99-
# This is especially recommended for binary packages to ensure reproducibility, and is more
100-
# commonly ignored for libraries.
101-
# uv.lock
97+
# This repository uses pip and pyproject metadata; local uv resolution is not a build input.
98+
uv.lock
10299

103100
# poetry
104101
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.

README.md

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@ Optional, separately deployed direct-download discovery providers for
77

88
This repository provides the version-one Pullbox direct-download protocol,
99
Python DTO package, compatibility policy, conformance runner, synthetic
10-
reference provider, and the official GetComics and Anna's Archive providers.
11-
Production images are independently versioned, multi-architecture, scanned,
12-
signed, and published to GHCR and Docker Hub.
10+
reference provider, the official GetComics and Anna's Archive providers, and a
11+
release-gated LibGen community provider. Published production images are
12+
independently versioned, multi-architecture, scanned, signed, and available
13+
from GHCR and Docker Hub.
1314

1415
## Source Providers
1516

1617
- GetComics: metadata discovery and stateless artifact-route normalization.
1718
- Anna's Archive: metadata discovery with opt-in member fast-download
1819
resolution; a member secret is required only by the resolve operation.
20+
- LibGen: bounded HTML discovery, keyed metadata enrichment, and same-source
21+
generic HTTPS resolution with an editable validated source origin. Its image
22+
remains independently gated until the LibGen release decision is approved.
1923

2024
Each provider will run as an independent, stateless OCI service and implement a
2125
versioned, language-neutral Pullbox provider contract.
@@ -38,6 +42,7 @@ providers/
3842
synthetic/
3943
getcomics/
4044
annas_archive/
45+
libgen/
4146
tests/
4247
conformance/
4348
fixtures/
@@ -66,7 +71,7 @@ coverage gate. `make docker-conformance` builds the digest-pinned synthetic
6671
image and proves the protocol over an internal-only Docker network.
6772
`make security-check` runs Bandit and a strict dependency audit.
6873

69-
The source-provider Compose harness builds both providers, waits for
74+
The source-provider Compose harness builds all source providers, waits for
7075
process-only socket healthchecks, and validates authenticated manifests over an
7176
internal-only network. Process healthchecks intentionally do not call upstream
7277
sources. The harness uses generated test credentials and performs no live
@@ -80,7 +85,7 @@ Pull requests run four stable aggregate checks: `CI Required`,
8085
default permissions. The security gate includes Gitleaks, strict Python
8186
dependency auditing, Bandit, dependency review, and CodeQL's extended security
8287
queries scoped to shipped provider code. Container checks build and smoke-test
83-
all three runtime images, scan them with Grype, and prove Linux AMD64 and ARM64
88+
all four runtime images, scan them with Grype, and prove Linux AMD64 and ARM64
8489
builds without publishing.
8590

8691
High and Critical findings inherited from the pinned public Python base image
@@ -126,6 +131,29 @@ the allowlisted native control types in the contract. Pullbox validates those
126131
controls, renders its own settings UI, and rejects provider-supplied HTML,
127132
JavaScript, or unknown configuration fields.
128133

134+
### Native Provider Configuration
135+
136+
Provider settings use a closed, documented vocabulary:
137+
138+
- `enum` declares closed choices. The configured value must be one of the
139+
declared values.
140+
- `x-pullbox-suggestions` declares editable HTTPS origin suggestions. A user may
141+
enter another safe public HTTPS origin because the list is not an allowlist.
142+
- `x-pullbox-source-origin` marks a field as the provider's effective source
143+
origin for provider-scoped link and browser-resolver policy.
144+
145+
Suggestions and source-origin marking are independent controls. A provider may
146+
offer editable suggestions without changing its source origin, or allow a custom
147+
source origin without supplying suggestions. Pullbox validates suggested and
148+
default origins before rendering or saving them and validates the selected
149+
origin again before use.
150+
151+
Search candidates may include an optional content fingerprint formatted as
152+
`md5:<32 lowercase hexadecimal characters>`. It must remain stable only while
153+
the candidate bytes are identical; changed bytes require a new fingerprint.
154+
Pullbox uses it only for deduplication and fallback grouping, never as a security
155+
or authenticity checksum, and does not treat it as durable library metadata.
156+
129157
## Source Provider Behavior
130158

131159
### GetComics
@@ -141,8 +169,8 @@ after a recognized challenge. It never downloads or proxies artifact bytes.
141169
The Anna's Archive provider is an explicit opt-in integration. Its configurable
142170
official URL accepts only `https://annas-archive.gl`,
143171
`https://annas-archive.pk`, or `https://annas-archive.gd`. Pullbox renders the
144-
field as an editable URL input with those exact choices suggested; lookalike and
145-
arbitrary domains remain rejected. Unattended resolution requires the user's
172+
field as a closed selector with those exact choices; lookalike and arbitrary
173+
domains remain rejected. Unattended resolution requires the user's
146174
member fast-download secret; free slow-download automation, CAPTCHA bypass,
147175
unofficial domains, and payload proxying are not supported.
148176

@@ -153,6 +181,22 @@ quota, source availability, and malformed responses remain distinct failures.
153181
Opening search-result details must not call resolve because a fast-link request
154182
may consume source quota.
155183

184+
Search attempts the selected official Anna's Archive page first. When that page
185+
is blocked by a browser challenge, is temporarily unavailable, or returns no
186+
candidates, the provider performs a bounded fallback against the LibGen comics
187+
catalog. Only candidates with a matching lowercase LibGen ID and MD5 content
188+
fingerprint are considered for Anna's Archive discovery, with canonical files
189+
listed before mobile derivatives. Catalog presence does not guarantee that Anna
190+
offers a member fast-download route, so availability is verified only when the
191+
user grabs the result. Catalog-derived candidates intentionally do not expose a
192+
cross-provider fingerprint to Pullbox: if Anna cannot resolve the record,
193+
Pullbox reports that failure instead of silently downloading it from LibGen.
194+
Resolution still uses the official member fast-download JSON API, and the member
195+
secret is never sent to LibGen. This fallback covers only Anna's Archive records
196+
sourced from LibGen and does not claim parity with Anna's Archive's complete
197+
catalog. The Anna's Archive image includes the catalog-discovery dependency and
198+
does not require a separate LibGen provider container.
199+
156200
Successful resolves may report provider-generic remaining/limit/window quota
157201
telemetry. The response intentionally excludes account identity and download
158202
history. Pullbox stores only the latest capacity observation, applies its
@@ -161,6 +205,22 @@ accepted source when Anna's Archive is unavailable. Manual grabs may use the
161205
reserved slots. Quota errors may include a bounded `retry_after_seconds` hint
162206
so Pullbox can recover automatically even without an earlier capacity report.
163207

208+
### LibGen
209+
210+
The LibGen provider is a separately packaged community integration. It accepts
211+
the documented LibGen origins as editable suggestions, validates the selected
212+
public HTTPS origin before every operation, and attempts ordinary HTTP before
213+
using a request-scoped browser resolver for a recognized source gate. Search is
214+
bounded to three query variants and keyed metadata enrichment; positive and
215+
negative caches are process-local and bounded.
216+
217+
Candidate and artifact identity are revalidated by lowercase MD5, keyed file
218+
metadata, and edition relationships before a same-source public HTTPS artifact
219+
is returned. The MD5 is content identity and deduplication evidence, not a
220+
security guarantee. The provider does not proxy payload bytes, retain resolver
221+
cookies, expose full artifact URLs in logs, or reuse Anna's Archive links.
222+
Known-source failover is bounded to one alternate origin per operation.
223+
164224
## Deployment And Registration
165225

166226
Provider services are deployed separately from Pullbox. An operator creates a
@@ -187,8 +247,11 @@ either registry; both names resolve to the same signed digest.
187247

188248
| Provider | GHCR | Docker Hub |
189249
| --- | --- | --- |
190-
| GetComics | `ghcr.io/pullboxapp/pullbox-provider-getcomics:1.0.0` | `docker.io/pullbox/pullbox-provider-getcomics:1.0.0` |
191-
| Anna's Archive | `ghcr.io/pullboxapp/pullbox-provider-annas-archive:1.0.0` | `docker.io/pullbox/pullbox-provider-annas-archive:1.0.0` |
250+
| GetComics | `ghcr.io/pullboxapp/pullbox-provider-getcomics:1.0.2` | `docker.io/pullbox/pullbox-provider-getcomics:1.0.2` |
251+
| Anna's Archive | `ghcr.io/pullboxapp/pullbox-provider-annas-archive:1.0.1` | `docker.io/pullbox/pullbox-provider-annas-archive:1.0.1` |
252+
253+
LibGen image publication is intentionally omitted from this table until its
254+
independent release gate is approved and a numbered provider release exists.
192255

193256
Pin a numbered version or the immutable digest in production. `latest` tracks
194257
only the newest stable provider release; prerelease and manual `edge` builds do

docker/Dockerfile.annas-archive

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
1-
ARG PYTHON_BASE=python:3.14-slim@sha256:cea0e6040540fb2b965b6e7fb5ffa00871e632eef63719f0ea54bca189ce14a6
1+
ARG PYTHON_BASE=python:3.14-slim@sha256:83ff1d245a3d57d04152252d3ef9cb361494d0b3395abd65a5ebe91c401c8e83
22

3-
FROM ${PYTHON_BASE} AS build
3+
FROM ${PYTHON_BASE} AS security-patched
4+
5+
RUN apt-get update && \
6+
apt-get install --yes --no-install-recommends \
7+
libssl3t64=3.5.7-1~deb13u2 \
8+
openssl=3.5.7-1~deb13u2 \
9+
openssl-provider-legacy=3.5.7-1~deb13u2 && \
10+
rm -rf /var/lib/apt/lists/*
11+
12+
FROM security-patched AS build
413

514
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
615
PIP_NO_CACHE_DIR=1
716

817
WORKDIR /build
918

1019
COPY packages/provider_contract packages/provider_contract
20+
COPY providers/libgen providers/libgen
1121
COPY providers/annas_archive providers/annas_archive
1222

1323
RUN python -m pip wheel --wheel-dir /wheels ./packages/provider_contract && \
24+
python -m pip wheel --find-links /wheels --wheel-dir /wheels ./providers/libgen && \
1425
python -m pip wheel --find-links /wheels --wheel-dir /wheels ./providers/annas_archive
1526

16-
FROM ${PYTHON_BASE} AS runtime
27+
FROM security-patched AS runtime
1728

1829
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
1930
PIP_NO_CACHE_DIR=1 \

0 commit comments

Comments
 (0)