Skip to content

Commit 0a28b89

Browse files
authored
Merge pull request #77 from TrentBrown/tb-portreeve-apple-trust-05-notarization-submit-recovery
Preserve Apple notarization request recovery
2 parents 4f4610f + 4f92350 commit 0a28b89

20 files changed

Lines changed: 1173 additions & 74 deletions

.github/workflows/release.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ jobs:
216216
bun run release:produce-apple-trust -- --record
217217
"$RELEASE_ROOT/release-record.json" --qualification
218218
"$RELEASE_ROOT/evidence/trust-qualification.json" --output "$TRUSTED_ROOT"
219+
- name: Preserve failed notarization recovery evidence
220+
if: failure()
221+
uses: actions/upload-artifact@v7
222+
with:
223+
name: trusted-recovery-${{ inputs.version }}-${{ github.run_attempt }}
224+
path: ${{ env.TRUSTED_ROOT }}/recovery
225+
if-no-files-found: warn
226+
retention-days: 7
219227
- uses: actions/upload-artifact@v7
220228
with:
221229
name: trusted-${{ inputs.version }}

docs/issues/tb-portreeve-apple-trust/decisions.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,22 @@ When the protected producer transforms either macOS CLI, it must rewrite the cor
5959
Defer metadata repair until finalization - rejected because native verification must consume a self-consistent protected output. Keep separate unsigned and signed manifests - rejected because multiple authorities would make downstream selection ambiguous. Rebuild metadata independently in each native job - rejected because read-only verifiers must not mutate the producer output.
6060

6161
**Promoted:** 2026-08-29. PR: https://github.com/TrentBrown/portreeve/pull/76.
62+
63+
---
64+
65+
## Persist notarization continuity at the producer boundary
66+
67+
**Confidence:** HIGH
68+
69+
**Blast Radius:** Protected Apple producer, failure artifacts, exact signed DMG retention, workflow uploads, and notarization evidence
70+
71+
Treat successful notarytool submit output as request creation even when status is absent, while keeping info responses strict. Drive the real producer through the existing finite recovery state machine, persist every non-secret state transition beside the exact signed DMG before and after Apple calls, and upload only that recovery directory when the protected job fails. Once a request ID exists, all continuation targets that ID; no new submission is allowed. Successful candidates retain the recovery history but move the authoritative DMG into the normal trusted artifact set.
72+
73+
**Triggered by:** Live run 33267482516 returned a valid Apple request ID without status, then the producer rejected the response and deleted its output
74+
75+
**Alternatives considered:**
76+
- Add `notarytool --wait` - rejected because it bypasses the approved explicit polling and continuity model.
77+
- Default missing status without recording the request lifecycle - rejected because a later failure would again erase continuity.
78+
- Keep deleting output on failure - rejected because exact signed bytes and failed-attempt evidence would disappear.
79+
80+
**Promoted:** 2026-08-29. PR: https://github.com/TrentBrown/portreeve/pull/77.

docs/issues/tb-portreeve-apple-trust/events.jsonl

Lines changed: 39 additions & 0 deletions
Large diffs are not rendered by default.

docs/issues/tb-portreeve-apple-trust/issues.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ unconditional cleanup.
6464

6565
## I-5 - Produce and aggregate native trust evidence
6666

67-
- **Status:** in-review
67+
- **Status:** closed
6868
- **Estimate:** 2d
6969
- **Plan steps:** P5
7070
- **Rubric criteria:** R3, R5, R7
@@ -77,7 +77,7 @@ aggregation require one current document for each native architecture.
7777

7878
## I-6 - Seal finalization and publication boundaries
7979

80-
- **Status:** in-review
80+
- **Status:** closed
8181
- **Estimate:** 2d
8282
- **Plan steps:** P6
8383
- **Rubric criteria:** R1, R2, R6, R7
@@ -90,7 +90,7 @@ recovery and disjoint Apple/publication authority.
9090

9191
## I-7 - Complete operator documentation and regression suites
9292

93-
- **Status:** in-review
93+
- **Status:** closed
9494
- **Estimate:** 1d
9595
- **Plan steps:** P7
9696
- **Rubric criteria:** R1, R3, R4, R5, R6, R7
@@ -103,7 +103,7 @@ the focused and broad verification matrices.
103103

104104
## I-8 - Run protected rehearsal and complete verification
105105

