Skip to content

v1.2.2

Latest

Choose a tag to compare

@github-actions github-actions released this 14 Aug 03:49
· 46 commits to main since this release

Release Notes v1.2.2

Released: 2026-08-14

A security and release-integrity release. The api runtime image no longer carries pip, which removes the two HIGH advisories that failed the v1.2.1 publish scan, and the container CVE scan moves from a post-tag postmortem to a gate that runs on pull requests and before a release object exists. No application code changed.

Highlights

pip removed from the api runtime image

The v1.2.1 publish scan failed on msgpack 1.1.2 (GHSA-6v7p-g79w-8964) and setuptools 70.3.0 (CVE-2025-47273), neither of which appears in uv.lock. Both arrived with pip: the base image ships it only to bootstrap installs, and Trivy reads the versions pip vendors in pip/_vendor/vendor.txt and attributes them to the image. Scanning python:3.13-slim-bookworm alone reproduces the same pair, so no dependency bump could have cleared either. apps/api/Dockerfile now deletes pip's site-packages directory, its dist-info and its console scripts from the runtime stage, then fails the build if import pip still succeeds. The application lives in /app/.venv, which uv builds without pip, so nothing in the runtime needs it. A scan of the rebuilt image reports 0 HIGH or CRITICAL across 301 inventoried packages, with msgpack, setuptools and pip all absent.

One shared CVE gate, asked at three points

The Trivy inventory and verdict move into a composite action, .github/actions/trivy-image-gate, wrapped by a reusable workflow, .github/workflows/image-cve-gate.yml. CI, release and publish all delegate to it, so the pre-merge check and the release check cannot drift apart. The reusable workflow builds images into the local daemon (load: true, push: false) and interrogates them there, so it needs no registry credentials and is safe to run on pull requests without giving fork branches publishing reach.

Releases wait on the image scan

Previously the only image scan lived in publish.yml, which release.yml dispatches from its final step, so the verdict always landed after the tag and release object were public. Release now has an image-cve-gate job that the release job declares in needs, covering both images on both architectures, each on its own native runner rather than through emulation. Base image OS package sets genuinely differ per architecture, so an amd64-only gate could pass while publish failed on arm64 after the release already existed.

Staged release visibility

release.yml now creates the GitHub release as a pre-release that is not latest. Promotion happens in the final step of publish's release job, after images are scanned, signed and pushed and every package has shipped. That step carries no files, so nothing can fail after the flip. A pre-release is used rather than a draft because a draft has no tag association, and publish reads the release back by tag to preserve its notes.

Committed suppression policy

Trivy reads a .trivyignore from the working directory unless told otherwise, so an untracked file could have silenced every gate with nobody accountable. The gate now names the path explicitly via trivyignores: .trivyignore, and the committed file carries the exception policy with no entries: fix first, one PR per entry reviewed on the security question alone, and every entry needs an advisory ID, a reason, an exposure statement and an exp: date. The classifier treats .trivyignore as an input to both images, so a suppression cannot land without the scan it would suppress running on that commit. Verified both directions against the base image: the committed file suppresses nothing, and an entry for CVE-2025-47273 does drop it.

Known Gap: scanned bytes are not shipped bytes

This is stated plainly in release.yml and is not closed by this release:

  • The pre-tag gate scans an image built from the dispatched SHA. Publish rebuilds from floating inputs (the base image tag, uv:latest, live apt repositories), so the bytes it ships are not the bytes that were scanned before the tag.
  • Publish's own scan reads the pushed digests, which means it runs after the images are in GHCR. A dirty image can still land in the registry under a version tag; what the gate blocks is promotion to latest and the full release.
  • Staged visibility bounds the failure mode rather than eliminating it: a version whose distribution failed never presents itself as the one to install, and a failure before promotion leaves the pre-release standing.
  • Closing the gap properly means promoting the exact scanned digests instead of rebuilding. That is a publish rework and remains pending.

CI Change Classification

  • uv.lock, the root pyproject.toml, every Dockerfile, package.json, pnpm-lock.yaml, .dockerignore and .trivyignore all sit outside apps/, so they matched no existing case arm: a dependency bump ran no tests and no scan. They now mark a change as both runtime-affecting and image-affecting, which is exactly the path the msgpack and setuptools advisories arrived by.
  • New image_changed, image_scan_matrix and run_image_scan outputs drive a new image-cve-scan job. Scan targets are attributed per image by design, so a change that can only reach the web image builds and scans the web image: the Node dependency audit reads web's manifests but cannot see packages inherited from its runtime base. Shared or unattributable inputs, including a change to the gate itself, scan both.
  • Pre-merge scanning is amd64 only, trading the last few points of coverage for review latency; the release gate supplies both architectures before anything ships.

