Skip to content

Commit 2042850

Browse files
authored
Merge PR #79: Gatekeeper optional origin correction
Accept Gatekeeper assessments that omit optional origin display metadata while preserving independent exact codesign identity and Team ID authority.
2 parents de43dae + b7ff766 commit 2042850

17 files changed

Lines changed: 449 additions & 22 deletions

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,28 @@ Treat either a bare 'accepted' status line or spctl's real '<assessed path>: acc
9595
Strip the path before parsing - rejected because the parser should validate the complete command result contract in one place. Accept any occurrence of the word accepted - rejected because unrelated diagnostic text could create a false positive. Replace Gatekeeper with notarization status alone - rejected because the approved design requires both independent facts.
9696

9797
**Promoted:** 2026-08-29. PR: #78.
98+
99+
---
100+
101+
## Treat Gatekeeper origin as optional display metadata
102+
103+
**Confidence:** HIGH
104+
105+
**Blast Radius:** Protected producer and both native Apple trust evidence collectors
106+
107+
Require Gatekeeper exit zero, an exact accepted status line, and
108+
`source=Notarized Developer ID`. If `spctl` emits `origin=`, require the exact
109+
PortReeve Developer ID identity; if it omits that display field, retain the
110+
successful assessment without inventing an origin. Exact Developer ID identity,
111+
Team ID, hardened runtime, and secure timestamp remain independently mandatory
112+
through `codesign` facts for every CLI, application, and DMG.
113+
114+
**Triggered by:** Live preview.7 run 33272715923 returned a valid accepted primary-signature assessment without an `origin=` line
115+
116+
**Alternatives considered:**
117+
118+
- Continue requiring `origin=` - rejected because live `spctl` can omit it from an accepted notarized assessment.
119+
- Synthesize the expected origin when absent - rejected because evidence must not invent command output.
120+
- Drop exact identity verification - rejected because the approved design requires independent Developer ID and Team ID authority.
121+
122+
**Promoted:** 2026-08-29. PR: #79.

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

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

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ continuity with producer-level tests before another protected attempt.
134134

135135
## I-10 - Accept real Gatekeeper assessment output
136136