106-
- **Status:** open
106+
- **Status:** in-progress
107107
- **Estimate:** 1d
108108
- **Plan steps:** P8
109109
- **Rubric criteria:** R1, R2, R3, R4, R5, R6, R7, R8
@@ -114,3 +114,20 @@ From reviewed code pinned on `main`, produce and inspect the complete trusted
114114
preview packet with both native authorities and publication disabled. Preserve
115115
zero-public-mutation evidence; route defects through fresh sequential slices
116116
before full feature evaluation and close-out.
117+
118+
## I-9 - Correct asynchronous notarization submission recovery
119+
120+
- **Status:** in-review
121+
- **Estimate:** 1d
122+
- **Plan steps:** P2, P8
123+
- **Rubric criteria:** R4, R7, R8
124+
- **Depends on:** I-2, I-4, I-8
125+
- **PR:** [#77](https://github.com/TrentBrown/portreeve/pull/77)
126+
127+
Correct the protected producer defect observed in hosted run
128+
`33267482516`: asynchronous `notarytool submit` returned a valid request ID
129+
without a status, but the producer required status before beginning its poll
130+
loop. Integrate the existing bounded recovery state machine into the real
131+
producer, preserve exact signed candidates and non-secret request history on
132+
failure, add failure-only workflow upload, and prove one-submit request
133+
continuity with producer-level tests before another protected attempt.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"schemaVersion": 1,
3+
"scope": "slice",
4+
"featureId": "tb-portreeve-apple-trust",
5+
"repositoryAlias": "portreeve",
6+
"packetId": "pr-77",
7+
"pullRequest": {
8+
"repository": "TrentBrown/portreeve",
9+
"number": 77,
10+
"url": "https://github.com/TrentBrown/portreeve/pull/77",
11+
"state": "OPEN",
12+
"isDraft": true,
13+
"baseRefName": "main",
14+
"baseRefOid": "4f4610f27639a09ba53692757971ea0ce7af7061",
15+
"headRefName": "tb-portreeve-apple-trust-05-notarization-submit-recovery",
16+
"headRefOid": "048bee8901d13780a47ef19237c1bdf06ab4e3ed"
17+
},
18+
"mergeBaseSha": "4f4610f27639a09ba53692757971ea0ce7af7061",
19+
"evaluatedSourceSha": "048bee8901d13780a47ef19237c1bdf06ab4e3ed",
20+
"featureBaseSha": null,
21+
"applicability": {
22+
"specEvaluation": true,
23+
"judge": true,
24+
"patternReview": true
25+
},
26+
"gates": {
27+
"verification": {"disposition": "passed", "reason": null},
28+
"specEvaluation": {"disposition": "passed", "reason": null},
29+
"judge": {"disposition": "passed", "reason": null},
30+
"codeReview": {"disposition": "passed", "reason": null},
31+
"patternReview": {"disposition": "passed", "reason": null},
32+
"explainDiff": {"disposition": "passed", "reason": null}
33+
}
34+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Code Review - PR #77
2+
3+
**Pinned diff:** `4f4610f27639a09ba53692757971ea0ce7af7061..048bee8901d13780a47ef19237c1bdf06ab4e3ed`
4+
5+
**Verdict:** PASS
6+
7+
## Findings
8+
9+
No actionable findings remain at the corrected pinned source.
10+
11+
The first boundary review found that the producer submitted a recovery copy
12+
and then stapled that same file in place. A later failure could therefore
13+
retain post-staple bytes that no longer matched the pre-staple SHA-256 bound to
14+
Apple's request. GateReeve entered remediation before review was requested.
15+
Commit `048bee8901d13780a47ef19237c1bdf06ab4e3ed` preserves the submitted copy
16+
unchanged and performs stapling, Gatekeeper assessment, mounted verification,
17+
and final artifact staging on a distinct working copy. The added regression
18+
test mutates the working copy and proves the submitted SHA-256 is unchanged.
19+
20+
## Review Evidence
21+
22+
- Submit output parsing accepts a valid UUID without inventing a status;
23+
strict `info` parsing supplies the status authority.
24+
- Recovery state is persisted atomically before and after Apple calls and is
25+
bound to release ID plus candidate SHA-256.
26+
- A known request is polled and never resubmitted; nonzero submit results still
27+
preserve any valid machine-readable UUID before failing.
28+
- Failure-only upload contains only the recovery directory, uses a bounded
29+
retention period, and excludes keychain/P12/P8/password material.
30+
- The trusted producer still has no publication authority, and the correction
31+
does not change installer architecture, channels, or public state.
32+
33+
## Residual Risks and Test Gaps
34+
35+
- The corrected path has not yet called Apple's live service or produced the
36+
two current native evidence documents; that is the next feature-final slice.
37+
- The failure artifact path is validated by producer and workflow contract
38+
tests; its hosted retention must be observed if the protected rerun fails.

0 commit comments

Comments
 (0)