Publish Workflow Fixes

  • The SBOM is written before the verdict and uploaded with if: ${{ !cancelled() }}, so it survives a failing gate. A failing gate is precisely when someone needs to read what is in the image.
  • The docker-security job gains a checkout, which a local composite action needs in order to resolve. It still analyses a registry digest, not the working tree.
  • Asset upload and promotion were split into ordered steps. action-gh-release updates release metadata before it touches assets (src/run.ts), and its post-upload finalize step returns immediately for a non-draft release (src/github.ts, finalizeRelease), so a single call that both uploads and promotes promotes first. A partial upload would have left a release already marked full and latest with assets missing and no rollback.

Tests

  • test_image_cve_gate_is_shared_by_every_caller pins the shared Trivy configuration (severity, exit-code, vuln-type, ignore-unfixed, explicit trivyignores), the presence of .trivyignore, the credential-free local build, and delegation from all three callers.
  • test_release_is_promoted_only_after_publish_succeeds parses publish.yml and asserts order rather than coexistence: exactly two action-gh-release steps, the one carrying files preserves pre-release status, the promoting step carries no files, and promotion is the last step in the job. Verified against three regression shapes, including the single-call form it replaces.
  • test_ci_runs_benchmark_gate_for_benchmark_only_changes now collects every case arm that sets runtime_changed=true and asserts benchmarks/* appears in one of them, dropping its dependence on the order arms happen to be written in. Removing benchmarks/* from ci.yml still fails it.
  • moon.yml adds ci.yml, image-cve-gate.yml, the composite action and .trivyignore to the workflow-contract task inputs, so the cache cannot report a stale green after the gate itself changes.

Documentation

  • docs/architecture/SIBYL_1_3_RETHINK_2026-08-13.md gains a Phase 0 headline experiment: race the 8-lane machine against a naive-strong arm (verbatim spans, stemmed BM25, dense KNN, reciprocal-rank fusion, tight pack, extract-then-answer). Parity deletes the machine; a loss is the first clean measurement of which lanes carry the score. Fusion and pipeline unification resolve as consequences of the race.
  • The same document adds a standing direction to make hand-lexicon contamination structurally unwriteable: analyzer-level snowball(english) stemming in SurrealDB, a lemmatizer for surviving Python-side normalization, and a smaller role for the bespoke coverage ranker whose hand weights the audit showed are mostly ordinal no-ops.

Upgrade Notes

  • Version pins move to 1.2.2 across VERSION, charts/sibyl, charts/surrealdb, docker-compose.quickstart.yml, apps/api/pyproject.toml, apps/cli/pyproject.toml, the Ansible role defaults and the deployment docs. Update your own pins or set SIBYL_IMAGE_TAG.
  • pip is gone from the api image. Anything that shells into the container and runs pip install will now fail. Install into the image at build time instead, or extend it with a stage that provides its own toolchain.
  • Nothing in the api runtime invoked pip, and the Dockerfile asserts its absence at build time, so a future base image that reinstates pip somewhere the removal does not reach breaks the build rather than the scan.
  • Contributors adding a Trivy exception should read the policy at the top of .trivyignore first: fixing or removing the component comes before listing it, and every entry needs its own pull request and an expiry date.

Install

Local server

curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --version 1.2.2

Remote CLI

curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --remote --version 1.2.2
sibyl init --remote https://sibyl.example.com
sibyl auth login

Homebrew

brew install hyperb1iss/tap/sibyl
sibyl up

Arch Linux (AUR)

paru -S sibyl
sibyl up

Headless server

curl -fsSL https://raw.githubusercontent.com/hyperb1iss/sibyl/main/install.sh | sh -s -- --version 1.2.2 --no-open

Kubernetes (Helm)

helm repo add sibyl https://raw.githubusercontent.com/hyperb1iss/sibyl/gh-pages
helm repo update sibyl
helm upgrade --install sibyl sibyl/sibyl --version 1.2.2

Artifacts

This release includes Python wheels and sdists, the generated
Homebrew formula, the generated AUR PKGBUILD, Helm charts, Docker
SBOMs, aggregate dual-registry cosign receipts, and a SHA256 checksum
manifest.