137-
- **Status:** in-review
137+
- **Status:** closed
138138
- **Estimate:** 0.5d
139139
- **Plan steps:** P2, P8
140140
- **Rubric criteria:** R4, R5, R7, R8
@@ -148,3 +148,20 @@ of the real `<assessed path>: accepted` status line. Preserve every existing
148148
exit-status, source, and exact-origin requirement; add positive real-shape and
149149
negative rejected-shape coverage; land the correction on `main` before the
150150
next protected attempt.
151+
152+
## I-11 - Accept omitted Gatekeeper origin display metadata
153+
154+
- **Status:** in-review
155+
- **Estimate:** 0.5d
156+
- **Plan steps:** P2, P5, P8
157+
- **Rubric criteria:** R4, R5, R7, R8
158+
- **Depends on:** I-10
159+
- **PR:** [#79](https://github.com/TrentBrown/portreeve/pull/79)
160+
161+
Correct the second live Gatekeeper compatibility defect observed in hosted run
162+
`33272715923`: Apple accepted the exact request-bound DMG and `spctl` returned
163+
exit zero, a path-prefixed `accepted` line, and
164+
`source=Notarized Developer ID`, but omitted the optional `origin=` display
165+
line. Permit that field to be absent while rejecting any present wrong origin;
166+
retain exact Developer ID and Team ID authority through the independently
167+
required `codesign` facts.
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-79",
7+
"pullRequest": {
8+
"repository": "TrentBrown/portreeve",
9+
"number": 79,
10+
"url": "https://github.com/TrentBrown/portreeve/pull/79",
11+
"state": "OPEN",
12+
"isDraft": true,
13+
"baseRefName": "main",
14+
"baseRefOid": "de43dae24f2629748b1c1a3376c478e183e0ec33",
15+
"headRefName": "tb-portreeve-apple-trust-09-gatekeeper-origin",
16+
"headRefOid": "31da295f7359c25347b96a9d979421bed565671b"
17+
},
18+
"mergeBaseSha": "de43dae24f2629748b1c1a3376c478e183e0ec33",
19+
"evaluatedSourceSha": "31da295f7359c25347b96a9d979421bed565671b",
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Code Review - PR #79
2+
3+
**Pinned diff:** `de43dae24f2629748b1c1a3376c478e183e0ec33..31da295f7359c25347b96a9d979421bed565671b`
4+
5+
**Verdict:** PASS
6+
7+
## Findings
8+
9+
No actionable findings remain at the pinned source.
10+
11+
`parseGatekeeperFacts` continues to require a zero exit code, an anchored bare
12+
or path-prefixed `accepted` status, and the exact
13+
`source=Notarized Developer ID`. It conditionally includes `origin` only when
14+
the command emitted it, and rejects every emitted value other than PortReeve's
15+
expected Developer ID identity.
16+
17+
`assertAppleNativeTrustEvidence` mirrors that contract without weakening the
18+
separate exact `codesign` requirements for identity, Team ID, secure timestamp,
19+
and hardened runtime. This avoids fabricating missing command output while
20+
keeping the actual signing authority mandatory.
21+
22+
## Coverage and Residual Risk
23+
24+
- The live no-origin shape and present-wrong-origin shape have direct parser
25+
tests.
26+
- Native evidence accepts an omitted DMG Gatekeeper origin only while its exact
27+
codesign authority remains present, and rejects a forged displayed origin.
28+
- Existing source, status, rejected, exit-code, codesign, notarization, staple,
29+
and aggregation negatives continue to pass.
30+
- The remaining risk is platform output variation on the later standalone CLI
31+
or mounted application assessments. The shared parser and native evidence
32+
checks cover the same optional-field behavior, but preview `.8` must still
33+
demonstrate both native runners and final aggregation.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>PR #79 - Gatekeeper optional origin correction</title>
7+
<style>
8+
:root { color-scheme: light dark; --accent:#0a7a68; --card:#ffffff12; --border:#7f8c8d55; }
9+
body { font: 16px/1.55 system-ui, sans-serif; max-width: 960px; margin: 0 auto; padding: 2rem; }
10+
h1,h2 { line-height:1.2; } h1 { color:var(--accent); }
11+
section { border:1px solid var(--border); background:var(--card); border-radius:12px; padding:1.1rem 1.3rem; margin:1rem 0; }
12+
code,pre { font-family:ui-monospace, monospace; } pre { overflow:auto; padding:1rem; border-radius:8px; background:#111; color:#eee; }
13+
.flow { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:.6rem; text-align:center; }
14+
.flow div { padding:.8rem; border:1px solid var(--border); border-radius:8px; }
15+
.good { color:#17a673; font-weight:700; } .pending { color:#d89500; font-weight:700; }
16+
.quiz label { display:block; margin:.45rem 0; } button { padding:.5rem .8rem; cursor:pointer; }
17+
.answer { min-height:1.5rem; font-weight:700; }
18+
</style>
19+
</head>
20+
<body>
21+
<h1>PR #79: accept an omitted Gatekeeper origin</h1>
22+
<p><strong>Pinned source:</strong> <code>31da295f7359c25347b96a9d979421bed565671b</code></p>
23+
<p>This correction accepts a real Gatekeeper assessment that omitted one optional display field. It does not weaken PortReeve's independent signing identity, Team ID, hardened-runtime, timestamp, notarization, staple, architecture, or publication checks.</p>
24+
25+
<section>
26+
<h2>What happened</h2>
27+
<div class="flow">
28+
<div>Apple notarized preview .7</div><div><code>spctl</code> exited zero</div><div>Status and source were accepted</div><div><code>origin=</code> was absent</div><div>Producer stopped safely</div>
29+
</div>
30+
<p>The exact replay emitted:</p>
31+
<pre>/tmp/PortReeve-0.1.0-preview.7-macos-arm64.dmg: accepted
32+
source=Notarized Developer ID</pre>
33+
</section>
34+
35+
<section>
36+
<h2>The code change</h2>
37+
<pre>- if (origin !== APPLE_SIGNING_IDENTITY) throw ...
38+
+ if (origin !== undefined &amp;&amp; origin !== APPLE_SIGNING_IDENTITY) throw ...</pre>
39+
<p>An omitted origin remains omitted in the evidence object. If the command does emit an origin, it must still exactly match <code>Developer ID Application: Trent Brown (PMWYD5A82A)</code>.</p>
40+
</section>
41+
42+
<section>
43+
<h2>Where identity authority lives</h2>
44+
<p>Gatekeeper still must return exit zero, an anchored accepted status, and exactly <code>source=Notarized Developer ID</code>. Separately, each native evidence subject must carry exact <code>codesign</code> facts for the Developer ID identity, Team ID, hardened runtime, and secure timestamp.</p>
45+
<p>This separation matters: an optional display line cannot become the sole authority for who signed the artifact, and absent output is never synthesized.</p>
46+
</section>
47+
48+
<section>
49+
<h2>Scope and evidence</h2>
50+
<p class="good">Slice checks pass: 577 repository tests, 3025 assertions, zero failures.</p>
51+
<p>The recovered preview .7 DMG reproduced the accepted no-origin shape locally. Direct negative tests reject a wrong displayed origin and a wrong independent signing identity. No credentials, release topology, channel, public metadata, or publication path changed.</p>
52+
<p class="pending">Feature completion is still pending.</p>
53+
<p>After merge, unused preview .8 must run from reviewed <code>main</code>, produce both native architecture evidence documents and the sealed distribution packet, and prove public state remains unchanged.</p>
54+
</section>
55+
56+
<section class="quiz">
57+
<h2>Review check</h2>
58+
<form id="quiz">
59+
<div data-answer="b"><p>1. Why did preview .7 stop?</p><label><input type="radio" name="q1" value="a"> Apple rejected notarization</label><label><input type="radio" name="q1" value="b"> Gatekeeper omitted an optional origin display line</label></div>
60+
<div data-answer="a"><p>2. Does the parser invent an origin when none was printed?</p><label><input type="radio" name="q2" value="a"> No</label><label><input type="radio" name="q2" value="b"> Yes</label></div>
61+
<div data-answer="b"><p>3. What happens if Gatekeeper prints the wrong origin?</p><label><input type="radio" name="q3" value="a"> It is ignored</label><label><input type="radio" name="q3" value="b"> The evidence is rejected</label></div>
62+
<div data-answer="a"><p>4. Which independent evidence still binds the exact signer?</p><label><input type="radio" name="q4" value="a"> <code>codesign</code> identity and Team ID facts</label><label><input type="radio" name="q4" value="b"> The artifact filename</label></div>
63+
<div data-answer="b"><p>5. What proves the feature complete?</p><label><input type="radio" name="q5" value="a"> This parser correction alone</label><label><input type="radio" name="q5" value="b"> A main-pinned two-architecture nonpublishing preview .8 rehearsal</label></div>
64+
<p><button type="button" id="grade">Grade</button></p><p class="answer" id="result" aria-live="polite"></p>
65+
</form>
66+
</section>
67+
<script>
68+
document.getElementById('grade').addEventListener('click', () => {
69+
const items=[...document.querySelectorAll('[data-answer]')];
70+
const score=items.filter((item,i)=>document.querySelector(`input[name=q${i+1}]:checked`)?.value===item.dataset.answer).length;
71+
document.getElementById('result').textContent=`${score} / ${items.length} correct`;
72+
});
73+
</script>
74+
</body>
75+
</html>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Independent Judge - PR #79
2+
3+
**Pinned diff:** `de43dae24f2629748b1c1a3376c478e183e0ec33..31da295f7359c25347b96a9d979421bed565671b`
4+
5+
**Scope:** Slice 9 (`I-11`), P2, P5, and P8; rubric R4, R5, R7, and R8
6+
7+
**Verdict:** PASS for the correction slice
8+
9+
## Evaluation
10+
11+
- The change matches live run `33272715923`: Apple accepted the exact DMG and
12+
Gatekeeper returned exit zero, accepted status, and notarized source while
13+
omitting only `origin=`.
14+
- It preserves the approved separation between Gatekeeper acceptance and exact
15+
Developer ID authority. `codesign` still supplies required identity, Team ID,
16+
hardened runtime, and timestamp facts.
17+
- It never invents an origin. A present wrong origin fails, as does a wrong
18+
independent codesign identity.
19+
- The producer, native evidence schema, parser fixtures, and negative fixtures
20+
use one consistent optional-field contract.
21+
- The slice changes no release topology, credential custody, permissions,
22+
publication path, channel, or public state.
23+
- The tracker correctly leaves feature-level completion `NOT YET` until a new
24+
`.8` run produces both native documents and the sealed packet.
25+
26+
No contradiction, scope expansion, or missing slice-level test was found.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Pattern Review - PR #79
2+
3+
**Pinned diff:** `de43dae24f2629748b1c1a3376c478e183e0ec33..31da295f7359c25347b96a9d979421bed565671b`
4+
5+
**Verdict:** PASS
6+
7+
`pattern_tool.py review-inputs` resolved `.pattern-review` as the sole rule
8+
source, found no overrides, and triggered no active rules for the nine changed
9+
source, test, and lifecycle files. No finding, waiver, or proposal is required.
10+
11+
The security-sensitive evidence change received direct code and spec review.
12+
Those reviews confirm that an omitted display field is not synthesized and
13+
that all present origin mismatches plus every independent codesign identity
14+
mismatch remain blocking.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Spec Evaluation - PR #79
2+
3+
**Pinned diff:** `de43dae24f2629748b1c1a3376c478e183e0ec33..31da295f7359c25347b96a9d979421bed565671b`
4+
5+
**Scope:** Slice 9, plan steps P2, P5, and P8 (`I-11`)
6+
7+
**Verdict:** PASS for this correction slice
8+
9+
## Rubric Evaluation
10+
11+
| Criterion | Status | Evidence |
12+
|---|---|---|
13+
| R4 - Protected production and credential custody | PASS FOR SLICE; OVERALL NOT YET | The correction changes no credential or publication path. Preview `.7` again proved main-only protected production and sanitized exact-byte recovery. Full producer completion remains required. |
14+
| R5 - Native Apple verification | PASS FOR SLICE; OVERALL NOT YET | Gatekeeper acceptance remains parsed from exit status, complete accepted line, and exact notarized source. Optional origin display metadata is not invented; exact identity and Team ID remain separately mandatory through codesign. Current ARM64 and Intel evidence documents still require preview `.8`. |
15+
| R7 - Failure, recovery, and immutability | PASS FOR SLICE; OVERALL NOT YET | `.7` preserved its accepted Apple request and exact candidate, remained unpublished, and will not be reused. Wrong present origin and wrong codesign identity continue to fail closed. |
16+
| R8 - Protected nonpublishing rehearsal | NOT YET | Run `33272715923` truthfully failed before both native documents; the next protected `publish=false` rehearsal must use reviewed corrected code and preview `.8`. |
17+
18+
The approved design states that Gatekeeper assessments supplement independent
19+
Developer ID and Team ID verification. This slice follows that boundary: it
20+
does not downgrade or remove any exact codesign fact, and it does not treat
21+
notarization alone as Gatekeeper acceptance.
22+
23+
No `development*` branch entered the evaluated ancestry.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Verification - PR #79
2+
3+
**Scope:** `de43dae24f2629748b1c1a3376c478e183e0ec33..31da295f7359c25347b96a9d979421bed565671b`
4+
5+
**Verdict:** PASS
6+
7+
## Verification Matrix
8+
9+
| Category | Command or evidence | Result |
10+
|---|---|---|
11+
| Toolchain | pinned Bun `1.3.14` repository check | PASS; required Bun and platform accepted |
12+
| Documentation | `bun run docs:check` within `bun run check` | PASS |
13+
| Build/typecheck | `bun run typecheck` within `bun run check` | PASS |
14+
| Lint | `bun run lint` within `bun run check` | PASS |
15+
| Format | `prettier --check .` within `bun run check` | PASS |
16+
| Focused Apple trust tests | pinned Bun over parser and native evidence suites | PASS; 12 tests, 55 expectations, 0 failures |
17+
| Repository test gate | pinned Bun `run check` | PASS; 577 tests, 3025 assertions, 0 failures |
18+
| Workflow docs | branch-doc, issue, and tracker validators | PASS; decision triage intentionally deferred to boundary gate |
19+
| Live Apple request | preview `.7` recovery JSON | PASS; request `2e9f8382-58d1-4d8e-a2d6-5ad32d6ce4aa` reached `Accepted` for SHA-256 `02e11e0bec065bff8dc9d546cbf44316b29b784dc7793f5d121d5debd6890a3b` |
20+
| Exact Gatekeeper command | producer arguments replayed against preserved DMG | PASS; exit 0, path-prefixed `accepted`, exact `Notarized Developer ID` source, and no `origin=` line |
21+
| Negative identity checks | present wrong Gatekeeper origin and wrong codesign identity | PASS; both rejected by deterministic tests |
22+
| Browser/E2E/API | user-facing/network runtime | N/A; release evidence parsing only |
23+
| Hosted corrected run | complete two-architecture trust matrix | NOT YET; requires this correction on reviewed `main` and unused preview `.8` |
24+
25+
The correction does not synthesize an absent origin. Exact Developer ID
26+
identity, Team ID, hardened runtime, and secure timestamp remain mandatory in
27+
the independent `codesign` evidence for each CLI, application, and DMG.
28+
29+
No release, tag, Homebrew change, Desktop update, or publication mutation
30+
occurred. The post-run authorities exactly matched the preflight baseline.

0 commit comments

Comments
 (0)