PR-304 is the dependency-selected Phase 12 remediation for exact daemon, API and app subject binding. It does not start Phase 13 or create a release authority. The existing maintenance freeze, original app projection and root-owned runtime supervisor retain their separate responsibilities.
Implementation began on clean develop commit 27e478c2d1d72ce9f05925626e16c0c1fdcc8c71,
tree 96f6fe8d7c17ed7a8beccaddb7625dae4c39daff. PR #1405 was confirmed merged at
2026-09-11T05:11:14Z. Its planning/final feature commit
07db8a53226cf3ee6ea5184022d16cc9c866254d has the same tree as this squash integration.
Neither identity is a candidate product, predecessor product or protected execution receipt.
The original evaluator was run and verified at fixed assessment time 2026-09-11T05:13:26Z
with a fresh private output root. It reported 49 mandatory requirements, 47 unresolved,
phaseDecision=incomplete and phaseComplete=false. Those numbers describe missing acceptance
dimensions, not a count of unimplemented features. Historical open-item Markdown and repository
status bytes remain unchanged.
| Audited gap | Supported implementation and retained boundary |
|---|---|
| Maintenance v1 has no API/app freeze relationship | Closed v2 private metadata binding; unchanged historical v1 bytes and public assets |
| Current tool can describe its own API classes | Fixed isolated export from selected packaged JAR; explicit historical ABI bridge with no fallback |
| Existing first-party producer requires a future freeze and omits experimental Mail | Prefreeze operation under the existing maintenance authority; Stable and experimental catalogs remain separate |
| App-bearing maintenance selection rejects before supervisor admission | Original complete roster, Java admission and exact per-role products reach the existing typed capability |
| Older selected build need not be the actual named predecessor | Producer validates owning lineage before freeze; consumer matches independently authenticated release/build/product and rejects archive aliases |
| Blocked-only measurement v1 cannot express a supported component | Measurement v2 and supervisor report v3 bind original product/journal derivation; nine parent rows remain blocked |
| Historical policy pins cannot be rewritten as new evidence | Immutable PR-303 policy copy and explicit version-2 implementation disposition; mandatory acceptance scope unchanged |
The prospective maintenance producer builds packages once, authenticates the original selected
app projection and its exact scoped sources, exports the selected portable JAR's API snapshot and
complete baseline registry, and runs native signed app admission against those exact bytes.
It seals runtime/runtime-subjects.json before the maintenance freeze binds that manifest.
The manifest never includes the final freeze digest. Original producer attestations cover the
freeze, public checksums and assets, and the private runtime members.
The pre-freeze signed app handoff belongs to the existing maintenance workflow. Its closed original producer class is distinct from post-freeze independent reproduction. Runtime inventory v3 carries that distinction; original API inventory v2 keeps its existing authority semantics. The handoff must precede projection and freeze and cannot require a future freeze or publication receipt. Stable seven and experimental Mail remain separately cataloged.
The two app operations are produce-app-products (Stable seven) and
produce-experimental-app-products (Stable seven plus separately cataloged experimental Mail).
Both app-product operations omit expected_predecessor_pointer_digest and reject a supplied
value: no daemon predecessor is authenticated by app-product preparation. Freeze, authorization
and publication operations still require an exact SHA-256 predecessor pointer digest.
They use the existing protected maintenance evidence environment. Set
CRYPTAD_MAINTENANCE_APP_ARTIFACT_BASE to the owner-approved artifact URL base and retain the
existing app-signing and STABLE_CATALOG_SIGNING_* environment secrets only in the producing job.
Catalog and app signing identities must be distinct. These operations prepare and attest bytes;
they do not publish a catalog or activate an app.
The prospective freeze manifest's maintenanceRuntimeInputs directory contains cohort.json,
projection-selection.json with {"coordinates": ...}, and separately scoped public trust
registries under trust/<registry basename>. The cohort pins the original authenticated tool
artifact and complete approved materialized JDK tree. Preparation dereferences internal links
from the setup-java installation into a new private maintenance-runtime-jdk directory, then
compares its exact tree digest to the existing javaTreeDigest. The prepared cohort uses that
staged path throughout projection and observation; the approved digest is never rewritten.
Dangling/cyclic links, links outside the selected installation, special files, excessive trees
and changed bytes reject. Link-free validation remains mandatory. Distro JDKs that link external
configuration files are not supported by this internal-link adapter. The freeze helper materializes
these exact inputs in its protected ephemeral job. Release-signing credentials never enter the
persistent runtime supervisor. The installed cohort remains root-owned and is readable only by
root and the producing runner's primary group (0640); its directory remains root-owned without
unprivileged write access. The producer runs as the ordinary runner. Offline metadata validation performs no process execution or
network collection; original artifact collection and exact-package observation are explicit
producer/admission operations.
Freeze v1 remains immutable. A strict version dispatcher accepts original v1 and prospective closed
v2; v2 adds runtimeMetadata while leaving the public asset roles and public checksum rows intact.
The metadata directory contains the manifest, exact snapshot, complete registry, original projection
inventory, and native admission declarations. These internal members remain outside public release
assets and publication plans. Preparation and frozen handoff retention preserve their original bytes.
The fixed network.crypta.platform.api.PackagedApiExport entry point takes no arguments. It emits
static contract and complete registry JSON as exact strings. Protected orchestration extracts only
lib/cryptad.jar from the governed portable archive and invokes that fixed class in an isolated,
credential-free, bounded process with a controlled classpath. Archive and executable identities are
checked before and after observation. Snapshot byte digest and canonical static contract digest
are distinct. Matching integer labels cannot conceal a different surface. The /api/v1 URL,
integer contract, baseline name and integer daemon build remain separate identities.
The fixed historical bridge uses a platform-parent isolated classloader and four reviewed ABI calls
on the selected original JAR. It cannot fall back to API classes on the current helper classpath.
An unsupported original ABI remains blocked. The observed snapshot is labeled
observed-from-original-package, never frozen-with-original-release. Original RC app-product and
portable producer authorities remain distinct; this observation does not independently authenticate
GA publication. The prospective freeze first reuses the owning maintenance validator's GA-root and
latest-predecessor graph checks. Runtime admission then matches its authenticated predecessor
release/build/source/product identity against the independently reopened original product.
The prospective observation additionally binds the predecessor source commit; v1 remains unchanged.
GA publication and original RC release IDs may differ, so that path compares the exact RC product
digest, source and integer build and retains both names instead of demanding universal equality. A GA-derived
RC product is therefore selected through the owning release's predecessor observation, without
requiring the historical GA to possess future freeze-v2 metadata. A maintenance predecessor keeps
its original maintenance-product authority. The original candidate observation's baseline, receipt,
pointer and clock remain explicitly labeled candidate-freeze-observed-published-predecessor.
The same archive cannot be relabeled as the candidate's predecessor.
A historical source rebuild is never an original shipped package. Gates requiring original frozen
API evidence remain blocked when that authority lacks it.
The complete shipped Stable set remains the original seven first-party apps. The independently approved experiment cohort comes from the existing protected app-subject policy and authenticated inventory, including its external submission and explicit experimental Mail opt-in. The per-role installed subset is separately fixed: sender and recipient use Site Publisher, Feed Reader and opted-in Mail; the predecessor uses Site Publisher and Feed Reader; the relay installs no apps. Omitting a required role app rejects. Experimental Mail does not enter the Stable shipped set.
The Java subject-projection command retains its original v1 declaration mode. Paired --contract
and --baseline-registry inputs select a prospective v2 declaration after exact registry-summary
verification and shared native PlatformApiAppAdmission. The separately identified release
strict-tested-range policy remains stricter than ordinary runtime warning semantics. Static
admission grants no permissions and establishes no successful runtime scenario. Ordinary AppHost
publisher, review, origin, sandbox, consent and source-pinning rules continue to apply. Selected
federation projection remains unsupported; keys are not merged globally to bypass scoped trust.
authenticate_products consumes original maintenance v2 freeze artifacts and independently
reauthenticates their app projection. Its existing sealed in-process capability binds the exact plan,
archive and required private app paths. Root supervisor activation preserves the safe identities;
launch and continuation recheck exact inputs and corroborate the running static contract surface.
A plausible endpoint or serialized admission flag cannot create original authority.
Historical measurement v1 remains blocked-only diagnostic data. Prospective measurement v2 derives separate subject admission and journal derivation components from the exact plan, original journal, checkpoint and admitted product rows. Supervisor report v3 binds their complete product-set digest through start, checkpoints and finish. Phase 12 reopens the original report chain and recomputes the measurement at its original evaluation cutoff; later assessment does not refresh observation time.
A narrow subject/derivation component can pass while every broader maintenance row remains blocked. All nine rows retain their named missing adapter reasons. Required case/direction sets, routine or hotfix policy windows, cleanup and uncertain outcomes remain separate. No supplementary caller subcase, requested duration, fake clock, node-hour sum or pre-freeze work satisfies observed coverage. A changed freeze or app cohort requires a new admission and experiment binding.
The later PR-307 companion path adds freeze v3 for selected federation, retaining v1/v2 semantics and their private-input rejection. It encrypts the entire runtime set, authenticates ciphertext before opening, and keeps native relations in a private capability. Supervisor v5/measurement v4 use ciphertext identities in outward projections.
Federated projection/catalog-origin runtime coverage is the provisional PR-305 dependency handoff.
Scheduler pressure, historical and independent profile readers, migration and Mail recovery,
complete API/runtime and sandbox/security/support cohorts remain separate adapter work. Original
provider configuration, independent reviews, real observations and public deployment proof retain
their owners. Full p12-300-consumers, p12-300-adapters, p12-300-72h and Phase 12 completion are
not established by these local components.
No protected workflow was dispatched, catalog or release published, live node operated, real Mail or migration data accessed, PR merged, or 72-hour experiment performed during this implementation. Synthetic test authorities and transport seams cannot be selected as production trust flags.
./gradlew spotlessApply test passed with 17,201 JUnit tests, zero failures or errors and ten
skips. The skips cover a disabled benchmark, Windows/non-Linux directions and existing
crypto/provider assumptions. After the final native edits, focused admission/projection/export
checks passed 37 tests without skips (20 API admission, 16 signed projection, one real compiled
implementation/classpath test). These are local results, not hosted exact-source receipts.
./gradlew build assembleCryptadDist :platform-devtools:installDist passed. Local Linux DEB
packaging ran; RPM was skipped by the current environment, and other native operating-system
packaging was not executed. The final devtools formatting, tests and SpotBugs test task passed.
Inspected SpotBugs reports retain existing findings: API main/test 139/27; devtools main/test 8/9.
None refer to the touched admission/exporter/projection/fixture classes. Existing compiler warnings
remain, and SonarLint tasks were skipped by repository configuration; task success is not an
analyzer-clean claim.
| Python check | Final local result |
|---|---|
| cross-version-soak | 92 passed |
| stable-platform-api-1x | 88 passed |
| stable-maintenance | 243 passed |
| stable-maintenance-drill | 13 passed |
| stable-supply-chain | 86 passed |
| phase-12-closeout | 175 passed |
| stable-rc | 55 passed |
| stable-ga | 83 passed |
| public-ecosystem-transparency | 91 passed |
| release-certification | 50 passed |
| protected unittest discovery | 85 passed |
| Real signed producer/product/journal/Phase12 integration | 1 passed, 89.098 seconds, no skips |
| Focused supervisor / runtime / measurement checks | 16 / 66 / 14 passed |
The protected suite includes 11 bounded package/metadata tests and five owning GA/maintenance predecessor-graph tests. The integration signs actual app bundles and catalogs, observes two compiled API implementations with the same contract integer, and executes both maintenance and historical product branches through native admission and original-context measured consumption. Wrong package, API surface, cohort, original predecessor and source identities reject. Provider transport/attestation and OS-ownership seams use isolated test authorities. It does not claim to run a protected release, a live daemon workload, or the full production packaging matrix.
Actionlint passed for CI, maintenance and supply-chain workflow changes. The ordinary Linux PR
integration job builds the native tools and fails if the integration is skipped; it uses no
production secrets or protected environment. Its hosted execution has not occurred here.
git diff --check passed. Long-running release-certification checks were allowed to finish.
Policy version 2 records the explicit pr304-runtime-subject-binding-successor scope decision.
The exact prior policy is retained under
tools/release-certification/history/phase-12-acceptance-policy-pr303.json. All 49 mandatory
requirement IDs, assertions, dimensions, subjects, prerequisites, owner mappings, closure rules
and original historical inputs remain unchanged. Updated implementation paths and their exact
source/test pins cover the producer, exporter, product admission and narrow original consumers.
This is a reviewable implementation delta, not permission to close missing operational evidence.
| Identity | Prior PR-303 | PR-304 successor |
|---|---|---|
| Policy byte digest | sha256:2217b58c2ff3f316f0105d1f93bba8122921ff35d42c836910bf9ec37d11ca3e |
sha256:9a61b4b20646f1738c0cf0878580b37e1a2eb4f1d16c692144055e484c2f846e |
| Effective evaluator tool digest | sha256:7e8b217a90b16e67bba111f5eabcee294c787322b356e044846b8d98e0b1966e |
sha256:a572d9792af335933824782c169e44ddcabed55a60ae5b8a1be5499996249bea |
The evaluation and verification used 2026-09-11T05:13:26Z in separate fresh private roots.
Both assessments report phaseDecision=incomplete, phaseComplete=false and 47 unresolved
requirements. Both --require-complete checks reject with exit 2. The original assessment and
historical source bytes were retained. The precommit assessment’s checkout commit/tree identify the starting
integration; its effective tool digest and policy source pins identify the prepared changes.
| Requirement | Prior implementation | Successor implementation | Other repository-only dimensions |
|---|---|---|---|
| p12-300-products | partial | implemented for the supported contract | localVerification not-run; originalProvenance not-supplied; coverage missing |
| p12-300-consumers | partial | partial | localVerification not-run; originalProvenance not-supplied; coverage missing |
| p12-296-subjects | implemented | implemented | localVerification not-run; originalProvenance not-supplied; coverage missing |
The evaluator does not import this local test log as an authenticated original evidence selection,
so localVerification=not-run in those requirement rows is expected despite the separate tests
above. product-subject-binding retains its original open reason and clock, with an explicit
implementation-prepared-observation-outstanding disposition. Federation, complete adapters,
72-hour coverage, Mail/migration scenarios, independent providers/reviews and public proof remain
open. The unresolved count was not used as an acceptance target.
PR-305 remains the next implementation workstream after this supported foundation: selected federation projection and catalog-origin runtime coverage. The assessment continues to retain product-original observation prerequisites; it does not treat prepared code as their fulfillment.
The cohort installation regression executes the actual workflow install commands in a temporary
directory as an unprivileged Linux user. It confirms native cohort loading succeeds, direct writes
and unlink attempts fail, and an unrelated user cannot read the file. It requires passwordless
sudo and reports a skip when that hosted-runner prerequisite is unavailable. No system /etc
configuration is changed by the test.
The JDK preparation regression exercises the actual preparation function with internal shared legal links and an independently specified materialized identity. The full compiled-package integration also invokes the production JDK staging helper before native execution. Original provider authentication is isolated in the preparation test; no JDK is downloaded or executed before the approved staged digest matches.
Both packaged exporters use Java 25 package-private static entry points; the historical exporter also uses an unnamed catch parameter. Their stdout JSON and the historical exporter's fixed stderr failure code remain protocol output with method-scoped S106 suppressions. The actual subprocess export integration passed after this cleanup, and file-level SonarLint reported no findings.
The first PR-304 hosted integration attempt failed during JDK setup because its Java version
selector was not accepted by setup-java. The ordinary CI job now uses the supported Java 25
selector shared by the other CI jobs. The successor policy refreshes only the affected workflow
and test byte pins; acceptance scope and historical policy bytes are unchanged. Protected release JDK
selections are outside this correction. Hosted execution must be verified against the new head.
The same job also requested :platform-api:jar explicitly, which the repository build guard
rejects. Its build command now requests only :platform-devtools:installDist, whose dependency
graph produces the required API JAR. The test prerequisite diagnostic uses the same command.
The hosted Ubuntu runner also denied Bubblewrap's isolated loopback setup (RTM_NEWADDR).
An early no-product sandbox probe reproduces this prerequisite failure directly. The shared disposable-runner setup
loads a checked-in AppArmor user-namespace permission attached only to /usr/bin/bwrap.
AppArmor and the system-wide user-namespace restriction remain enabled; the exporter retains
--unshare-all, read-only input mounts, bounded temporary storage and execution limits. This
runner setup now also runs prospectively in the freeze job; no protected workflow or live
system was operated while implementing it.
A subsequent coverage pass added ten Java regression cases: selected-target admission below the app's minimum, incomplete/malformed projection targets, unexpected exporter arguments, missing or malformed historical packages, absent historical API classes despite a populated helper classpath, and manifest-supplied external classes. The three affected test classes passed 47 tests with no failures, errors or skips. Production Java is unchanged. The successor policy refreshes the existing exporter integration-test byte pin without changing scope or treating these tests as runtime receipts.
Review follow-up: app-product preparation selects each app's unique packaging output, so the
Site Publisher 3.1 app version remains independent of the daemon build number. Missing,
ambiguous and linked package inputs reject; native signed manifest/catalog verification remains
mandatory. Ordinary integration and freeze-candidate now share
.github/actions/setup-packaged-export-sandbox, provisioning Bubblewrap and its scoped AppArmor
profile and testing isolation before any freeze metadata is sealed. The signed integration fixture
uses the independent Site Publisher version to exercise the actual producer.
Projection tools retain their separately approved toolOriginal.sourceCommit. That tool revision
need not equal the candidate, predecessor, or consuming workflow SHA. Original artifact
authentication and member attestation verify the pinned tool source/signer and run attempt, then
both archive and installed-tree digests must match the protected cohort. The develop-only tool
producer remains unchanged. Preparation tests exercise distinct cohort/consumer/tool revisions
and reject wrong source proofs, attempts, producer families and installed bytes; the signed
producer/consumer integration uses a third tool revision distinct from both daemon products.
An exporter-only coverage pass adds eight subprocess regressions for exact JSON envelope bytes and preserved snapshot whitespace, symlink/directory rejection, and compiled historical ABI failures (missing method, throwing factory, initialization error and wrong serializer return type). Both production exporter classes are unchanged. All 16 tests in the owning exporter integration class passed locally with no skips; the symlink case is scoped to Linux/macOS. These are local tests, not protected release receipts or a measured coverage percentage.
The macOS CI fixture correction resolves the temporary root before constructing authenticated product paths. A symlinked temporary root reproduced the original package-substitution rejection on Linux; after correction all 23 product-admission tests passed under the same setup. Production symlink rejection remains unchanged. The successor policy refreshes only that test's existing source digest; requirement scope and historical inputs are unchanged.
Exporter subprocess tests now propagate the test worker's JaCoCo agent with append enabled and instrumentation restricted to the two production exporters. The fixed commands, clean environment, exact fixture package bytes, process exit checks and private diagnostic assertions remain intact. Recompiled fixture contract classes do not contribute coverage. API and root coverage reports and verification tasks include devtools execution data, because those tests execute the API exporter across the module boundary. This changes test measurement only, with no production instrumentation, coverage exclusion or threshold change. The local JaCoCo XML, mapped to SonarCloud PR 1406's current new executable lines and conditions, records 74/75 covered lines and 23/26 covered conditions: 97/101, or 96.0% new-code coverage. The native exporter is 100.0% and the historical bridge 91.7%; the other two changed production classes remain at 100.0%. These are locally measured results; the hosted percentage requires the subsequent CI scan. The bridge's remaining defensive classloader branches stay in scope. The final exporter suite adds a positive ordinary-manifest case (no external Class-Path), exercising the layout accepted for real package manifests, and passes all 17 tests. Both module suites pass (261 devtools, 1,165 API); the preceding full test/report run recorded 17,220 tests with zero failures/errors and ten existing platform, benchmark and crypto-condition skips.
The protected Python discovery fixtures also canonicalize temporary roots for JDK preparation, app package selection and runtime metadata checks. Running the full protected suite under a symlinked temporary parent reproduces the macOS path-precondition failures; after correction all 91 tests pass locally. An explicit regression still rejects a symlinked subject parent even when the file bytes match. Only test-source pins change; production path checks remain intact.
Prospective app handoffs bind their authenticated releaseId, integer buildVersion and product
sourceCommit to the candidate freeze before sealing. Cohort production also compares release
and product source, so changing only the cohort header cannot relabel an older app handoff.
Every first-party or experimental Mail source in a prospective freeze must use the maintenance
app-product authority; external submissions retain their separate reviewed authority. The original
artifact coordinates still bind the workflow revision, signer and attempt, which may legitimately
differ from the separately attested product source. No universal SHA equality is introduced.
The real producer/exporter integration rejects substituted release, build and source identities
without emitting a frozen artifact or retaining runtime metadata; isolated test authorities do
not create operational receipts. Existing historical formats keep their original meaning.
Normal maintenance and security-hotfix candidate authentication now includes the authenticated predecessor source commit in its default freeze-observation mapping. Explicit follow-up mappings remain authoritative. The caller regression fails for both normal release classes without this fix; v1 remains compatible and v2 still rejects a substituted predecessor source independently of its runtime-metadata gate. No schema, historical input or acceptance scope is changed.
Candidate-freeze regression tests live in test_stable_maintenance_freeze.py, registered in the
Stable maintenance self-test suite and discovered by the aggregate certification suite. This
keeps the original maintenance test module below the repository's 5,000-line limit while retaining
both predecessor source-binding regressions and all 247 maintenance tests. The size limit is
unchanged; the split changes test organization only.