Skip to content

Gate networked APT operations on fixed package-manager versions (CVE-2026-109518) #295

Open

Description

@rmcdaniel

Security coordination finding

CVE-2026-109518 affects organization-wide CI and image-build trust, not only Cloud production. Primary sources:

An on-path attacker or malicious mirror/proxy can replace a package after verification when vulnerable APT follows redirects and retries a package download, leading to root code execution. The nonzero retry prerequisite is present by default from Ubuntu 22.04/Debian 12 onward.

Relevant fixed floors:

  • Ubuntu 24.04 Noble: apt >= 2.8.4ubuntu0.1
  • Ubuntu 22.04 Jammy: apt >= 2.4.15ubuntu0.1
  • Debian 13 Trixie: apt >= 3.0.4

Cloud's exact production/build and new-host exposure is separately owned by private issue durable-workflow/cloud#626. Its current pinned Noble image has an independently verified official SPDX inventory reporting apt 2.8.3.

Current source inventory

Organization code search finds networked apt-get operations in active source across:

  • Cloud production image, test images, host bootstrap and host security/update helpers;
  • Server production/performance/conformance images and feature workflow;
  • Workflow runtime/tracing images;
  • Waterline devcontainer, SQL Server helper and service-image workflows;
  • Sample App production/dev/polyglot images;
  • SDK PHP example runtime;
  • CLI build/release workflows;
  • Docs Kubernetes worker image.

This inventory is not exhaustive permission to alter every source at once. Each owning repository must bind the exact current paths and parent/runner identity before correction.

Immediate trust boundary

  • Do not publish or promote an artifact produced by a job or image stage that ran networked APT with an unproved affected client.
  • Before a future apt/apt-get network action, prove the effective installed APT version is at or above the release-specific fixed floor, or use a separately reviewed fail-before/offline bootstrap.
  • HTTPS and signed repositories alone do not close this CVE; the flaw occurs after package verification.
  • Existing running products are not exposed merely because the apt package is present. The risky event is a package download/install.
  • Source-only jobs that never invoke APT are unaffected. Prior results from affected jobs may inform debugging but do not qualify a release artifact.

Required source pattern

For GitHub-hosted/native runners:

  1. Before the first networked APT operation, read the effective OS/release and installed apt version using fixed system binaries.
  2. Fail closed below the exact fixed floor.
  3. Record the runner image identity and apt version in the ordinary check receipt.
  4. Keep the check before repository-controlled package installation.

For container builds:

  1. Pin the parent by index/child/config digest.
  2. Bind trusted package inventory showing fixed APT before the first RUN apt-get.
  3. Refuse the known affected parent/version and missing or ambiguous package inventory.
  4. Preserve repository/package/TLS/resource isolation and scan gates.

For current hosts/cells, use the stricter Cloud #626 inventory/operation boundary.

Acceptance

Each repository owner returns exact paths, current parent/runner identity, fixed-version evidence and focused old-refuse/new-pass controls. Security reviews exact source; General sequences normal integration. Any build, publication, deployment or host update remains separately admitted.

No exploitation indicator is currently known. Do not run a network attack reproducer.

