Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions cliproxyapi-pro-core/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
FROM golang:1.26-bookworm@sha256:1ecb7edf62a0408027bd5729dfd6b1b8766e578e8df93995b225dfd0944eb651 AS builder

ARG DEBIAN_SNAPSHOT=20260715T000000Z
RUN set -eux; \
sed -ri \
-e "s#https?://deb.debian.org/debian-security#http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}#g" \
-e "s#https?://deb.debian.org/debian#http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}#g" \
/etc/apt/sources.list.d/debian.sources; \
apt-get -o Acquire::Check-Valid-Until=false update; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl jq python3 build-essential git; \
rm -rf /var/lib/apt/lists/*

Expand Down Expand Up @@ -59,18 +54,12 @@ RUN set -eux; \

FROM debian:bookworm@sha256:9344f8b8992482f80cba753f323adeaf17690076c095ccff6cc9536be98185dc

ARG DEBIAN_SNAPSHOT=20260715T000000Z

RUN mkdir -p /CLIProxyAPI/usage

COPY --from=ghcr.io/komari-monitor/komari-agent:latest@sha256:48815bdfce540d83c60b92809858d9aea3635d07abf78581486e0433ca97d676 /app/komari-agent /CLIProxyAPI/komari-agent
COPY --from=ghcr.io/komari-monitor/komari-agent:latest /app/komari-agent /CLIProxyAPI/komari-agent

RUN set -eux; \
sed -ri \
-e "s#https?://deb.debian.org/debian-security#http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}#g" \
-e "s#https?://deb.debian.org/debian#http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}#g" \
/etc/apt/sources.list.d/debian.sources; \
apt-get -o Acquire::Check-Valid-Until=false update; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl tzdata; \
rm -rf /var/lib/apt/lists/*

Expand Down
9 changes: 2 additions & 7 deletions cliproxyapi-pro-core/Dockerfile.runtime
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
FROM debian:bookworm@sha256:9344f8b8992482f80cba753f323adeaf17690076c095ccff6cc9536be98185dc

ARG TARGETARCH
ARG DEBIAN_SNAPSHOT=20260715T000000Z

RUN mkdir -p /CLIProxyAPI/usage

COPY --from=ghcr.io/komari-monitor/komari-agent:latest@sha256:48815bdfce540d83c60b92809858d9aea3635d07abf78581486e0433ca97d676 /app/komari-agent /CLIProxyAPI/komari-agent
COPY --from=ghcr.io/komari-monitor/komari-agent:latest /app/komari-agent /CLIProxyAPI/komari-agent

RUN set -eux; \
sed -ri \
-e "s#https?://deb.debian.org/debian-security#http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}#g" \
-e "s#https?://deb.debian.org/debian#http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}#g" \
/etc/apt/sources.list.d/debian.sources; \
apt-get -o Acquire::Check-Valid-Until=false update; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl tzdata; \
rm -rf /var/lib/apt/lists/*

Expand Down
1 change: 0 additions & 1 deletion cliproxyapi-pro-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ docker build \
- `CLIPROXY_COMMIT` — 可选 upstream commit SHA;设置后按该提交下载源码,同时保留 `CLIPROXY_VERSION` 作为版本标识。
- `CLIPROXY_BUILD_VERSION` — 可选 runtime 版本号。为空时使用 `CLIPROXY_VERSION` 解析到的 upstream 版本。
- `SOURCE_DATE_EPOCH` — 可选 Unix 时间戳,用于写入确定的构建时间;与不可变 upstream commit 一起设置可获得确定的 source binary。
- `DEBIAN_SNAPSHOT` — builder 和 runtime apt 源使用的 Debian snapshot 时间戳,默认固定为已审阅的 `20260715T000000Z`。
- `GITHUB_TOKEN` — 可选 GitHub API token。

Release workflow 会从 Core、models 和定制层三个不可变提交中取最新时间作为 `SOURCE_DATE_EPOCH`。Core 归档统一规范文件顺序、时间戳、属主和权限,Go 构建同时启用 `-trimpath`。
Expand Down
1 change: 0 additions & 1 deletion cliproxyapi-pro-core/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ Build args:
- `CLIPROXY_COMMIT` — optional upstream commit SHA; when set, source is downloaded from that commit while `CLIPROXY_VERSION` remains the version label.
- `CLIPROXY_BUILD_VERSION` — optional runtime version. If empty, it uses the upstream version resolved from `CLIPROXY_VERSION`.
- `SOURCE_DATE_EPOCH` — optional Unix timestamp used for the embedded build date. Set it together with an immutable upstream commit for a deterministic source binary.
- `DEBIAN_SNAPSHOT` — Debian snapshot timestamp used by builder and runtime apt sources; defaults to the reviewed `20260715T000000Z` snapshot.
- `GITHUB_TOKEN` — optional token for GitHub API requests.

Release workflows derive `SOURCE_DATE_EPOCH` from the newest immutable Core, models, and customization commit. Core archives use normalized ordering, timestamps, ownership, and permissions; Go builds also use `-trimpath`.
Expand Down
1 change: 0 additions & 1 deletion compatibility/upstream.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ CORE_UPSTREAM_TAG=v7.2.94
MANAGEMENT_UPSTREAM_REPO=router-for-me/Cli-Proxy-API-Management-Center
MANAGEMENT_UPSTREAM_TAG=v1.18.5
BUN_VERSION=1.3.14
DEBIAN_SNAPSHOT=20260715T000000Z
17 changes: 11 additions & 6 deletions docs/code-review/2026-07.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ The pinned PR baseline lives in `compatibility/upstream.env`. Pull requests and
| PATCH-006 | P2 | Management overlay provenance | The reviewed replacement allowlist bound only upstream hashes, so local full-file replacement content could change without updating the review metadata | A customization-only edit could bypass the explicit replacement review boundary while the upstream file stayed unchanged | Modify a reviewed overlay replacement without changing its upstream counterpart | Move replacement provenance to a versioned manifest that binds the upstream tag and SHA-256 plus the overlay SHA-256, and reject changes on either side | Overlay collision source-hash test and repository manifest validation | Resolved |
| RELEASE-001 | P1 | GitHub Actions | Workflow actions used mutable major-version tags, including actions with release and workflow-run permissions | A moved tag could execute unreviewed code with repository or registry credentials | Inspect every external `uses:` reference | Pin all actions to commit SHAs and enable grouped Dependabot updates | Workflow pin checker and actionlint | Resolved |
| RELEASE-002 | P1 | Release inputs | Matrix jobs resolved upstream tags and the models branch independently | One release could combine different source or model commits across platforms | Compare the checkout and models fetch inputs used by matrix jobs | Resolve Core, Management, and models commits once and pass immutable SHAs to every consumer | Workflow validation and release notes mapping | Resolved |
| RELEASE-003 | P1 | Build dependencies | Container bases, Komari executable image, manylinux builders, and downloaded Go archives were not content-verified | Rebuilding the same commit could execute different build or runtime inputs | Inspect Docker `FROM`/`COPY --from` and toolchain download steps | Pin image digests and verify the official Go archive SHA-256 | Source-image build and workflow validation | Resolved |
| RELEASE-004 | P2 | Core build reproducibility | Docker apt installs used rolling repositories, binaries embedded the current wall-clock time and local source paths, and platform archive tools retained host metadata | Rebuilding the same immutable source inputs could change the runtime dependency set, binary metadata, or release archive bytes | Rebuild after repository metadata, clock, or checkout path changes and compare artifact inputs and hashes | Pin a reviewed Debian snapshot, derive one `SOURCE_DATE_EPOCH` from immutable Core/models/customization commits, fix generated commit dates, use `-trimpath`, and normalize every tar.gz/zip entry | Reproducible archive tests, static workflow/Docker guards, pinned source-image build, and no-cache image-boundary audit | Mitigated |
| RELEASE-003 | P1 | Build dependencies | Container bases, the Komari executable image, manylinux builders, and downloaded Go archives were not content-verified | Rebuilding the same commit could execute different build or runtime inputs | Inspect Docker `FROM`/`COPY --from` and toolchain download steps | Pin base and build images, verify downloaded Go archives, and explicitly trust the official Komari `latest` image as a maintenance-first runtime dependency | Source-image build and workflow validation | Accepted upstream trust boundary |
| RELEASE-004 | P2 | Core build reproducibility | Rolling system repositories, hosted toolchains, and OCI exporters prevent a practical cross-time byte-for-byte guarantee | Rebuilding the same immutable source can receive newer system packages or exporter metadata, while official published artifacts remain authoritative | Rebuild after repository, runner-image, or exporter updates and compare artifact inputs and hashes | Keep immutable source inputs, deterministic build metadata, `-trimpath`, and normalized archives; deliberately allow rolling Debian packages and external toolchains | Reproducible archive tests, static workflow/Docker guards, and source-image build | Accepted maintenance boundary |
| STATE-001 | P1 | Runtime state queue | Failed SQLite writes were removed from the writer's pending maps, and snapshots were silently dropped when the channel filled | Routing cursors or account runtime statistics could lag permanently or disappear across export/restart | Force an auth-runtime insert failure or hold SQLite while queuing more than 1024 snapshots | Retain failed items, coalesce overflow by key, drain overflow before flush/delete/stop, and retry during shutdown | Runtime writer failure and overflow tests | Resolved |
| STATE-002 | P1 | Usage collection lifecycle | `PopOldest` removed usage payloads before SQLite commit, and service shutdown closed the store without waiting for workers | A transient write error or graceful restart could lose an already-popped batch or race an in-flight store operation | Fail `usage_events` inserts after enqueue, then recover SQLite; cancel the service during background work | Keep the popped batch pending until commit, retry it, flush pending events on shutdown, and wait for workers before closing SQLite | Collector retry test and race detector | Resolved |
| STATE-003 | P1 | Reset boundary | A delayed live batch could commit after reset and repopulate statistics with pre-reset requests | Reset appeared to succeed, then old requests reappeared and stale SSE cursors advanced | Reset after an event is queued or popped but before its insert transaction | Serialize live inserts with reset/retention and reject live events whose timestamps are at or before the persisted reset boundary | Live-event reset-boundary test and SSE reset tests | Resolved |
Expand All @@ -77,6 +77,11 @@ The pinned PR baseline lives in `compatibility/upstream.env`. Pull requests and
| FRONTEND-009 | P2 | Realtime log search and ownership | Account-name search derived auth indexes only from recently loaded event rows and then suppressed raw event-text search; request cancellation, stable cursors, auto-follow, and scroll restoration all remained embedded in the page | Older events for a known account could be omitted, raw matches could disappear when account metadata also matched, and further paging changes had a large regression surface | Search for an account absent from the recent event snapshot while another event contains the same raw term, then paginate the combined results | Build auth matches from complete auth-file metadata, OR them with raw text on the server, preserve both conditions in the stable cursor, and extract the realtime controller into a dedicated hook | Combined-search cursor test, auth metadata search tests, controller source guards, full Core/Management validation, and race detector | Resolved |
| FRONTEND-010 | P2 | Analytics compatibility fallback | Monitoring retains client-side analytics when `/usage/aggregates` is unavailable, while retained event details are intentionally bounded | On older servers, analytics can be approximate when more than 6000 candidate rows are retained | Run Management against a server without the aggregate endpoint and exceed `ACCOUNT_STATS_ANALYTICS_ROW_LIMIT` | Prefer server aggregates, cap fallback work at 6000 rows, and retire the fallback only when the compatibility baseline enforces aggregate API support | Aggregate and fallback analytics Bun tests plus full Management validation | Accepted compatibility boundary |

## Accepted Release Boundaries

- `RELEASE-003` owner: project maintainers. The official Komari `latest` image is trusted as an upstream runtime component so updates arrive without digest-maintenance PRs; pinned base/build images and verified Go archives remain in place.
- `RELEASE-004` owner: project maintainers. Rolling Debian security packages and hosted toolchains are accepted because lower maintenance and timely system updates matter more than cross-time byte-for-byte reproduction; deterministic source identity, build metadata, and release archives remain enforced.

## Package 12 Residual Risks

- `AccountInspectionPage.tsx` is now 2211 lines, down from 3493 (about 37%). It intentionally remains the page-level coordinator for transport lifecycle, destructive-action commands, settings persistence, and rendered composition; pure result derivation, account statistics, confirmation builders, and reducer state now live in the feature model.
Expand Down Expand Up @@ -125,18 +130,18 @@ The pinned PR baseline lives in `compatibility/upstream.env`. Pull requests and

- Hosted runner images and their macOS, Windows, and Ubuntu compilers, linkers, system libraries, Python, zlib, and other tools remain externally versioned inputs. Pinned actions and immutable source inputs narrow this boundary; byte-for-byte Core binary and archive comparisons are meaningful only under the same reviewed toolchain environment.
- The FreeBSD cross-build action still installs `clang`, `lld`, and `wget` from the current Ubuntu runner repositories. Its action revision is pinned and Core metadata/archives are normalized, but compiler and linker package versions can still drift.
- Docker image dependency inputs and application files are pinned, but Docker/BuildKit layer tar metadata remains exporter-dependent. Independent no-cache builds produced identical application file hashes and visible filesystem metadata but different image IDs; consumers must verify the digest published for each release rather than assume a locally rebuilt OCI digest will match byte-for-byte.
- The Debian snapshot must be advanced deliberately to receive security fixes. `DEBIAN_SNAPSHOT` is centralized in the compatibility baseline and validated against every Docker stage so the update is explicit and reviewable rather than silent.
- Base and manylinux images remain pinned, while Debian packages and the official Komari `latest` image intentionally follow upstream updates. This reduces pin maintenance but means rebuilding an unchanged commit at a later date can consume different runtime inputs.
- Docker/BuildKit layer tar metadata remains exporter-dependent. Consumers must verify the digest published for each release rather than assume a locally rebuilt OCI digest will match byte-for-byte.
- Local Docker builds that intentionally resolve `latest` and omit `CLIPROXY_COMMIT` or `SOURCE_DATE_EPOCH` remain dynamic. Deterministic source binaries require the documented immutable commit and epoch inputs used by CI/release paths.
- Exact-string patch anchors and reviewed full-file replacements remain manual merge boundaries when upstream changes. Both now fail closed before writes; the manifest binds changes on either side, but semantic reconciliation still requires human review.

## Package 9 Validation Results

- Repository validation: 20 Python customization tests, two reproducible archive tests, immutable workflow action checks, Python/shell syntax, JSON, YAML, and whitespace checks passed. Local ShellCheck and actionlint executables were unavailable; required PR checks provide those CI gates.
- The pinned source Docker build for Core `v7.2.94` and commit `36b45d57a3e804b9dfcee307e5d7b3e8cea5acfc` completed through both builder and runtime snapshot apt installs and produced the final image with a fixed build date and `-trimpath`.
- The source Docker build for pinned Core `v7.2.94` and commit `36b45d57a3e804b9dfcee307e5d7b3e8cea5acfc` completed through both builder and runtime apt installs and produced the final image with a fixed build date and `-trimpath`.
- Core `v7.2.94`: guarded-source preflight, rejected reapplication invariance, embedded usage tests, management handlers, plugin host/store, Redis queue, auth scheduler tests, the embedded usage and targeted package race detector, and the server build passed on a fresh generated checkout.
- Management `v1.18.5`: bidirectional overlay replacement preflight, repeated-application invariance, 91 Bun tests, ESLint with no warnings, TypeScript checking, and the production single-file build passed on a fresh checkout.
- Regression coverage verifies deterministic tar.gz/zip ordering and metadata, repository-wide use of fixed Core build inputs, Debian snapshot pinning in every Docker stage, manifest baseline alignment, and rejection when either a reviewed upstream replacement or its overlay source changes without a manifest update.
- Regression coverage verifies deterministic tar.gz/zip ordering and metadata, repository-wide use of fixed Core build inputs, the accepted Docker maintenance policy, manifest baseline alignment, and rejection when either a reviewed upstream replacement or its overlay source changes without a manifest update.

## Package 8 Residual Risks

Expand Down
17 changes: 6 additions & 11 deletions scripts/validation/check_reproducible_builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ def require(text: str, needle: str, source: Path) -> None:
raise SystemExit(f'{source}: missing reproducible-build guard: {needle}')


def check_dockerfile(path: Path, expected_stages: int, snapshot: str) -> None:
def check_dockerfile_maintenance_policy(path: Path) -> None:
text = path.read_text()
if text.count(f'ARG DEBIAN_SNAPSHOT={snapshot}') != expected_stages:
raise SystemExit(f'{path}: expected {expected_stages} pinned Debian snapshot stage(s)')
require(text, 'http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}', path)
require(text, 'http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}', path)
require(text, 'Acquire::Check-Valid-Until=false', path)
for forbidden in ('DEBIAN_SNAPSHOT', 'snapshot.debian.org', 'komari-agent:latest@sha256:'):
if forbidden in text:
raise SystemExit(f'{path}: retired high-maintenance Docker pin remains: {forbidden}')


def main() -> None:
Expand All @@ -27,15 +25,12 @@ def main() -> None:
for line in (root / 'compatibility/upstream.env').read_text().splitlines()
if line and not line.startswith('#') and '=' in line
)
snapshot = compatibility.get('DEBIAN_SNAPSHOT', '')
if not snapshot:
raise SystemExit('compatibility/upstream.env: DEBIAN_SNAPSHOT is required')
overlay_manifest_path = root / 'cliproxyapi-pro-management/overlay-replacements.json'
overlay_manifest = json.loads(overlay_manifest_path.read_text())
if overlay_manifest.get('upstream', {}).get('tag') != compatibility.get('MANAGEMENT_UPSTREAM_TAG'):
raise SystemExit(f'{overlay_manifest_path}: upstream tag must match compatibility/upstream.env')
check_dockerfile(root / 'cliproxyapi-pro-core/Dockerfile', 2, snapshot)
check_dockerfile(root / 'cliproxyapi-pro-core/Dockerfile.runtime', 1, snapshot)
check_dockerfile_maintenance_policy(root / 'cliproxyapi-pro-core/Dockerfile')
check_dockerfile_maintenance_policy(root / 'cliproxyapi-pro-core/Dockerfile.runtime')

ci_path = root / '.github/workflows/ci.yml'
ci = ci_path.read_text()
Expand Down
Loading