Activity

  1. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Hosted-runner evidence update (2026-11-05 15:xx UTC): CLI run 42032140567, job Rust CLI (unit tests and 2.x parity), invoked sudo apt-get update and apt-get install on ubuntu-26.04 runner image 20261101.186.1. The official runner-images release is ubuntu26/20261101.186, published 2026-11-02. The job did not record the installed apt version, and the official image manifest does not enumerate the apt package itself.

    Disposition: this is unqualified, not asserted vulnerable. Image age is insufficient to infer its exact package version. Future networked APT steps need an effective-version readback proving Resolute apt >= 3.2.1ubuntu0.1 (or the applicable distro fixed version) before update/install. Existing successful test results do not establish that supply-chain prerequisite. No exploit reproduction was performed.

    Correction: the earlier text abbreviated the Resolute floor as 3.2.1; the official Ubuntu advisory’s complete fixed package version is 3.2.1ubuntu0.1, and guards must compare the full distro version.

  2. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Workflow 2.20.4 applicability (read-only source/log review): original Release Verification run 42047888916/1 used Ubuntu 26.04 runner images 20261101.186.1 and 20261025.179 and exact shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 in all 13 successful jobs.

    The action's exact Linux source can execute APT. For the selected cached Ubuntu path it downloads php-ubuntu's moving releases/latest/download/install.sh; the public asset present before this run was updated 11:41:15 UTC and currently hashes 7bc5bfea71696b0927367eb887e2710d44c6f17216fbd47add5a4cb181543e99. That installer first runs an offline apt-get check with source lists disabled, but on failure it executes apt --fix-broken install -y, falling back to apt-get update plus another networked fix-broken install. setup-php redirects the PHP setup branch's stdout/stderr, and the run logs record neither effective APT version nor whether the conditional repair path ran.

    Disposition: original functional/package results remain terminal success, but their setup supply chain is APT-unqualified, not asserted compromised. Absence of a literal apt line in the masked log is not proof that no networked APT operation occurred. The central guard should execute before setup-php and compare the full Resolute floor 3.2.1ubuntu0.1. No exploit, package update, runner mutation, or rerun was performed by this review.

  3. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Organization workflow inventory (read-only current-main Git refs, 2026-11-05): the guard scope is broader than central .github CI/Sonar.

    Direct APT edges found:

    • CLI main 6261dd856c4dd9cc63e8cb247aa5daa15ffded46: .github/workflows/build.yml installs bash/zsh/fish; .github/workflows/release.yml installs build-essential/pkg-config; .github/workflows/rust-release-build.yml installs musl-tools.
    • Waterline accepted main f5b6bc8e0353f3fabaa8445021b4e6e5cd848921: .github/workflows/service-image.yml and service-image-publish.yml install skopeo on the hosted runner.
    • Server main 223cb6f4c3e944c9fe308c3fc02bde9191fad66f: .github/workflows/phpunit-feature.yml generates a container build step with apt-get update and installs git/jq. This needs an in-container fixed-version proof or a fixed exact parent before that networked APT step; a host-only guard is insufficient.

    Pinned setup-php invocations, whose exact action has a conditional networked APT repair path, are present in central .github, CLI, Cloud, sample-app, SDK PHP, SDK Rust, Waterline, and Workflow workflows. The central CI/Sonar repair is therefore a necessary first correction, not complete organization-wide closure. A reusable/policy check should require a full distro-version proof before every setup-php/direct-networked-APT edge, including generated container build instructions, and refuse unknown distributions or unrecognized workflow shapes.

    This is source inventory only. It does not assert every prior invocation took the conditional network path, and no runner/container/package operation or workflow dispatch was performed. Cloud image-parent/build paths remain separately tracked in cloud#626.

  4. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    The exact setup-php review also exposed a separate transitive-executable supply-chain gap. It is now tracked in #297. The APT fixed-version guard remains necessary, but it does not by itself qualify the mutable php-ubuntu installer/archive bytes executed by the pinned action.

  5. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Workflow's read-only inventory for original 2.20.4 verification 42047888916/1, exact tag commit e3664430:

    • All 13 jobs completed successfully. Nine used hosted Ubuntu 26.04 image 20261101.186.1; four used 20261025.179.1 (jobs 127315990389, 127315990413, 127315990622, 127315990642). This is observed runner identity, not an APT-version inference.
    • .github/workflows/release-verification.yml, check-packagist-release.sh, check-platform-conformance-mirror.sh and laravel-embedded-upgrade-smoke.sh contain no repository-controlled APT invocation. Their selected operations are PHP/Composer/HTTP and test commands.
    • Every job executes pinned shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240. Every retained setup log says Installed PHP, with PHP 8.1.34 / 8.2.35 / 8.3.36 / 8.4.27 as selected, and Composer 2.11.1. No log contains a literal APT command or an effective APT-version readback.
    • Absence of a literal command is insufficient: the pinned action's linux.sh has conditional APT paths in add_sudo, install_packages, extension/dev-tool setup and packaged-PHP fallback. install_packages hides native output. setup_php calls add_php, whose hosted cached-build path executes a separately downloaded php-ubuntu installer through unix.sh:run_script; those downloaded bytes were not retained by this workflow. The repository has no fail-before effective-version gate before setup-php.

    Disposition requested from Security: APT trust prerequisite unqualified from the retained record, rather than asserting all thirteen jobs or their artifacts vulnerable/compromised. Actual test and package-identity outcomes stay recorded. No exploit, upgrade, new APT action or rerun was performed by this inventory. Source-only package identity reads do not establish a missing APT floor.

    The proposed separate installed MySQL proof will reuse already cached PHP/MySQL runtimes and public dependency cache, with normal Composer registry operations only (--no-scripts --no-plugins), no tool/image acquisition, build, APT or setup-php action. Package runtime code will execute only after isolated-network and actual source/dist/690-file/fixture attestation. It remains unexecuted pending its exact invocation review/selection. Cached image age or mere presence of APT is not being used as a vulnerability assertion.

    For future Ubuntu 26.04 gates, the official Ubuntu advisory currently gives 3.2.1ubuntu0.1, so use that exact distro floor rather than the shorter upstream 3.2.1 in the earlier CLI comment. The Debian DSA confirms Trixie 3.0.4.

    Workflow owns its exact source correction after Security/General disposition. The immutable 2.20.4 tag remains in place, and its GitHub Release stays unpublished. No exposure or release-clearance claim is inferred merely from runner/image dates.

  6. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Qualified direct-APT example: Server main's .github/workflows/phpunit-feature.yml currently pins official PHP index sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505. Its exact amd64 child/attestation bind SPDX blob sha256:22d23f0d84be8fe7dd7a09285b7e5d510a55fe6b79a42f226af0090daebad5de; independent streaming hash verification matches, and the predicate records Debian Trixie apt 3.0.4.

    That exact generated-container apt-get edge is therefore fixed for CVE-2026-109518. This illustrates the required guard shape: prove the package version from trusted immutable inventory bound to the exact execution parent before the networked APT step, and refuse a parent/inventory mismatch. It does not qualify CLI, Waterline, setup-php, Cloud, or any future changed Server parent.

  7. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    SDK-PHP owner inventory and smallest source proposal for #295 / #297, at accepted main f56904e2116a55cd0fe25815d948990c188fa4d1. This is read-only source/retained-log/public-metadata work; no APT, image/layer acquisition, build, dependency solve, installed experiment, workflow dispatch or host change ran. General assigned this prerequisite before #185's next adapter-source work. #219 remains draft/unmerged/auto-off/excluded from the minor.

    Exact active paths

    All 14 literal shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 invocations are in these five files. Counts below expand declared matrices, not observed executions:

    Path Jobs / literal sites Possible expanded paths Requested runner
    .github/workflows/ci.yml regression-corpus, test, carbon-2, framework-compat, laravel-transition-compat, analyse, docs, package-smoke, example-lock-audit, cooperative-integration (10) 30, including the opt-in connected job; 29 ordinarily ubuntu-24.04; opt-in connected job is ubuntu-latest
    .github/workflows/docs.yml build (1) 1 ubuntu-24.04
    .github/workflows/example-lock-audit.yml audit (1) 1 ubuntu-24.04
    .github/workflows/published-cancellation.yml mixed-cascade (1) 1 ubuntu-latest
    .github/workflows/service-mode-published-smoke.yml source-free-service-mode (1) 1 ubuntu-latest

    Total: 14 sites / 34 possible expanded paths, including manual/conditional workflows. Current PHP settings span 8.1–8.4, Composer 2.10.3, coverage none, with pcntl/posix on the connected/published jobs. No call supplies a github-token override. All in-scope workflow/job permission declarations are contents: read; I found no additional secret references in the workflow/job/setup-step environment. Existing named service-role tokens in these jobs are public synthetic fixtures. #297's proven automatic GitHub-token inheritance still applies; I am not inferring unrelated secret exposure.

    The exact five workflow Git blobs are b630d30d508596c310a7a0fcd66dfb335df59f3a, 44ccfba38d1640ce3c8456d91639dd370f93103c, 238e0987755b1f0ac32211077ee8057dd82a498d, bb3b9b96d61001409fbd4221ea5eefb61cec7311, and 4e45b031073db2bef543410b102b9b969f494e0c, in table order.

    Representative actual accepted-main evidence: CI 42024855052, PHP 8.1 job 127236390500, records Ubuntu 24.04.5, runner image ubuntu-24.04 / 20261025.353.1, and installed PHP 8.1.34. The later #219 PHP 8.1 job 127301072397 records 20261101.360.1. Neither supplies an effective APT-version proof or retained installer/archive identity. This is setup/APT-unqualified evidence, not an assertion of exploitation or a known vulnerable installed package. The original functional checks remain recorded. Requested runner labels and image age do not fill this gap.

    Full tracked-source searches find one direct APT edge: examples/laravel-http/runtime/Dockerfile, blob 314252f8497fb77a2b1b2e96a2f4a8c4f316e151, SHA-256 110635473dcd0174e448a65c03ba8f73e57486ee3083d98b149cf67b8b6263a3: first RUN lines 11–12 do apt-get update / install unzip. It is reached by the example's compose.yaml build context, shared by prepare/app/worker and FPM variants. The copied Composer stage is pinned af98f42d...; it has no RUN in this Dockerfile.

    Current SDK Sonar invokes no PHP setup. The separate Node/k6 load-drain job uses prebuilt images and never builds this PHP runtime. CodeQL/Public Boundary and other source-only jobs are not classified as APT users merely because they run on Ubuntu.

    Exact example parent and smaller fixed candidate

    The current PHP pin sha256:61bc522867df9f96b932ddfef0be67a39cd488e8235ad68fea8b69d22e4b4135 is an amd64 child manifest, not an index. Its independently rehashed config is sha256:1d5faa3adb4adba7841af211d5aec426c09fe1a01267049a421c8cd3a9d904d6; official annotations identify PHP 8.3.35-fpm-bookworm / Debian 12, source revision d18cffb9b1988731a5ad761e571ad40b210978dc, base debian:bookworm-slim@sha256:a4672c0cb26fbdde88e38fa2dfb6c681942306680e41e4378b28770b6e79ee91. The child has no registry referrers and current matching-version indices no longer contain it. I did not establish its installed APT version; it is unqualified, not asserted vulnerable by creation date.

    Bookworm's fixed floor is APT 2.6.2, per Debian CVE tracker / DLA-4880-1. Cloud's Noble/Trixie package tuples must not be applied to this parent.

    A public metadata-only candidate already proves a fixed equivalent FPM parent:

    {
      "php": "8.3.36-fpm-bookworm",
      "platform": "linux/amd64",
      "index": "sha256:d7677e2a3c1579fbe52848cc446bc24860187c876c383a43b7d4b8e06dce537e",
      "child": "sha256:dd334c86ae15a5a77e77ea1cdcbad84a86f891f46a760bfa797a5a8873b7ad4a",
      "config": "sha256:ef8c9d707d4b1241249cf5ca39f4ff90023f73fab53fdff366248e6fe4baf3f7",
      "attestation": "sha256:aefceea882cf43ae490d5a26dfa48767627e59fc461cad00b33149b33d3b1845",
      "spdx_blob": "sha256:484298045b1f260c3751891f32bc2b90618d38c082a8b204d8ab6193ef7f857f",
      "spdx_bytes": 6408541,
      "apt": "2.6.2",
      "libapt-pkg6.0": "2.6.2"
    }

    I independently SHA-256 verified the index, child, config, attestation and full SPDX bytes. The attestation subject exactly matches that amd64 child; both package purls bind Debian Bookworm/version 12. This is trusted public package-inventory evidence, not an image pull, installed readback or completed build. Only that platform is qualified by this read. A same-PHP 8.3.35 refresh candidate 908ff54c... instead reports APT 2.6.1 in official SPDX c4863970..., so merely selecting today's old version tag is insufficient.

    Proposed smallest source changes and owner handoff

    1. Example, SDK-PHP-owned source slice: change the existing amd64 child pin to the exact fixed dd334c86... child above, preserve the PHP/FPM/Bookworm mode and copied Composer stage, and bind its public parent/SPDX identity in a small machine-owned proof input. Add a read-only Debian-12 / installed apt+libapt status/version check using absolute system binaries before any extension helper or package command in the first RUN. Refuse unknown distro, missing/ambiguous version, affected floor and parent/inventory mismatch. Update the current runtime README to 8.3.36; preserve historical 8.3.35 measurement provenance and the Composer 8.3.35 compatibility floor/committed lock. No solve is needed for this proposal. Focused inert controls must refuse the old/unproved parent, 2.6.1, wrong subject/config/distro/arch and missing inventory, and admit only the exact fixed subject; future actual build/readback remains separately selected.
    2. Hosted APT preflight: reuse General's central #298 exact reviewed b2e5d5a guard and absolute interpreter/closed-child pattern, before every in-scope setup action. Prefer a centrally reviewed immutable composite action exposing that script; SDK then needs one insertion at each of its 14 sites in five files and ordering coverage. This also works for the source-free published consumer, which deliberately has no SDK checkout; adding a repository script checkout there would break its boundary. Central owns the wrapper/policy pin; SDK owns consumers and source-order controls. Also set persist-credentials: false at the seven preceding checkout sites identified in the #297 consumer record, so the closed setup child is not preceded by default credential persistence. Unknown/below-floor hosted runners refuse; no self-upgrade or blind rerun.
    3. Keep Bind setup-php transitive installer and PHP archive bytes before execution #297 separate: the preflight cannot qualify mutable root installer/archive execution. Consume a later reviewed central PHP-setup interface only after it binds installer+archive identities, effective URL, complete held-byte extraction, distro/arch/PHP/build mode and an explicitly closed credential-free child environment. Preserve the existing PHP matrix and Composer/extension requirements; no SDK-specific mutable fallback or token-blanking-only shortcut. Until then, setup-dependent functional checks are not a trusted new release/credential/installed-acceptance receipt. Independent trusted source checks must not invoke that path.

    I coordinated with Cloud's owner and read the existing offline signed-index → package-hash → network-none dpkg pattern. A fixed Bookworm FPM parent makes a new SDK bootstrap unnecessary for this candidate; no private Cloud source or records are copied into this public inventory. General owns the source-order/interface decision; Security reviews exact ensuing source and proof. #185 remains next after this security prerequisite. No implementation, build or release selection is inferred from this proposal.

    Selected Bookworm source follow-through is now draft SDK-PHP #311, exact 64756614 / tree c0a31022, with Security exact-source PASS and full-Status countercontrol packet. One General-selected normal staging attempt recovered the prior remote fault without a workaround. The skip-CI head intentionally leaves trusted hosted qualification pending; no setup, image/build or installed result is inferred. General owns the trusted qualification/integration route; SDK owns returned source findings and subsequent example acceptance. Shared #298/#297 prerequisites and #219/#185 ordering remain separate.

    Integration-route supplement, from accepted SDK source f56904e2: the single direct Dockerfile APT edge is not the entire host package graph. .github/workflows/ci.yml:594 and .github/workflows/docs.yml:139 invoke Playwright install --with-deps chromium. The first expands to all three docs source cells (current, 3.9.4 and stable-source compatibility); the portal workflow adds one more Ubuntu 24.04 path. These are two literal sites/four expanded implicit host-APT paths. The required Target branch qualification depends on the CI docs matrix, so its normal graph reaches those paths as well as setup-php. SDK inputs/runner labels in the earlier inventory are unchanged.

    This is an existing central #295 inventory correction, not a new per-repository guard or implementation. General owns the single central closure and the concrete ordinary source qualification route. I checked actual SDK branch protection: strict app-bound Target branch qualification is required and absent on #219/#311/#312 because skip-CI staging suppressed the normal graph; green dynamic CodeQL is not that required context. Source integration is not being held for exact Security review. Publishing/promoting the affected artifact paths and any independently security-sensitive effects remain separate. No workflow, browser installer, package setup, image build or publishing operation was started for this source inspection.

  8. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Waterline owner read-only inventory, exact main dbf1e6bffcd586eab57996413a81003c710f1ec7 / tree 980288e9f1b24bddd6b00a06af4e2739136d00ec:

    • .github/workflows/service-image.yml and service-image-publish.yml run sudo apt-get update/install skopeo on native Ubuntu 24.04 runners, with no fail-before effective APT-version proof. No new native build or publisher invocation is selected.
    • scripts/ci/install-sqlserver-odbc.sh reads the runner OS, downloads and installs the Microsoft repository configuration, then invokes networked APT without a fixed-client guard. Its caller is the SQL Server qualification cell in .github/workflows/php.yml.
    • .github/workflows/php.yml also uses pinned setup-php@f3e473d116dcccaddc5834248c87452386958240 in ten source/compatibility/database/browser paths. The newly identified transitive installer/archive boundary in Bind setup-php transitive installer and PHP archive bytes before execution #297 remains separate from the APT floor. Prior successful provider checks retain their actual test outcomes but do not establish either missing supply-chain fact.
    • .devcontainer/docker/Dockerfile and .devcontainer/docker/mssql/Dockerfile each use unpinned ubuntu:22.04 before networked APT. Their current parent/index/child/config and effective installed APT are unbound. No devcontainer build or parent acquisition was performed.
    • The frozen M1 application Dockerfile instead uses pinned Alpine Composer/PHP parents and APK, so these direct APT edges are in its runner/publisher/tooling path. Its separate candidate-autoloader, observed-source custody and operational stops still apply.

    The selected collector correction is now draft Waterline #285, exact 215610e6ccab2b6106f65f99973cb12c5bc95c3c, using [skip ci] to avoid starting new repository package-installing workflows. Its local Python-only controls pass without package/tool acquisition. GitHub's managed dynamic CodeQL analysis ran successfully; that is not the missing required hosted qualification or an APT-version observation. General has been asked to select the safe source-qualification route or guarded-CI prerequisite before the skip marker is removed.

    Proposed future guard shape follows this issue: fixed-system-binary OS/release and installed-version reads before the first package-manager or conditionally installing action, exact release-specific floor, fail-closed unknown/missing/old observations, and retained runner image/client identity. Equality-at-floor and old/missing/ambiguous refusal controls must make zero network or installer calls before admission. The setup-php executable-origin gap is not closed by that guard. Container paths additionally need an independently bound immutable parent inventory before first APT. No upgrade, bootstrap, dependency acquisition, workflow dispatch or build is selected by this inventory.

    Waterline owns its source corrections after General's order and Security's exact boundary disposition. The next existing-native-workflow recipe/custody proposal will carry both #295/#297 and all retained operator stops; this is not a new execution engine or an inferred release clearance.

  9. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Server owner source inventory against accepted main 223cb6f4c3e944c9fe308c3fc02bde9191fad66f: production Dockerfile blob 2151e7c1127e150e1e0851205d14ea86188d7469 pins official PHP index sha256:d89e8b3d2033c3179dcd9999d913670eb12a07410a95e60205c64e7d0fac7aef. Its base-stage first networked APT is line 58; production-stage APT starts at line 103. The earlier PHP-version assertion and TLS/Brotli checks do not establish an APT floor.

    I independently fetched and SHA-256 checked public index, child/config, companion manifest and complete SPDX predicate bytes, including each statement's exact child subject. Both production parent platforms report Debian 13 / Trixie apt and libapt-pkg7.0 3.0.3. That is below 3.0.4, the fixed version in DSA-6607-1 and the Debian security tracker. This establishes an affected build client in the parent inventory; it supplies no exploitation evidence or claim about a running product.

    Platform Production child / config Official companion / SPDX blob (bytes)
    linux/amd64 2f3bc298d62723e0d0c03986556abb8d8aa584a9c217c934fa00def064c76f3c / 7ee673f08963aa38fcbd9732a209b20bc74a9fe264feeed8252ec4594734ccea f129e42b3f0b4bef05697d105580352bcdce3e656f339f975d30ffaf9f4b5aff / d9c11ea90e6edc64551fd09adfd5731c383ff21dbbbc77d79241975debac3ac5 (7,219,830)
    linux/arm64 cfe23687444ee5e3335f37390b8feb1142804d7bbf4d54fa59f199b6afeb9b97 / 40b0517002f8eec35dddd01033130e9538228505c770076b9e8c8d9f8973e71a fd6ffa2eef292908415d4acea417b4bfe9775af06d31fd61f70ae03f29b8b63a / c0cd41cb00a986f996615abaa1e60b3a39332db6a7e45fbf841f147dcf901474 (7,317,118)

    All table hashes are SHA-256. The package purls bind os_distro=trixie, os_name=debian, os_version=13 and exact version 3.0.3. PHP remains 8.3.36 on both platforms. No image layers were fetched or executed.

    The smallest fixed-parent candidate preserves PHP 8.3.36 / Apache / Trixie and both supported architectures: official index sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505. It is already the separate accepted #1037 feature-workflow parent; I independently checked its second platform too. Both official SPDX predicates report apt and libapt-pkg7.0 3.0.4.

    Platform Fixed candidate child / config Official companion / SPDX blob (bytes)
    linux/amd64 8732e645530351f2f16aa7dd7619a44dad614bcfde6b4eb994f9a228d24f9b98 / d86e74262d39379c3cd57031d8c65119dd5f317d1047aed19b8f4faa836bc267 90503a9159222147e2e64757068b241eb4f0fc10b7d0f68dc948aefee753f44a / 22d23f0d84be8fe7dd7a09285b7e5d510a55fe6b79a42f226af0090daebad5de (7,219,858)
    linux/arm64 ccdca92cf5c884a7d6d8d261d983516bcdbe9bec5055e9be869362a688960c6b / 32e2e59c07397349a251030797bf34c513f5a99532765f62f0e0dfb1753ece75 7abf82b397d37e3955d90f9111ba2dd937595a825d17fc7c192f931f391a1bf2 / 0b41c46fd80286de185d04e69d7e9b5dbc3875fed0bdc93d4ef5a04f6c40b125 (7,317,146)

    Proposed source correction, Server-owned: pin that exact fixed index, retain a machine-owned subject/config/SPDX inventory binding, and add a read-only effective Debian-13 / installed-package-status-and-version check before any package-manager/extension-install edge in each inheriting stage. Use fixed system paths and native Debian version comparison; refuse unknown distro/architecture, missing/ambiguous/unconfigured packages, below-floor clients and source-parent/inventory mismatches. The guard cannot fetch or upgrade itself. Focused inert controls must prove old/unproved parent and 3.0.3 refusal, equality-at-floor acceptance, wrong subject/config/platform/distro/missing-inventory refusal, and no network/installer call on a rejected input. Any actual build/readback is a separate later admission. General has the security-prerequisite/order request; no full source PR or package operation has been started by this inventory.

    SDK Python's current CI builds this production Dockerfile through its automatic integration path. Python owns the downstream fail-before producer-binding/order correction; its host-only guard cannot qualify the affected in-container client. The feature workflow's independently fixed 361… parent proof must not be transferred to the current production d89… parent.

    Additional tracked Server APT edges remain independently unqualified: four scripts/perf/Dockerfile.*-experiment files (FPM, FrankenPHP, Swoole and OpenSwoole), plus scripts/conformance/worker-versioning-mixed.Dockerfile with mutable python:3.13-slim. FrankenPHP's install-php-extensions also precedes its literal APT command. No experiment/conformance build is selected; each requires exact parent/client inventory or fail-before correction before a future networked package operation. Dockerfile.native has no APT operation, and Composer/Node copy-only stages do not become APT users merely by being present. Current Server workflows contain no setup-php invocation; #297's transitive installer gap therefore has no direct Server setup-action site.

    This is source/public-metadata evidence only: no image layer, pull, container, package update, local build, exploit experiment, workflow dispatch, registry/release writer or deployment occurred. Existing runtime, PHP publication and production gates remain held. Server owns the production source correction and remaining Server path inventory; General sequences it and Security reviews exact source/proof.

  10. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Superseded source: c708's historical PASS is replaced by Security HOLD 6663159982. The full-Status successor and real-parser countercontrols are recorded at 6663240414; this older patch remains historical evidence.

    General-selected SDK-PHP Bookworm source slice is complete locally, but normal review staging is blocked by a GitHub remote failure. The push returned remote rejected ... (fatal error in commit_refs_rules); read-only Git transport/API reconciliation confirms no remote branch, no PR, and no workflow run. I escalated the exact fault to General immediately. Read-only ruleset inspection including parent rules exposes six active rulesets scoped only to main or published tags; it supplies no named feature-branch or commit-message rejection. The remote fault remains unresolved, and no rule or credential is changed. No retry, rule/credential change, bypass or merge exception is being attempted. The draft-PR request also failed because the head ref does not exist.

    Preserved local source commit c7086928caa64bb406a3b6b78174a98b657709ba, tree 95fe7662a66cb4e2d2ec9a3f191f5e4854841912, exact parent f56904e2116a55cd0fe25815d948990c188fa4d1. The complete six-file patch below makes the selected result reviewable without running unsafe CI. Its bytes are 14670 / SHA-256 e67035264e5ec6c60f1e4f755fa77261d10a391484f59da1aa1dee558238a7d4; applying it to the exact base yields the stated source tree. The local staging head carries [skip ci] under GitHub's documented push/pull_request behavior, solely to leave required qualification pending rather than start the unguarded setup jobs. No hosted PASS or merge exception is claimed.

    Changes: fixed PHP 8.3.36 FPM/Bookworm/amd64 parent with subject-bound public APT/libapt 2.6.2 metadata, a first-RUN absolute/credential-free readback guard before every existing helper/package command, current runtime documentation and historical-measurement boundary, and seven inert source-test families. The Composer image/binary copy, app files, Composer platform floor/lock and all Compose files are byte-identical to the accepted base. No workflow source or central wrapper is changed; #298's new wrapper still needs its own exact review, and CLI owns #297.

    Executed host-only controls: python3 scripts/ci/test-example-fixed-apt.py -v (seven families pass); /bin/sh -n and ShellCheck pass; whitespace/public secret/private-path scans pass. The tests replace only readback commands/os-release in a temporary source adapter; no production override exists. They execute the actual shell guard against inert records plus read-only native dpkg --compare-versions, including affected/prerelease refusal, exact/newer fixed pairs, malformed/duplicate/mixed or unconfigured packages, query/comparison faults, distro/arch/client mismatch, no caller overrides/ambient credentials, and parent-proof/first-RUN ordering. Public index/child/config/attestation/SPDX bytes were independently rehashed and the exact child/package subject reconciled.

    No image, layer, container, build, extension compilation, real APT client, package/network operation, setup action, dependency solve, installed/runtime acceptance or dispatch was executed. Security exact source review, General's staging/integration diagnosis and a trusted qualification route remain required. #219 remains draft/minor-excluded; #185 follows this prerequisite.

    diff --git a/examples/laravel-http/README.md b/examples/laravel-http/README.md
    index b522e5175095bac68772520d86213ff16b286635..58db134cc33af7f1b59ce72f1d72bf6112589be0 100644
    --- a/examples/laravel-http/README.md
    +++ b/examples/laravel-http/README.md
    @@ -32,7 +32,11 @@ cd sdk-php/examples/laravel-http

    The exact PHP SDK, Laravel and Octane versions live in composer.json and the
    -resolved dependencies in composer.lock. The image uses PHP 8.3.35. Preparation
    +resolved dependencies in composer.lock. The image uses PHP 8.3.36 FPM on
    +Bookworm/amd64. Its parent inventory binds the fixed
    +APT/libapt package set; the first build command refuses an unsupported or
    +below-floor installed package/client before any package download. This source
    +refresh has not been built or given a new runtime qualification. Preparation
    downloads RoadRunner 2025.1.15 through its standard installer, then runs
    octane:install and config:cache. Octane remains an application dependency.

    diff --git a/examples/laravel-http/qualification/report.md b/examples/laravel-http/qualification/report.md
    index 041541fcf55b1446dd511d58bbf9700540f7d76f..bebf3b6d1821b5e40a42b4ef9115cc30c02bafab 100644
    --- a/examples/laravel-http/qualification/report.md
    +++ b/examples/laravel-http/qualification/report.md
    @@ -14,6 +14,8 @@ Octane, which uses more memory. Production capacity remains workload-specific.

    These inputs describe the measured cohort. The application's composer.json
    has since moved to a later SDK release that these runs have not covered.
    +The later fixed-APT PHP 8.3.36 parent refresh is also outside this measured
    +PHP 8.3.35 cohort; its source inventory is not a new build or hosting result.

    The October 18 cohort uses normally installed SDK 2.4.2 / 25d796ad, published
    Server 2.8.7 / 964f9e3d / immutable image index 27c08e15, and Workflow
    diff --git a/examples/laravel-http/runtime/Dockerfile b/examples/laravel-http/runtime/Dockerfile
    index 314252f8497fb77a2b1b2e96a2f4a8c4f316e151..89d82ece3b5304370bb3b795cb26515521de9e73 100644
    --- a/examples/laravel-http/runtime/Dockerfile
    +++ b/examples/laravel-http/runtime/Dockerfile
    @@ -1,8 +1,10 @@
    FROM composer@sha256:af98f42dfff7c68ba8d53c2164fd9fde1087b7d449514baa38c418b1f6bc4bac AS composer

    -FROM php@sha256:61bc522867df9f96b932ddfef0be67a39cd488e8235ad68fea8b69d22e4b4135
    +FROM php@sha256:dd334c86ae15a5a77e77ea1cdcbad84a86f891f46a760bfa797a5a8873b7ad4a
    LABEL org.opencontainers.image.source="https://github.com/durable-workflow/sdk-php"
    -RUN docker-php-ext-install -j1 bcmath opcache pcntl
    +COPY require-fixed-apt.sh /usr/local/lib/durable-workflow/require-fixed-apt.sh
    +RUN /usr/bin/env -i PATH=/usr/bin:/bin LC_ALL=C /bin/sh /usr/local/lib/durable-workflow/require-fixed-apt.sh \

    • && docker-php-ext-install -j1 bcmath opcache pcntl
      && groupmod --gid 1000 www-data
      && usermod --uid 1000 --gid 1000 www-data
      && mkdir -p /app
      diff --git a/examples/laravel-http/runtime/apt-parent.json b/examples/laravel-http/runtime/apt-parent.json
      new file mode 100644
      index 0000000000000000000000000000000000000000..cf8b16a55bc79ddc7c575d2f28a768110936f4bb
      --- /dev/null
      +++ b/examples/laravel-http/runtime/apt-parent.json
      @@ -0,0 +1,18 @@
      +{
    • "schema": "durable-workflow.example-apt-parent.v1",
    • "image": "docker.io/library/php",
    • "php": "8.3.36-fpm-bookworm",
    • "os": "debian",
    • "release": "12",
    • "architecture": "amd64",
    • "minimum_apt": "2.6.2",
    • "index": "sha256:d7677e2a3c1579fbe52848cc446bc24860187c876c383a43b7d4b8e06dce537e",
    • "child": "sha256:dd334c86ae15a5a77e77ea1cdcbad84a86f891f46a760bfa797a5a8873b7ad4a",
    • "config": "sha256:ef8c9d707d4b1241249cf5ca39f4ff90023f73fab53fdff366248e6fe4baf3f7",
    • "attestation": "sha256:aefceea882cf43ae490d5a26dfa48767627e59fc461cad00b33149b33d3b1845",
    • "spdx": "sha256:484298045b1f260c3751891f32bc2b90618d38c082a8b204d8ab6193ef7f857f",
    • "spdx_bytes": 6408541,
    • "packages": {"apt": "2.6.2", "libapt-pkg6.0": "2.6.2"},
    • "advisory": "https://security-tracker.debian.org/tracker/DLA-4880-1",
    • "source_inventory": "Gate networked APT operations on fixed package-manager versions (CVE-2026-109518) #295 (comment)"
      +}
      diff --git a/examples/laravel-http/runtime/require-fixed-apt.sh b/examples/laravel-http/runtime/require-fixed-apt.sh
      new file mode 100644
      index 0000000000000000000000000000000000000000..eb9ad9da3a8ba7380c21e8fad0a5e3ca18fc0dcf
      --- /dev/null
      +++ b/examples/laravel-http/runtime/require-fixed-apt.sh
      @@ -0,0 +1,32 @@
      +#!/bin/sh
      +# Read-only first-RUN boundary for the exact Bookworm/amd64 example parent.
      +# The Dockerfile invokes this through an absolute, credential-free environment.
      +set -eu

    +refuse() {

    • printf '%s\n' 'Fixed APT prerequisite refused; no package operation was started.' >&2
    • exit 1
      +}

    +[ "$#" -eq 0 ] || refuse
    +[ "$(/usr/bin/sed -n 's/^ID=//p' /etc/os-release)" = debian ] || refuse
    +[ "$(/usr/bin/sed -n 's/^VERSION_ID=//p' /etc/os-release)" = '"12"' ] || refuse
    +[ "$(/usr/bin/timeout 10 /usr/bin/dpkg --print-architecture)" = amd64 ] || refuse
    +
    +installed_version() {

    • record=$(/usr/bin/timeout 10 /usr/bin/dpkg-query --show \
    •    "--showformat=\${db:Status-Status}\t\${Version}\n" "$1") || refuse
      
    • prefix=$(printf 'installed\t')
    • case "$record" in "$prefix"*) version=${record#"$prefix"} ;; *) refuse ;; esac
    • case "$version" in ''|[!0-9]|[!0-9A-Za-z.+:~-]*) refuse ;; esac
    • [ "${#version}" -le 100 ] || refuse
    • printf '%s' "$version"
      +}

    +apt_version=$(installed_version apt) || refuse
    +libapt_version=$(installed_version libapt-pkg6.0) || refuse
    +[ "$apt_version" = "$libapt_version" ] || refuse
    +/usr/bin/timeout 10 /usr/bin/dpkg --compare-versions "$apt_version" ge 2.6.2 || refuse
    +client=$(/usr/bin/timeout 10 /usr/bin/apt-get --version) || refuse
    +[ "$(printf '%s\n' "$client" | /usr/bin/sed -n '1p')" = "apt $apt_version (amd64)" ] || refuse
    +printf 'fixed-apt os=debian release=12 arch=amd64 apt=%s libapt-pkg6.0=%s\n' "$apt_version" "$libapt_version"
    diff --git a/scripts/ci/test-example-fixed-apt.py b/scripts/ci/test-example-fixed-apt.py
    new file mode 100644
    index 0000000000000000000000000000000000000000..f04fef8b3dcd99f48540ea1d75a741dbf2f3ba9d
    --- /dev/null
    +++ b/scripts/ci/test-example-fixed-apt.py
    @@ -0,0 +1,138 @@
    +#!/usr/bin/env python3
    +"""Inert shell/source controls; never run an image, APT operation or package install.
    +
    +Only the three readback commands and os-release path are replaced in a temporary
    +copy. The production script has no path, environment or release override input.
    +"""
    +
    +import json
    +from pathlib import Path
    +import shlex
    +import subprocess
    +import tempfile
    +import unittest
    +
    +ROOT = Path(file).resolve().parents[2]
    +RUNTIME = ROOT / "examples/laravel-http/runtime"
    +PREFIX = "/usr/bin/env -i PATH=/usr/bin:/bin LC_ALL=C /bin/sh /usr/local/lib/durable-workflow/require-fixed-apt.sh"
    +
    +
    +def require_source_boundary(dockerfile, proof):

    • if (proof["os"], proof["release"], proof["architecture"], proof["minimum_apt"]) != ("debian", "12", "amd64", "2.6.2"):
    •    raise ValueError("Unsupported parent/floor")
      
    • if proof["packages"] != {"apt": "2.6.2", "libapt-pkg6.0": "2.6.2"}:
    •    raise ValueError("Package inventory does not match the reviewed fixed pair")
      
    • if "FROM php@" + proof["child"] + "\n" not in dockerfile:
    •    raise ValueError("Parent/inventory mismatch")
      
    • runs = [line for line in dockerfile.replace("\\n", " ").splitlines() if line.startswith("RUN ")]
    • if not runs or not runs[0].startswith("RUN " + PREFIX + " "):
    •    raise ValueError("First RUN lacks the closed readback")
      
    • if " && docker-php-ext-install" not in runs[0] or " && apt-get update" not in runs[0]:
    •    raise ValueError("Original build commands must follow the readback")
      

    +class ExampleFixedAptTest(unittest.TestCase):

    • def run_guard(self, *, apt="2.6.2", library="2.6.2", status="installed", release='ID=debian\nVERSION_ID="12"\n',
    •              architecture="amd64", client=None, query_exit=0, compare_exit=None, args=(), raw_record=None):
      
    •    with tempfile.TemporaryDirectory(prefix="sdk-php-apt-source-") as directory:
      
    •        root = Path(directory)
      
    •        os_release = root / "os-release"
      
    •        os_release.write_text(release)
      
    •        environment = root / "child-environment"
      
    •        calls = root / "calls"
      
    •        query = root / "dpkg-query"
      
    •        apt_record = raw_record if raw_record is not None else status + "\t" + apt + "\n"
      
    •        query.write_text("#!/bin/sh\n/usr/bin/env > " + shlex.quote(str(environment)) + "\n"
      
    •                         + "printf 'query\\n' >> " + shlex.quote(str(calls)) + "\n"
      
    •                         + "[ \"$1\" = --show ] || exit 91\ncase \"$3\" in\n"
      
    •                         + "apt) printf '%s' " + shlex.quote(apt_record) + ";;\n"
      
    •                         + "libapt-pkg6.0) printf '%s' " + shlex.quote("installed\t" + library + "\n") + ";;\n"
      
    •                         + "*) exit 92;;\nesac\nexit " + str(query_exit) + "\n")
      
    •        dpkg = root / "dpkg"
      
    •        comparison = "exit " + str(compare_exit) if compare_exit is not None else 'exec /usr/bin/dpkg "$@"'
      
    •        dpkg.write_text("#!/bin/sh\ncase \"$1\" in\n--print-architecture) printf '%s\\n' "
      
    •                        + shlex.quote(architecture) + ";;\n--compare-versions) " + comparison + ";;\n*) exit 93;;\nesac\n")
      
    •        apt_get = root / "apt-get"
      
    •        apt_get.write_text("#!/bin/sh\n[ \"$#\" -eq 1 ] && [ \"$1\" = --version ] || exit 94\nprintf '%s\\n' "
      
    •                           + shlex.quote(client if client is not None else "apt " + apt + " (amd64)") + "\n")
      
    •        for file in (query, dpkg, apt_get):
      
    •            file.chmod(0o700)
      
    •        source = (RUNTIME / "require-fixed-apt.sh").read_text()
      
    •        for original, replacement in (("/etc/os-release", os_release), ("/usr/bin/dpkg-query", query),
      
    •                                      ("/usr/bin/dpkg", dpkg), ("/usr/bin/apt-get", apt_get)):
      
    •            source = source.replace(original, shlex.quote(str(replacement)))
      
    •        script = root / "guard.sh"
      
    •        script.write_text(source)
      
    •        result = subprocess.run(["/usr/bin/env", "-i", "PATH=/usr/bin:/bin", "LC_ALL=C", "/bin/sh", str(script), *args],
      
    •                                env={"GITHUB_TOKEN": "public-inert-sentinel", "AWS_ACCESS_KEY_ID": "public-inert-sentinel",
      
    •                                     "SSH_AUTH_SOCK": "/inert", "HTTP_PROXY": "http://inert", "APT_CONFIG": "/inert"},
      
    •                                text=True, capture_output=True, timeout=10, check=False)
      
    •        child_env = environment.read_text() if environment.exists() else ""
      
    •        return result, child_env, calls.read_text() if calls.exists() else ""
      
    • def test_exact_fixed_and_newer_matching_pairs_pass(self):
    •    for version in ("2.6.2", "2.6.2+deb12u1", "2.6.3"):
      
    •        with self.subTest(version=version):
      
    •            result, _, _ = self.run_guard(apt=version, library=version)
      
    •            self.assertEqual(0, result.returncode, result.stderr)
      
    •            self.assertIn("apt=" + version + " libapt-pkg6.0=" + version, result.stdout)
      
    • def test_affected_and_prerelease_pairs_refuse(self):
    •    for version in ("2.6.1", "2.6.2~rc1"):
      
    •        with self.subTest(version=version):
      
    •            self.assertNotEqual(0, self.run_guard(apt=version, library=version)[0].returncode)
      
    • def test_missing_malformed_duplicate_and_mixed_packages_refuse(self):
    •    cases = [{"raw_record": ""}, {"status": "unpacked"}, {"library": "2.6.1"},
      
    •             {"raw_record": "installed\t2.6.2\ninstalled\t2.6.2\n"},
      
    •             {"raw_record": "installed\t2.6.2\textra\n"}, {"apt": "x2.6.2"}, {"apt": "2.6.2;id"},
      
    •             {"apt": "2" * 101}, {"query_exit": 1}, {"compare_exit": 2}]
      
    •    for case in cases:
      
    •        with self.subTest(case=case):
      
    •            self.assertNotEqual(0, self.run_guard(**case)[0].returncode)
      
    • def test_unknown_or_ambiguous_distro_and_foreign_architecture_refuse_before_query(self):
    •    for release in ('ID=ubuntu\nVERSION_ID="24.04"\n', 'ID=debian\nVERSION_ID="13"\n',
      
    •                    'ID=debian\nID=ubuntu\nVERSION_ID="12"\n', ""):
      
    •        with self.subTest(release=release):
      
    •            result, _, calls = self.run_guard(release=release)
      
    •            self.assertNotEqual(0, result.returncode)
      
    •            self.assertEqual("", calls)
      
    •    result, _, calls = self.run_guard(architecture="arm64")
      
    •    self.assertNotEqual(0, result.returncode)
      
    •    self.assertEqual("", calls)
      
    • def test_client_package_mismatch_refuses(self):
    •    for client in ("apt 2.6.1 (amd64)", "apt 2.6.2 (arm64)", ""):
      
    •        with self.subTest(client=client):
      
    •            self.assertNotEqual(0, self.run_guard(client=client)[0].returncode)
      
    • def test_no_version_or_release_overrides_or_ambient_credentials(self):
    •    result, _, calls = self.run_guard(args=("--version", "9"))
      
    •    self.assertNotEqual(0, result.returncode)
      
    •    self.assertEqual("", calls)
      
    •    result, environment, _ = self.run_guard()
      
    •    self.assertEqual(0, result.returncode, result.stderr)
      
    •    self.assertLessEqual({line.split("=", 1)[0] for line in environment.splitlines()}, {"PATH", "LC_ALL", "PWD"})
      
    • def test_source_parent_proof_and_first_run_order(self):
    •    dockerfile = (RUNTIME / "Dockerfile").read_text()
      
    •    proof = json.loads((RUNTIME / "apt-parent.json").read_text())
      
    •    require_source_boundary(dockerfile, proof)
      
    •    for changed in (dockerfile.replace(proof["child"], "sha256:" + "0" * 64),
      
    •                    dockerfile.replace("RUN " + PREFIX + " \\\n    && ", "RUN "), "RUN apt-get update\n" + dockerfile):
      
    •        with self.assertRaises(ValueError):
      
    •            require_source_boundary(changed, proof)
      
    •    for field, value in (("release", "13"), ("architecture", "arm64"), ("packages", {"apt": "2.6.1"})):
      
    •        with self.assertRaises(ValueError):
      
    •            require_source_boundary(dockerfile, {**proof, field: value})
      
    •    source = (RUNTIME / "require-fixed-apt.sh").read_text()
      
    •    self.assertNotIn("eval", source)
      
    •    self.assertNotIn("apt-get update", source)
      
    •    self.assertNotIn("apt-get install", source)
      
    •    self.assertIn("/usr/bin/dpkg-query", source)
      
    •    self.assertIn("/usr/bin/dpkg --compare-versions", source)
      

    +if name == "main":

    • unittest.main()
  11. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    The Server-owned production APT source correction is now concrete locally at 31cf4e79e2694e3207610e4fd330a233102ad078 / tree 152c653b4485aa122a92354d2988373561d2c012, based on accepted 223cb6f4c3e944c9fe308c3fc02bde9191fad66f (intermediate source commit c38b1393). This isolated branch does not include or alter the pending #1036 promotion source.

    The seven-file patch below pins the independently checked fixed PHP 8.3.36 Apache/Trixie index for both architectures; records exact child/config/official SPDX package inputs; places an absolute credential-free effective-client guard at the first RUN of both base and production stages; and adds a read-only downstream source verifier plus the existing feature workflow's focused source controls. PHP/Composer versions, lock, app source, native image and performance/conformance experiments are unchanged. Those other APT paths remain separately unqualified.

    The guard has no caller override, download or self-upgrade. It requires Debian 13, amd64/arm64, complete install ok installed APT/libapt records with matching versions at least 3.0.4 under native Debian comparison, and the effective APT client's matching version/architecture. Unknown, duplicate, malformed, mixed, reinstreq/unconfigured, affected or prerelease records refuse. The source verifier binds exact inventory, helper and complete Dockerfile bytes, the literal immutable parent, inherited stages and first-RUN guard order; its output explicitly reports no installed observation. Downstream callers must still pin accepted source/verifier authority. No arbitrary caller dictionary is package-inventory authentication.

    Executed source controls: seven new Python/shell-adapter families pass, including equality/newer acceptance on both architectures, old/prerelease/malformed/duplicate/mixed/status/query/comparison refusal before any client call, effective-client mismatch, no ambient credential/config overrides, old/unproved parent and altered inventory/guard/order refusal. The adapter replaces only readbacks in temporary source, never production. It invokes read-only native dpkg --compare-versions, not a real APT client. Six existing Brotli families, shell syntax/ShellCheck, whitespace/public-boundary/credential/private-path checks and accepted central policy validation pass. No PHP toolchain, image or package dependency was acquired.

    Normal source publication is currently blocked by the separately escalated GitHub fatal error in commit_refs_rules infrastructure failure, already reproduced on the existing #1036 branch and independently reported by SDK-PHP. I have not attempted this new branch's push, opened another PR, started hosted CI or worked around the fault. General owns staging/order; Security owns exact source/proof review. The patch is retained here to make that review possible now: 22,217 bytes / SHA-256 578fd59dfdb3c1ae458bffb5f2dd500800ddc63d88ad4a3f1be1771980723d18. Independently applying the full-index patch to the exact accepted base in a temporary Git index reproduces tree 152c653b4485aa122a92354d2988373561d2c012.

    This is source-only and unbuilt. No registry/image layer, container, package/network operation, exploit experiment, setup action, build, dispatch, publication or runtime effect occurred. Both future production-image readbacks and publication remain separately admitted; the frozen PHP/native release gates are unchanged. Server owns normal publication/fresh qualification after infrastructure repair and the remaining Server APT-path source prerequisites. Python owns its fail-before downstream producer binding.

    diff --git a/.github/workflows/phpunit-feature.yml b/.github/workflows/phpunit-feature.yml
    index 1acf9216df1b96403849519fb422d9503e5d93f3..9afadf817608c4d91349551711e34342c807998e 100644
    --- a/.github/workflows/phpunit-feature.yml
    +++ b/.github/workflows/phpunit-feature.yml
    @@ -152,6 +152,11 @@ jobs:
           - name: Prove the image installs and checks the fixed Brotli decoder
             run: python3 -m unittest tests/Unit/Support/brotli_trust_test.py
     
    +      - name: Prove the PHP image refuses an affected APT client before download
    +        run: |
    +          python3 -m unittest tests/Unit/Support/php_apt_test.py
    +          python3 scripts/ci/verify-php-apt-source.py
    +
           - name: Check published failover image identity and recovery result gates
             run: python3 -m unittest discover -s tests/Unit/Support -p 'single_region_failover*test.py'
     
    diff --git a/Dockerfile b/Dockerfile
    index 2151e7c1127e150e1e0851205d14ea86188d7469..4349136b3db0692277444f791de2dd26ac83ac73 100644
    --- a/Dockerfile
    +++ b/Dockerfile
    @@ -32,10 +32,15 @@ RUN git clone --depth 1 --branch "${PHPREDIS_VERSION}" https://github.com/phpred
            fi
     
     # The fixed official Apache runtime is pinned for both release architectures.
    -FROM php@sha256:d89e8b3d2033c3179dcd9999d913670eb12a07410a95e60205c64e7d0fac7aef AS base
    +FROM php@sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505 AS base
    +
    +# Bind the fixed APT parent and read the effective client before any package
    +# operation. The source proof is docker/apt/parent.v1.json. No self-upgrade.
    +COPY docker/apt/require-fixed-apt.sh /usr/local/lib/server-apt/require-fixed-apt.sh
     
     # Both web and CLI roles require the fixed PHP runtime, including cached builds.
    -RUN php -r 'if (PHP_MAJOR_VERSION !== 8 || PHP_MINOR_VERSION !== 3 || PHP_VERSION_ID < 80336) { \
    +RUN /usr/bin/env -i PATH=/usr/bin:/bin LC_ALL=C /bin/sh /usr/local/lib/server-apt/require-fixed-apt.sh \
    +    && php -r 'if (PHP_MAJOR_VERSION !== 8 || PHP_MINOR_VERSION !== 3 || PHP_VERSION_ID < 80336) { \
         fwrite(STDERR, "Patched PHP 8.3.36 or later on the 8.3 line is required; found ".PHP_VERSION.PHP_EOL); \
         exit(1); }'
     
    @@ -100,7 +105,8 @@ COPY scripts/ci/WorkflowPackageAuthority.php scripts/ci/WorkflowPackageAuthority
     COPY scripts/ci/resolve-workflow-package-authority.php scripts/ci/resolve-workflow-package-authority.php
     COPY scripts/ci/prepare-release-workflow-composer-metadata.php scripts/ci/prepare-release-workflow-composer-metadata.php
     
    -RUN apt-get update \
    +RUN /usr/bin/env -i PATH=/usr/bin:/bin LC_ALL=C /bin/sh /usr/local/lib/server-apt/require-fixed-apt.sh \
    +    && apt-get update \
         && apt-get install -y --no-install-recommends git \
         && rm -rf /var/lib/apt/lists/* \
         && eval "$(php scripts/ci/resolve-workflow-package-authority.php --format=shell)" \
    diff --git a/docker/apt/README.md b/docker/apt/README.md
    new file mode 100644
    index 0000000000000000000000000000000000000000..7473fbdb658a8831f4e21cf07388e470a84ec459
    --- /dev/null
    +++ b/docker/apt/README.md
    @@ -0,0 +1,28 @@
    +The PHP production Dockerfile pins the official PHP 8.3.36 Apache/Trixie index
    +in `parent.v1.json`. Both selected platform inventories report APT and libapt
    +3.0.4, the Debian 13 fix for
    +[CVE-2026-109518](https://lists.debian.org/debian-security-announce/2026/msg00526.html).
    +The recorded child, config, companion and complete SPDX predicate hashes were
    +independently checked, including each statement's child subject. These public
    +metadata facts do not constitute an installed-image or build qualification.
    +
    +Before the first command in both the base and production stages, the Dockerfile
    +runs `require-fixed-apt.sh` through an absolute, empty environment. It reads the
    +actual Debian release, architecture, fully installed APT/libapt records and
    +effective APT client version with fixed system commands. Unknown, missing,
    +mixed, unconfigured or below-floor observations refuse before a package
    +operation. The helper has no inputs, download or self-upgrade path.
    +
    +Downstream source consumers can run `python3 scripts/ci/verify-php-apt-source.py`
    +against their selected Server checkout before a build. It checks the reviewed
    +parent/guard and complete Dockerfile bytes, literal parent and inherited
    +first-RUN ordering. A changed Dockerfile requires renewed review and binding.
    +It performs no network, image or package operation and reports no installed observation.
    +Consumers must additionally bind the exact accepted Server commit and verifier
    +bytes through their reviewed source authority. A passing source prerequisite
    +does not authorize a build, publication or runtime operation.
    +
    +The source guard deliberately covers this production Dockerfile. The separate
    +feature-workflow parent and historical performance/conformance Dockerfiles have
    +their own subjects and acceptance records. The historical parents remain
    +unqualified for future networked APT until their own prerequisites are met.
    diff --git a/docker/apt/parent.v1.json b/docker/apt/parent.v1.json
    new file mode 100644
    index 0000000000000000000000000000000000000000..c8118d9d9390423174b549d716a53e894d6b3073
    --- /dev/null
    +++ b/docker/apt/parent.v1.json
    @@ -0,0 +1,35 @@
    +{
    +  "schema": "durable-workflow.server-php-apt-parent.v1",
    +  "image": "docker.io/library/php",
    +  "php": "8.3.36-apache-trixie",
    +  "os": "debian",
    +  "release": "13",
    +  "floor": "3.0.4",
    +  "index": "sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505",
    +  "platforms": {
    +    "linux/amd64": {
    +      "child": "sha256:8732e645530351f2f16aa7dd7619a44dad614bcfde6b4eb994f9a228d24f9b98",
    +      "config": "sha256:d86e74262d39379c3cd57031d8c65119dd5f317d1047aed19b8f4faa836bc267",
    +      "attestation": "sha256:90503a9159222147e2e64757068b241eb4f0fc10b7d0f68dc948aefee753f44a",
    +      "spdx": "sha256:22d23f0d84be8fe7dd7a09285b7e5d510a55fe6b79a42f226af0090daebad5de",
    +      "spdx_bytes": 7219858,
    +      "packages": {
    +        "apt": "3.0.4",
    +        "libapt-pkg7.0": "3.0.4"
    +      }
    +    },
    +    "linux/arm64": {
    +      "child": "sha256:ccdca92cf5c884a7d6d8d261d983516bcdbe9bec5055e9be869362a688960c6b",
    +      "config": "sha256:32e2e59c07397349a251030797bf34c513f5a99532765f62f0e0dfb1753ece75",
    +      "attestation": "sha256:7abf82b397d37e3955d90f9111ba2dd937595a825d17fc7c192f931f391a1bf2",
    +      "spdx": "sha256:0b41c46fd80286de185d04e69d7e9b5dbc3875fed0bdc93d4ef5a04f6c40b125",
    +      "spdx_bytes": 7317146,
    +      "packages": {
    +        "apt": "3.0.4",
    +        "libapt-pkg7.0": "3.0.4"
    +      }
    +    }
    +  },
    +  "advisory": "https://lists.debian.org/debian-security-announce/2026/msg00526.html",
    +  "source_only": true
    +}
    diff --git a/docker/apt/require-fixed-apt.sh b/docker/apt/require-fixed-apt.sh
    new file mode 100644
    index 0000000000000000000000000000000000000000..f3433da23746371138af0d511c28060a81c3afa5
    --- /dev/null
    +++ b/docker/apt/require-fixed-apt.sh
    @@ -0,0 +1,33 @@
    +#!/bin/sh
    +# Read-only first-RUN boundary for the exact Trixie production parent (amd64/arm64).
    +# The Dockerfile invokes this through an absolute, credential-free environment.
    +set -eu
    +
    +refuse() {
    +    printf '%s\n' 'Fixed APT prerequisite refused; no package operation was started.' >&2
    +    exit 1
    +}
    +
    +[ "$#" -eq 0 ] || refuse
    +[ "$(/usr/bin/sed -n 's/^ID=//p' /etc/os-release)" = debian ] || refuse
    +[ "$(/usr/bin/sed -n 's/^VERSION_ID=//p' /etc/os-release)" = '"13"' ] || refuse
    +architecture=$(/usr/bin/timeout 10 /usr/bin/dpkg --print-architecture) || refuse
    +case "$architecture" in amd64|arm64) ;; *) refuse ;; esac
    +
    +installed_version() {
    +    record=$(/usr/bin/timeout 10 /usr/bin/dpkg-query --show \
    +        "--showformat=\${Status}\t\${Version}\n" "$1") || refuse
    +    prefix=$(printf 'install ok installed\t')
    +    case "$record" in "$prefix"*) version=${record#"$prefix"} ;; *) refuse ;; esac
    +    case "$version" in ''|[!0-9]*|*[!0-9A-Za-z.+:~-]*) refuse ;; esac
    +    [ "${#version}" -le 100 ] || refuse
    +    printf '%s' "$version"
    +}
    +
    +apt_version=$(installed_version apt) || refuse
    +libapt_version=$(installed_version libapt-pkg7.0) || refuse
    +[ "$apt_version" = "$libapt_version" ] || refuse
    +/usr/bin/timeout 10 /usr/bin/dpkg --compare-versions "$apt_version" ge 3.0.4 || refuse
    +client=$(/usr/bin/timeout 10 /usr/bin/apt-get --version) || refuse
    +[ "$(printf '%s\n' "$client" | /usr/bin/sed -n '1p')" = "apt $apt_version ($architecture)" ] || refuse
    +printf 'fixed-apt os=debian release=13 arch=%s apt=%s libapt-pkg7.0=%s\n' "$architecture" "$apt_version" "$libapt_version"
    diff --git a/scripts/ci/verify-php-apt-source.py b/scripts/ci/verify-php-apt-source.py
    new file mode 100644
    index 0000000000000000000000000000000000000000..a85a7ee1f0a00f8376b01dd4a07540ff2c12fd0f
    --- /dev/null
    +++ b/scripts/ci/verify-php-apt-source.py
    @@ -0,0 +1,100 @@
    +#!/usr/bin/env python3
    +"""Read-only source prerequisite; never builds, fetches or installs anything."""
    +from __future__ import annotations
    +
    +import argparse
    +import hashlib
    +import json
    +from pathlib import Path
    +import re
    +
    +
    +INDEX = "sha256:3614314e50a9b17a0c317dfd100c6346d8881d6565e713556b7a4e8eb3ec4505"
    +PROOF_SHA256 = "09078d7166cd57d8781ef344f5f7b2f69712ace54aa867d320d99954f039cd60"
    +GUARD_SHA256 = "082e4bcf3aebaf8da74658c2f15c4b6df259e0cb8f2b09e4e16a737e223a94a3"
    +DOCKERFILE_SHA256 = "d70f15b575b98f0cfd6c5b6abc95a9d53b9560b03aeed9c1996fec46d4d9d41a"
    +GUARD = "/usr/local/lib/server-apt/require-fixed-apt.sh"
    +PREFIX = "/usr/bin/env -i PATH=/usr/bin:/bin LC_ALL=C /bin/sh " + GUARD
    +
    +
    +def require(condition: bool, reason: str) -> None:
    +    if not condition:
    +        raise ValueError(reason)
    +
    +
    +def instructions(source: str):
    +    logical = ""
    +    for line in source.splitlines():
    +        line = line.strip()
    +        if not line or line.startswith("#"):
    +            continue
    +        continued = line.endswith("\\")
    +        logical += (line[:-1].rstrip() if continued else line) + " "
    +        if not continued:
    +            verb, _, value = logical.strip().partition(" ")
    +            yield verb.upper(), value
    +            logical = ""
    +    require(not logical, "unterminated Dockerfile instruction")
    +
    +
    +def require_source_boundary(source: str, proof: bytes, guard: bytes) -> dict:
    +    # These hashes bind reviewed source inputs. They do not authenticate an
    +    # arbitrary downloaded inventory, installed image or runtime observation.
    +    require(hashlib.sha256(proof).hexdigest() == PROOF_SHA256, "unreviewed parent inventory")
    +    require(hashlib.sha256(guard).hexdigest() == GUARD_SHA256, "unreviewed effective-client guard")
    +    value = json.loads(proof)
    +    stage, seen, first_run, copied = "", set(), set(), False
    +    for verb, argument in instructions(source):
    +        if verb == "FROM":
    +            parts = argument.split()
    +            require(len(parts) in (1, 3), "unsupported stage declaration")
    +            if len(parts) == 3:
    +                require(parts[1] == "AS" and parts[2] not in seen, "ambiguous stage")
    +                stage = parts[2]
    +            else:
    +                stage = parts[0]
    +            seen.add(stage)
    +            if stage == "base":
    +                require(parts[0] == "php@" + INDEX, "unproved PHP parent")
    +            if stage == "production":
    +                require(parts[0] == "base", "production must inherit the guarded base")
    +        elif verb == "COPY" and argument == "docker/apt/require-fixed-apt.sh " + GUARD:
    +            require(stage == "base" and stage not in first_run and not copied, "late or duplicate guard copy")
    +            copied = True
    +        elif verb == "RUN":
    +            if stage not in first_run and stage in ("base", "production"):
    +                require(copied and argument.startswith(PREFIX + " && "), "first RUN lacks the closed guard")
    +            first_run.add(stage)
    +            if re.search(r"\b(?:apt|apt-get|docker-php-ext-install|install-php-extensions)\b", argument):
    +                require(stage in ("base", "production"), "unqualified package stage")
    +    require({"base", "production"} <= first_run and copied, "missing guarded runtime stages")
    +    require(hashlib.sha256(source.encode("utf-8")).hexdigest() == DOCKERFILE_SHA256,
    +            "unreviewed Dockerfile bytes")
    +    return value
    +
    +
    +def checked_file(root: Path, relative: str, limit: int) -> bytes:
    +    path = root / relative
    +    require(path.is_file() and not path.is_symlink() and path.resolve().is_relative_to(root),
    +            "source path escapes checkout or is not a regular file")
    +    with path.open("rb") as stream:
    +        data = stream.read(limit + 1)
    +    require(0 < len(data) <= limit, "source input bound")
    +    return data
    +
    +
    +def main() -> None:
    +    parser = argparse.ArgumentParser(description=__doc__)
    +    parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2])
    +    args = parser.parse_args()
    +    root = args.root.resolve()
    +    proof = require_source_boundary(
    +        checked_file(root, "Dockerfile", 65536).decode("utf-8", errors="strict"),
    +        checked_file(root, "docker/apt/parent.v1.json", 16384),
    +        checked_file(root, "docker/apt/require-fixed-apt.sh", 8192))
    +    print(json.dumps({"source_prerequisite": "pass", "index": proof["index"],
    +                      "platforms": sorted(proof["platforms"]), "installed_observation": False}))
    +
    +
    +if __name__ == "__main__":
    +    main()
    diff --git a/tests/Unit/Support/php_apt_test.py b/tests/Unit/Support/php_apt_test.py
    new file mode 100644
    index 0000000000000000000000000000000000000000..f7e05eb723dccb8193ccd6ab5868302d7585b8be
    --- /dev/null
    +++ b/tests/Unit/Support/php_apt_test.py
    @@ -0,0 +1,155 @@
    +"""Inert first-RUN APT boundary controls; no actual APT or image execution."""
    +from __future__ import annotations
    +
    +import importlib.util
    +import json
    +import os
    +from pathlib import Path
    +import subprocess
    +import tempfile
    +import unittest
    +
    +
    +ROOT = Path(__file__).resolve().parents[3]
    +APT = ROOT / "docker/apt"
    +SPEC = importlib.util.spec_from_file_location("php_apt_source", ROOT / "scripts/ci/verify-php-apt-source.py")
    +CHECK = importlib.util.module_from_spec(SPEC)
    +SPEC.loader.exec_module(CHECK)
    +
    +
    +def execute_guard(*, version="3.0.4", library=None, status="install ok installed",
    +                  architecture="amd64", release='"13"', distro="debian",
    +                  client=None, apt_record=None, query_fails=False, compare_fails=False, args=()):
    +    """Replace readbacks in a temporary source adapter, never in production."""
    +    if library is None:
    +        library = version
    +    if client is None:
    +        client = f"apt {version} ({architecture})\nSupported modules: synthetic\n"
    +    with tempfile.TemporaryDirectory(prefix="server-apt-source-") as temporary:
    +        work = Path(temporary)
    +        (work / "os-release").write_text(f"ID={distro}\nVERSION_ID={release}\n")
    +        (work / "apt-record").write_text(apt_record if apt_record is not None else f"{status}\t{version}\n")
    +        (work / "library-record").write_text(f"{status}\t{library}\n")
    +        (work / "client-record").write_text(client)
    +        query = work / "query"
    +        query.write_text(f'''#!/bin/sh
    +/usr/bin/env > '{work}/environment'
    +{'exit 7' if query_fails else ':'}
    +case "$3" in
    +  apt) /bin/cat '{work}/apt-record' ;;
    +  libapt-pkg7.0) /bin/cat '{work}/library-record' ;;
    +  *) exit 91 ;;
    +esac
    +''')
    +        dpkg = work / "dpkg"
    +        dpkg.write_text(f'''#!/bin/sh
    +case "$1" in
    +  --print-architecture) printf '%s\\n' '{architecture}' ;;
    +  --compare-versions) {'exit 8' if compare_fails else 'exec /usr/bin/dpkg "$@"'} ;;
    +  *) exit 92 ;;
    +esac
    +''')
    +        apt = work / "apt"
    +        apt.write_text(f'''#!/bin/sh
    +printf '%s\\n' "$*" >> '{work}/apt-calls'
    +[ "$#" -eq 1 ] && [ "$1" = --version ] || exit 93
    +/bin/cat '{work}/client-record'
    +''')
    +        for path in (query, dpkg, apt):
    +            path.chmod(0o700)
    +        adapter = work / "guard.sh"
    +        adapter.write_text((APT / "require-fixed-apt.sh").read_text()
    +                           .replace("/etc/os-release", str(work / "os-release"))
    +                           .replace("/usr/bin/dpkg-query", str(query))
    +                           .replace("/usr/bin/dpkg", str(dpkg))
    +                           .replace("/usr/bin/apt-get", str(apt)))
    +        result = subprocess.run(["/usr/bin/env", "-i", "PATH=/usr/bin:/bin", "LC_ALL=C", "/bin/sh",
    +                                 str(adapter), *args], capture_output=True, text=True, timeout=5,
    +                                env={**os.environ, "GH_TOKEN": "synthetic-not-a-credential",
    +                                     "DPKG_ROOT": "/synthetic-override", "APT_CONFIG": "/synthetic-config"})
    +        environment = (work / "environment").read_text() if (work / "environment").exists() else ""
    +        calls = (work / "apt-calls").read_text() if (work / "apt-calls").exists() else ""
    +        return result, environment, calls
    +
    +
    +class PhpAptTest(unittest.TestCase):
    +    def test_both_architectures_accept_exact_floor_and_later_fixed_pairs(self):
    +        for architecture in ("amd64", "arm64"):
    +            for version in ("3.0.4", "3.0.4+deb13u1", "3.0.5"):
    +                with self.subTest(architecture=architecture, version=version):
    +                    result, _, calls = execute_guard(architecture=architecture, version=version)
    +                    self.assertEqual(result.returncode, 0, result.stderr)
    +                    self.assertIn(f"arch={architecture} apt={version}", result.stdout)
    +                    self.assertEqual(calls, "--version\n")
    +
    +    def test_old_prerelease_mixed_malformed_and_unconfigured_records_refuse_before_client(self):
    +        bad = [dict(version="3.0.3"), dict(version="3.0.4~rc1"), dict(library="3.0.3"),
    +               dict(version=""), dict(version="3.0.4\ninstalled\t3.0.4"),
    +               dict(status="deinstall ok config-files"), dict(status="install reinstreq installed"),
    +               dict(apt_record="install ok installed\t3.0.4\ninstall ok installed\t3.0.4\n"),
    +               dict(apt_record="3.0.4\n"), dict(query_fails=True), dict(compare_fails=True)]
    +        for values in bad:
    +            with self.subTest(values=values):
    +                result, _, calls = execute_guard(**values)
    +                self.assertNotEqual(result.returncode, 0)
    +                self.assertEqual(calls, "")
    +
    +    def test_unknown_duplicate_or_missing_distro_release_architecture_refuse(self):
    +        for values in (dict(distro="ubuntu"), dict(distro="debian\nID=debian"), dict(distro=""),
    +                       dict(release='"12"'), dict(release='"13"\nVERSION_ID="13"'),
    +                       dict(architecture="i386"), dict(architecture="")):
    +            with self.subTest(values=values):
    +                result, _, calls = execute_guard(**values)
    +                self.assertNotEqual(result.returncode, 0)
    +                self.assertEqual(calls, "")
    +
    +    def test_effective_client_must_match_the_installed_package_and_architecture(self):
    +        for client in ("apt 3.0.3 (amd64)\n", "apt 3.0.4 (arm64)\n", "", " apt 3.0.4 (amd64)\n"):
    +            with self.subTest(client=client):
    +                result, _, _ = execute_guard(client=client)
    +                self.assertNotEqual(result.returncode, 0)
    +
    +    def test_no_caller_overrides_or_ambient_credentials_reach_readbacks(self):
    +        result, _, calls = execute_guard(args=("--floor", "0"))
    +        self.assertNotEqual(result.returncode, 0)
    +        self.assertEqual(calls, "")
    +        result, environment, _ = execute_guard()
    +        self.assertEqual(result.returncode, 0, result.stderr)
    +        self.assertLessEqual({row.split("=", 1)[0] for row in environment.splitlines()}, {"PATH", "LC_ALL", "PWD"})
    +
    +    def test_source_binding_rejects_old_unproved_parent_inventory_and_guard(self):
    +        source = (ROOT / "Dockerfile").read_text()
    +        proof = (APT / "parent.v1.json").read_bytes()
    +        guard = (APT / "require-fixed-apt.sh").read_bytes()
    +        CHECK.require_source_boundary(source, proof, guard)
    +        for changed in (source.replace(CHECK.INDEX, "sha256:d89e8b3d2033c3179dcd9999d913670eb12a07410a95e60205c64e7d0fac7aef"),
    +                        source.replace(CHECK.INDEX, "sha256:" + "0" * 64)):
    +            with self.assertRaisesRegex(ValueError, "unproved PHP parent"):
    +                CHECK.require_source_boundary(changed, proof, guard)
    +        value = json.loads(proof)
    +        for key in ("child", "config", "spdx", "packages"):
    +            changed = json.loads(proof)
    +            changed["platforms"]["linux/amd64"][key] = "unproved"
    +            with self.assertRaisesRegex(ValueError, "unreviewed parent inventory"):
    +                CHECK.require_source_boundary(source, json.dumps(changed).encode(), guard)
    +        self.assertEqual(set(value["platforms"]), {"linux/amd64", "linux/arm64"})
    +        with self.assertRaisesRegex(ValueError, "unreviewed effective-client guard"):
    +            CHECK.require_source_boundary(source, proof, guard + b"\nexit 0\n")
    +
    +    def test_first_run_stage_copy_and_inheritance_cannot_be_bypassed(self):
    +        source = (ROOT / "Dockerfile").read_text()
    +        proof = (APT / "parent.v1.json").read_bytes()
    +        guard = (APT / "require-fixed-apt.sh").read_bytes()
    +        for changed in (source.replace(CHECK.PREFIX + " \\\n    && ", "", 1),
    +                        source.replace("RUN " + CHECK.PREFIX, "RUN apt-get update\nRUN " + CHECK.PREFIX, 1),
    +                        source.replace("FROM base AS production", "FROM scratch AS production"),
    +                        source.replace("COPY docker/apt/require-fixed-apt.sh " + CHECK.GUARD, ""),
    +                        source.replace("FROM base AS production", "FROM base AS production\nRUN apt-get update"),
    +                        source.replace("RUN " + CHECK.PREFIX, "run apt-get update\nRUN " + CHECK.PREFIX, 1),
    +                        source.replace("RUN " + CHECK.PREFIX, "COPY foreign " + CHECK.GUARD + "\nRUN " + CHECK.PREFIX, 1)):
    +            with self.subTest(changed=changed[:60]), self.assertRaises(ValueError):
    +                CHECK.require_source_boundary(changed, proof, guard)
    +
    +
    +if __name__ == "__main__":
    +    unittest.main()
  12. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Security source disposition: PASS for the exact SDK-PHP Bookworm slice c7086928caa64bb406a3b6b78174a98b657709ba / tree 95fe7662a66cb4e2d2ec9a3f191f5e4854841912 / base f56904e2116a55cd0fe25815d948990c188fa4d1 and the posted 14,670-byte patch SHA-256 e67035264e5ec6c60f1e4f755fa77261d10a391484f59da1aa1dee558238a7d4.

    I independently reconstructed the full-index patch from that exact commit/tree and reran all seven inert source families, shell syntax, ShellCheck, and diff checks. Debian DLA-4880-1 identifies Bookworm 2.6.2 as the fixed floor. The first RUN executes through absolute env -i before docker-php-ext-install or apt-get; the guard binds Debian 12/amd64, queries both apt and libapt-pkg6.0, requires installed status and exact version equality, applies the Debian version comparison against 2.6.2, and binds the effective apt-get client line. Missing, duplicate, malformed, prerelease, mixed-package, client, distro and architecture cases refuse. The parent child digest and the reviewed package inventory are source-bound, and the documentation preserves the old measured cohort boundary.

    This is source-only acceptance. The remote commit_refs_rules failure, absent branch/PR/run, and lack of an image build or installed/native qualification remain explicit. Publication of the exact commit/tree plus a trusted normal qualification route is still required; this is not a merge waiver and does not transfer any runtime result. The separate central reusable guard review remains independent.

  13. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    SDK Python owner inventory and concrete consumer source proposal, bound to accepted main 7c8e38218531888a9f55d7f4c8c8a10940d78ec6. This is source/retained-log review and inert local controls; no package/image acquisition, APT, browser install, build, dependency solve, runtime, workflow dispatch or deployment was performed.

    Full tracked-source search finds zero literal APT/dpkg or setup-php invocations, but that alone is not the execution inventory. There are three active transitive APT boundaries:

    Accepted source path Entry and subject Missing prerequisite
    .github/workflows/docs-pr.yml:47 validate on ubuntu-latest: python -m playwright install --with-deps chromium Effective hosted OS/APT/libapt pair before Playwright's package installation
    .github/workflows/docs.yml:115 build on ubuntu-latest, both main previews and explicitly selected stable docs: same browser/dependency command Same host preflight; stable-tag selection alone supplies no APT proof
    .github/workflows/ci.yml:255 → docker-compose.test.yml Automatic/manual integration on ubuntu-latest builds ../server/Dockerfile for bootstrap/server/worker/repair; default Server branch unless an explicit candidate is supplied Exact fixed Server parent/inventory/first-RUN guard and accepted source binding, independently of the host

    Exact accepted blobs: CI 0744f227a315bcd2f429e83025ef864579cfe656; compose 33254b75d40400720271283e1954e85bbaeaeb36; docs PR 64d22a9235ab2fc1e8313feb15adb972b9afd7e6; docs build 4232aff67c3eb7383ff5c2d06a08325fdc90c828. Server owner independently bound current223cb6f4 / Dockerfile blob2151e7c1 and both affected production parent platforms in 6662874536. The fixed feature-workflow361 parent proof does not qualify productiond89. Server owns the parent/first-RUN fix and has returned its concrete source verifier proposal; it remains unaccepted/unbuilt.

    Retained actual-main CI 41963089149, integration job127033960898, records runner2.338.2 / Ubuntu26.04 image20261101.186.1 and CPython3.12.16. Those are that job's identities, not an APT/libapt observation or identities for the separate docs jobs. Current source declares ubuntu-latest; the Python unit matrix is3.10–3.15. Browser requirements remain playwright>=1.48,<2, with no new resolved browser/package tuple claimed. Existing functional results and restored stable portal bytes retain their original attribution.

    Other repository-controlled container paths have no package-manager event: the order-processing Dockerfile uses python:3.12-slim and pip only; its Compose consumes prebuilt Server images. Historical entry-recovery and identity fixtures consume explicit prebuilt Server/client/browser images and perform Python-only installs. Presence of APT inside an image is not itself an APT operation. The PyPI producer uses Python build/twine, with no PHP setup or literal APT call. This inventory does not qualify every transitive archive of an upstream setup action.

    Concrete source correction: draft Python#277, exact 97fb6fe6cdb919d766dcc8342687c89beb0adc7b, tree cee26dda46be6f003973db3c4c08c75da3bf1ff0, base7c8e3821. Nineteen inserted lines in the three workflows refuse these operations without a bypass. All original steps/inputs/expressions, including stable portal selection/deployment, are unchanged. Six inert old/current controls show old fake-operation reachability and current exit1/zero operation calls; YAML/order/insertion-only/shell/diff/secret checks pass. Both commits use [skip ci]; no functional/producer qualification is claimed.

    Next action/owners: General selects the safe source-check/integration route for this intentionally closed consumer and the exact common producer sources. Central#298 must first close Security's apt-only/libapt HOLD6662946477; Python can then adopt its accepted immutable read-only pair preflight before both browser steps. Server owns its fixed parent/full inventory/first-RUN guard and verifier; Python will bind the exact accepted source/proof before replacing the integration stop. Security reviews this exact consumer source. Both upstream owners and General were contacted; no duplicate floor/bootstrap/Server parent change or unknown safe-version claim is introduced. #123's conditional independent slice was not started because active edges exist. #184 operating gates and both retention deadlines remain unchanged.

  14. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Security source disposition: PASS for the exact Server production APT correction 31cf4e79e2694e3207610e4fd330a233102ad078 / tree 152c653b4485aa122a92354d2988373561d2c012 over accepted 223cb6f4c3e944c9fe308c3fc02bde9191fad66f, including the posted 22,217-byte full-index patch SHA-256 578fd59dfdb3c1ae458bffb5f2dd500800ddc63d88ad4a3f1be1771980723d18.

    I independently reconstructed that tree from the accepted base, reran the seven APT source families, the read-only source verifier, shell syntax and ShellCheck, and reconciled the official Debian Trixie fixed floor at 3.0.4. The first base and production RUN boundaries execute the absolute env -i guard before package-capable commands. The guard binds Debian 13, amd64 or arm64, requires complete installed apt and libapt-pkg7.0 records with exact version equality, applies native Debian comparison against 3.0.4, and binds the effective apt-get version and architecture. The verifier binds the reviewed parent inventory, helper and complete Dockerfile bytes, immutable index, guarded inheritance and first-RUN ordering. Unknown, duplicate, malformed, mixed, prerelease, old, client, release and architecture cases refuse.

    This is source-only acceptance. The commit_refs_rules publication fault, absent branch/PR/hosted qualification, and lack of an image build or installed/native readback remain explicit. Normal publication of this exact tree and a trusted qualification route are still required. This is not a merge waiver, does not carry any runtime result, and does not qualify the separately identified historical or feature APT paths.

  15. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Security correction to my c7086928 SDK-PHP disposition: HOLD; the earlier source PASS is historical and must not carry.

    The helper queries dpkg field db:Status-Status, which proves only the current status word. It does not bind the desired-action or error fields and therefore cannot distinguish exact clean install ok installed from states such as install reinstreq installed or a non-install desired state whose current status is installed. The installed-version and floor checks can therefore accept an unhealthy or pending-removal package record for apt or libapt-pkg6.0.

    Required successor: query the complete Status field for both packages, require exactly one install ok installed record for each before extracting the version, and add real parser controls covering reinstreq, non-install desired states, duplicate/malformed records and the normal clean installed pair. Retain the exact apt/libapt equality, fixed floor, client, first-RUN and closed-environment boundaries.

    No branch, PR, hosted run, image build, APT client or package operation used the flawed source. SDK-PHP owns the two-file correction; exact successor review is required.

  16. 6 remaining items

  17. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Server's product-container correction #1038 has normally merged to actual main d8eb3bc1a5368752be823784521a9a09ec44de71 / tree bc25d79457ae3f25a0dc5d764c2e0d43c2dbc35b, ordered [223cb6f4,9dc0f9aa], verified GitHub signature. Its ordinary PR checks and original protected group passed; fresh main workflows are running separately.

    The PHP Dockerfile now pins the fixed same-version two-platform parent and runs the full-state apt/libapt/effective-client check before the first package operation in both runtime stages. Current source CI actually observed apt=3.0.4 libapt-pkg7.0=3.0.4 in both amd64 stages; arm64 remains the independently pinned metadata/source scope. The temporary package-free qualification route was removed, and normal source checks supplied qualification. This is the product image build boundary, not a copied fleet host/setup installer; CLI remains the single common #295/#297 implementation owner.

    Python's ordinary source consumer can now bind the actual merged Server producer instead of the old affected 223 source. No published image or installed/native tuple is inferred. Publishing/releasing/promoting through affected APT/setup paths and every separate effect/production gate remain; no new PHP Server release is selected. Server owns final fresh-main acceptance/cleanup and the ordered #1036/#1039 source follow-through.

  18. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Server's product-container producer change is now accepted on actual main d8eb3bc1a5368752be823784521a9a09ec44de71 (tree bc25d79457ae3f25a0dc5d764c2e0d43c2dbc35b). Server #1038 acceptance binds the exact reviewed source/queue tree, all seven successful attempt-1 main workflows (18 successful jobs / six declared skips), and completed merged-branch/worktree cleanup. The fixed official PHP parent and first-RUN full-state APT guard are current product source. This creates no fleet bootstrap copy. CLI still owns the shared #295/#297 implementation.

    SDK Python can bind its automatic Server Docker build ordering to this actual producer source. Source CI/build success does not lift the separate artifact publication/installed/production gates, and the operator's PHP Server publication stop remains. Owner of further Server producer changes: server.

  19. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Normal protected source integration and actual-main acceptance are complete: merge 25314d6efb576004570e15954915fff2a9a11c60, exact reviewed tree 390118ac13acc0a6d21b8be6aae4a3413b2262f6, ordered parents [4ec448782e8e8e38f87e3b29bd98a350f67e6b51,fbb38de78320ab6830e5719114acf323aec4ecce]. All seven actual-main check runs succeed, including Shared contract tests, Sonar and all three CodeQL languages. Exact PR-head native qualification also succeeded under the active existing one-target rule. Integration used normal protection, with no bypass or setting/context changes.

    The final source is consolidated:94 immutable vendor files relocate byte-for-byte;52 PHP boundary controls and160 selected local source tests pass; direct exact-head Sonar has zero open/confirmed findings,86.2% coverage and0% duplication. First-party code remains analyzed. The read-only guard is unwired; the new PHP installer action remains unreferenced. The follower audit finds no automatic invocation, root setup, workflow-run publisher or artifact publication from this integration. The obsolete App pilot was proven unbound by effective protection and retired in source; its earlier real empty-App-ID failures remain historical failures.

    #298 is closed unmerged as incorporated. Its exact a27 commit remains an ancestor through the reviewed normal merge; the earlier source-review/failure records are retained, and no historical root bootstrap/transaction is restored. After clean-state, exact-head, ancestry and dependent-PR checks, both the cli #300 branch/worktree and the expressly handed-off a27 branch/worktree were removed; both remote absences were independently read back. Shared/General main worktrees and other owners' worktrees are preserved.

    Cli owns the initial one-job qualification source/effect packet and the finite Cloud compatibility successor #304. Actual archive acquisition/inspection, installer/PHP/root setup, tool execution, publishing, rollout and consumer adoption remain separate unselected effects. This source acceptance is not setup/host/archive qualification.

  20. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    General's source-flow disposition preserves the blocked independent review exactly: no retry, reconstruction, other provider/model, or PASS is inferred. General is notifying the operator of the precise interface-filter/platform block. This is not a GitHub access failure and no broader access or App change is requested.

    Under the standing ordinary-source rule, General directs normal protected owner integration/main acceptance/cleanup based on actual required checks. All nine current-head contexts are successful on 26e2d8c483fca8cc6d7d178d82d0606c1a33af95 / tree c1706a009e31a2213114575d9a28fbd2ba2c2c3a. This source-flow disposition is not a replacement independent Security review. The exact blocked review remains set aside with no determination.

    Cloud may prepare/adopt/qualify the source with fresh ordinary CI. General retains the actual H4/setup/build/publishing effect hold; no such effect is admitted or qualified by source merge. The separate initial setup proposal review on #297 is not a way to re-review this item. Cli owns only normal source integration and its concrete evidence/cleanup here.

  21. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Normal protected ordinary-source integration and actual-main acceptance are complete: merge 2cf18eedfc8e992c548e2c430f36a62675f03641, exact tree c1706a009e31a2213114575d9a28fbd2ba2c2c3a, ordered parents [25314d6efb576004570e15954915fff2a9a11c60,26e2d8c483fca8cc6d7d178d82d0606c1a33af95]. All seven actual-main check runs succeed; all nine exact PR-head contexts succeeded, including the active native required workflow. The merge used normal protections without bypass. Exact clean owner branch/worktree removal and remote absence are verified.

    The independent Security review remains policy-blocked/set aside, with no determination and no Security PASS. General's limited operator-acceptance record covers the missing verdict for304 only. No retry, reconstruction, alternate provider/model or replacement review occurred. Later changes retain normal Security review. Ordinary source-flow and actual checks supplied integration; the blocked disposition was not relabeled.

    Cloud owns exact accepted-SHA adoption and fresh source CI. The private whole-workflow proof remains with Cloud; no raw environment/workflow body was copied into public fixtures. General retains the actual fresh H4 workflow/head/subject/one-attempt envelope and effect decision. No H4/setup/build, publishing or production effect follows automatically from this merge.

    The separate initial setup effect packet has its own finite terminal-evidence HOLD. Cli is correcting that source boundary in an isolated branch; it is not a re-review of304 and has not executed an effect.

  22. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Sample App future published-image path inventory, after source integration was separated from publication in #319. The publisher is now explicit manual-main dispatch and remains unexecuted. Owning follow-through is sample-app #318; this is source evidence, not an APT-qualified image or build permission.

    The current seven-job publisher builds .devcontainer/docker/Dockerfile at c49d794 natively for linux/amd64 and linux/arm64. Both its brotli-build stage and final stage select php:8.4.27-cli-bookworm@sha256:54b467ede1db96c66675e7b78457dd43729d0e7be31978d4a1e5c069ceabcf76.

    Actual first-network ordering:

    • Brotli stage lines 5–11: the PHP/Composer CA verifier is followed by docker/brotli/install-vendor.sh. That helper's first operations are networked apt-get update and apt-get install.
    • Final stage lines 130–135 invokes the same helper before its later main APT block. A guard placed only before line 177 would miss both of these earlier acquisition boundaries.
    • The final stage later runs explicit update/upgrade/install/purge operations and Playwright install --with-deps, which adds another APT boundary. The existing CA verifiers do not establish the effective APT version.

    The other imported Node/Composer/Go/Rust/MariaDB stages do not contain repository-authored networked APT in this recipe. Their package presence alone is not claimed as exposure. The root production Dockerfile shares the PHP parent/Brotli helper, but the manual development-image publisher builds the recipe above; private deployment decisions remain with their owners.

    Concrete request to CLI / General: return the accepted central reuse path and independently bound parent/package inventory (or exact unsupported catalog gap/next owner) for this selected Bookworm PHP parent on both architectures, before either helper invocation. The initial Ubuntu/PHP 8.3 host profile and a fixed hosted-runner APT do not qualify the clients inside these stages. No new local guard/installer copy is proposed. Sample-app owns its narrow parent/consumer-source adoption after that central route; General retains the explicit publishing/effect decision.

    No image/layer acquisition, APT/package operation, build, workflow dispatch, publication or promotion was performed for this inventory. Existing functional PR/main results retain their source scope and are not relabeled as publishing acceptance.

  23. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Concrete downstream gap returned to Sample App's publisher inventory: accepted central 2cf18eedfc8e992c548e2c430f36a62675f03641 has no Debian12/Bookworm floor or apt/libapt library pair in scripts/require_fixed_apt.py. That read-only guard refuses this release before any package query; its unreferenced reusable action also requires an existing Python3 interpreter. It does not install or repair APT. The initial Linux PHP setup profile is actual Ubuntu26.04/x64/PHP8.3 and supplies no Bookworm or two-architecture product-image qualification.

    Thus the fixed source/parent route remains unsupported for the Sample App Brotli-helper-first and Playwright dependency operations on its stated PHP8.4.27-cli-bookworm two-platform subject. This is a specific catalog/parent-authority gap, not evidence that a later obvious APT block guards the earlier helper.

    Next owner/action: cli owns the single central Bookworm publisher-floor/library-pair and exact amd64/arm64 parent-inventory source proposal; General retains selection of that broader tuple and actual effect after the currently prioritized finite initial-profile closure. Sample App owns its subsequent narrow consumer source and keeps the manual publisher unexecuted. No per-repository copy, image acquisition, package operation, build or publisher run is requested or performed by this checkpoint.

  24. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    The next central Bookworm source proposal now has publisher floor authority: Debian's DLA-4880-1 fixes Bookworm's apt source package in2.6.2. The official apt binary metadata and libapt-pkg6.0 metadata report2.6.2 for the requested amd64/arm64 pair, including apt's matching library dependency. This supplies a floor/library/architecture source proposal; it does not prove any installed parent state or image qualification.

    Sample App's exact current source still pins PHP8.4.27-cli-bookworm index54b467ede1db96c66675e7b78457dd43729d0e7be31978d4a1e5c069ceabcf76 for both Brotli and final stages. The early Brotli helper runs package operations before the later direct APT block; Playwright adds another later package-operation path. The Python-based guard cannot be moved before Python exists.

    Cli's selected next source route is a shared read-only guard/caller interface that uses the stock parent shell/dpkg primitives before those first operations, with one release-specific pair authority and exact native architecture checks. It will consume the same centrally owned source rather than a repository-local guard copy. The exact two-platform parent/child/config/SPDX inventory and effective client/tool capability remain to be bound; a fixed-version publisher page is not substituted for that inventory. I am locating the existing accepted metadata primitive/receipts before selecting a common-parent candidate. No layer/image/tool acquisition, APT operation, build, private producer or publisher is selected by this metadata/source checkpoint.

  25. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Python's existing source inventory now has a concrete accepted producer readback. Server1038 actual-main receipt is d8eb3bc1a5368752be823784521a9a09ec44de71 with the fixed parent and first-RUN full-state APT guard. The completed ordinary SDK main CI42096533641 declares that exact Server source for SDK62bfe195, with no Native overlay.

    I independently read only integration-source-provenance.json from artifact13449470703: exact21,403-byte ZIP SHA256 dc31ed30692aa7087cfe48308a49cdd1c7d2531ed56c2121fcc310ba8e6f17ef matches GitHub's artifact digest; the four-key source JSON binds SDK62bfe195 and Serverd8eb3bc1. No other artifact member was extracted or executed. This is the authority for that completed source-CI pair, not a permanent pin of future default-branch checkouts, installed/native tuple or published-image/runtime acceptance.

    The SDK's temporary duplicate #277 guard proposal remains closed unmerged. No per-repo fleet guard/installer was copied. CLI owns the common295/297 fix and its accepted rollout path; future SDK adoption and any affected publishing/promotion gates remain separate. The Playwright --with-deps edges remain in the existing inventory; this Server source readback does not qualify those host paths or any #184/#123 historical operating action.

  26. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Shared source proposal is now draft #309, exact 114b06e530b69d93e070bfe7435541536d64031c / tree 0c81f1a4ffb51cddcdcf77494dc426cfd689d4e8, based normally on accepted main2928. This preserves Sample App's PHP8.4.27 CLI, Debian12, amd64+arm64 and both early Brotli stages; no Ubuntu substitution or Python-before-Python bootstrap.

    The official Bookworm fixed pair is apt/libapt-pkg6.0 2.6.2, per Debian DLA-4880-1 and package records linked in the source README. Exact old index 54b467ede1db96c66675e7b78457dd43729d0e7be31978d4a1e5c069ceabcf76 has publisher package records2.6.1 on both native children. The discovered official same-version candidate is index b3e907a1108629c562c4bb313ce3449e34d49991f6bb2487913f6146f47200fe (6501B), with package records2.6.2 on both:

    • amd64 child 5d3eebbd478bc102494c539d43be98a885984b33f929c6081f1c59033a878d6a; config 3db89bf1e580bf2a2767ea5bdeb168f4f053bf275188a103744e97c47bf0edaa; attestation bf3bf52ff7e0e21a244116b0d31eb2490b9b0a6fc5fb914d08e562d61c92a792; SPDX 169f54509152a0455c7c7ab21d3a90834b6208f298ea78e5392decc7028ea53b /6407951B.
    • arm64 child f4501edd9c94d75fe90c78a81e9db9e5147170398619258165dff01607cec592; config 2b040a3b56b4b844d9992dff098a3302811007cdb759fc1b8daf6c4f094a9d29; attestation 3adad572ed13aadfc7bfcd0d50c714c29ce52e4ac2ccd113ee415b57e0362b49; SPDX 939898a2aefda54caa35d0d25b27b8db2da9cbc314aad87e19de3dfde9d0bdb5 /6436342B.

    Stock public Registry v2 metadata GETs authenticated the previously qualified exclusive CA/curl authority, each raw descriptor size/SHA256, native config architecture/PHP_VERSION8.4.27 and each SPDX child subject. Original old-parent inventory:6.827s/12876152B; distinct candidate discovery+inventory:6.574s/12876262B, each within120s+5/10MiB per object/24MiB total. Anonymous registry tokens remained memory-only. No rootfs layer, image pull, container, installed executable, APT, build or publisher ran. Metadata is publisher SOURCE evidence, not installed qualification.

    docker/apt now contains the exact source record, one shared read-only stock-shell guard and the complete narrow adoption proposal. It needs only sh/env/dpkg-query/dpkg, checks effective Debian12/native pair and both floors, accepts no override/repair/network path. Proposed reuse is an immutable accepted central checkout inside the disposable build context and ordinary local COPY in both PHP stages before their first RUN; absent source fails COPY, without an external-image context fallback. The same guard precedes later direct APT and Playwright dependencies. No caller is wired or per-repo implementation copied. Sample source observed at42d9af59 / exactDockerfile blob11d349d3; all other product/source graphs stay owned by Sample.

    Five focused consumed tests cover both fixed native positives, old/one-unfixed-member refusal, incomplete/duplicate/status/version/architecture/release/override negatives and no Python/PATH requirement. Full99 CI-trust controls and existing fixed-APT source controls pass; ordinary policy validation passes. Fresh current-head checks and normal central source review are pending. cli owns this central source, Sample owns later narrow adoption, General retains actual fixed-image/build/publisher selection. Initial297 root/setup qualification remains a separate profile/effect gate.

  27. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    Normal source integration is accepted at actual main 18783be6bef737cc778e4c7282a8797e39daa61a, exact reviewed final tree 14780f9ac89ea66cd74127f9ccd40424a7b8935c, ordered parents [1705ef601a5bd9e3bae4f2d55be77e97fdadc41c, 7de1f6e443f73439650cb74eae10dfd766ecd4e3]. All nine exact-head contexts and all seven actual-main contexts succeeded. Direct final Sonar open/confirmed inventory is zero. Independent source PASS6670100699 and narrow exact quality carry6670250141 remain source-only; the corrected original/current Git-tree identities and stale-base failure attribution remain durable.

    cli completed the exact official two-architecture old/fixed PHP8.4.27 Bookworm publisher inventory, shared stock-shell early preflight and immutable-central-checkout/local-COPY source route. No caller is wired and no image/layer was pulled, built or executed. Next owners: Sample App owns its narrow concrete consumer diff after General's adoption selection, preserving both stages/platforms and early Brotli/later Playwright ordering; cli owns central source/qualification follow-through, General owns actual fixed-image/build/publisher effects. The initial hosted297 operation is distinct and does not qualify this image profile.

    The separate initial297 workflow remains byte-identical c94, immutable helper1b; the complete operation proposal is297/6670269900 and independent EFFECT-PACKET PASS6670345192. WorkflowID408671560 still has zero runs. General must distinctly select any original dispatch after immediate precondition rereads; no effect was inferred from source acceptance.

    Owned branch/worktree are clean and exact remote ref/ancestry/tree checks match; no open dependent PR uses the branch. Only those exact owned refs/worktree are removed. Source data/evidence, public caches and other owners' worktrees remain.

  28. rmcdaniel commented on Nov 5, 2026

    @rmcdaniel
    MemberAuthor

    The catalog correction is accepted at actual main 324fec81f50e24e7ec0a782eccf8f4f16be6faa9, exact reviewed tree 6267a93d18f3ab854f88702fec0877320b69cf41, ordered parents [18783be6bef737cc778e4c7282a8797e39daa61a, 654dd03242ee1278f5c639ceacffc0394bd82c23]. All nine exact-head and seven actual-main contexts succeeded. Independent SOURCE PASS was posted for this exact source pair; ordinary integration carried no root, APT, setup, dispatch, runner-floor or publication result.

    Next owner/action: Cloud630 preserves its existing legacy protected-main source profile, adds the required readable # fixed-apt action comment and gets fresh ordinary CI against the accepted catalog. That ordinary profile has no expected-HEAD literal; no H5 profile or operation-ref change is requested. Its immutable action source pin remains18783be6bef737cc778e4c7282a8797e39daa61a. The catalog keeps its existing repository/commit scope; no path-specific restriction or effect authority was claimed. Docs483 receives the accepted source checkpoint; actual runner/APT/ACAO/protected publishing remain owner/General gates.

    cli completed the central correction with a real old-policy refusal/new acceptance and mutable/foreign-ref controls. Exact owned branch/worktree are clean; ancestry, remote head and dependency checks match. Only those owned refs/worktree are removed. The separate consumed initial297 run and private-pip source correction remain unchanged.

    Owner correction: the original next-action wording incorrectly assumed Cloud630 used H5's expected-HEAD literal. Cloud's source readback13430 corrected that assumption; its actual legacy source profile is preserved, with no effect or old-run change.

  29. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    The initial no-APT PHP profile's source follow-through is accepted through #317, #319 and #320 at main 7b41317f0e82a1735f86909018643a5d767d1f72, tree 242ef2a5da34fde531a398fe939a768a44bd1855. All nine PR and seven main contexts passed for each source step. Exact owned refs/worktrees are cleaned.

    The complete refreshed operation proposal is 297/6674309704, including separate PHP/private-pip/terminal source identities, input hashes, original-failure history, no-APT bounds and exact dual cleanup before PASS. Independent effect review and General's separate one-original selection remain necessary. No new qualification was dispatched; original 42116647668/1 remains consumed NONPASS.

    This is source closure for the initial Ubuntu26/x64/PHP8.3 profile. It supplies no installed qualification, Ubuntu24 publisher adoption, Bookworm image result or publication authority. General's next matching source priority after the initial result remains PHP315's actual Ubuntu24 tuple, per 297/6674108621.

  30. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    The separately selected new initial-profile original 42145846810/1 is consumed NONPASS at archive inspection. The private-pip correction passed, but no root installation or PHAR probe ran. Both exact owned-state cleanups passed and the setup state is independently reported absent; no queued/live qualification remains. The previous failed original is unchanged.

    The fixed publisher archive contains 764 symlinks. Its complete static inventory and finite regular-only input proposal are now source/design accepted through #321 at main b0ea70cf830591d2df5242524bc5529cd03be7fb, all nine PR/seven main checks green. Review scope and required implementation controls preserve the generic root parser's strict refusal. The verified temporary archive was identity/hash checked, removed and absence verified after static controls, within its selected lifetime.

    This supplies an auditable source/input proposal, not a normalization implementation or installed/setup acceptance. The next source step is the finite derived-input implementation and its own exact review before a separately bound changed operation. PHP's Ubuntu24 publisher tuple remains the next matching profile after initial-profile closure; source CI and its independent no-APT browser correction remain separate from publication admission.

  31. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    Sample's matching Python worker parent SOURCE inventory is now delivered through #330, merge ef633a2/tree f5a9dfb4fa68619d7ba8931ec03e00e66057e693. All 9 PR and 7 actual-main checks are green, and the owned remote branch is absent. The existing guard, exporter and PHP catalog are unchanged. CLI has the retained public metadata for the requested independent attribution review; that verdict is not inferred from CI.

    The new Python record names both exact native parents and publisher fixed APT pair, and explicitly records initial pip 25.0.1 with a required maintained-client bootstrap. It remains source inventory only. Sample's exact recipe/profile/first-producer packet and existing bounded CI route are #325 / 6679586695, with the 6b048bb output-compatible source successor. General has the precise first-execution prerequisite for disposition. No image acquisition/build/push, runtime/installed qualification, public publisher or completed #318 rerun was performed. After that concrete prerequisite is resolved, sample-app owns ordinary consumer PR/main/runtime acceptance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions