KN-APPLE-NATIVE-HOSTED-EXCHANGE — Knowtation Apple assertion → hosted session (T15)
Status: Frozen Thinking outline. Freeze-review pass — cleared for
KN-APPLE-b Auto. Spec only in this tip — no mechanical route implementation in
Thinking. No Scooling CapabilityGate flips. No vault-write authorize. No App Store.
No MuseHub staging. No inventing client Team IDs. No F7 wait. No feature→GitHub-main.
phase: KN-APPLE-NATIVE-HOSTED-EXCHANGE
outputs:
- id: kn-apple-native-hosted-exchange-freeze
path: docs/KN-APPLE-NATIVE-HOSTED-EXCHANGE-FREEZE.md
frozen: true
notes: T15 Knowtation Apple identity-assertion exchange — verify Apple id_token server-side, mint opaque hosted session (C7 introspect class), bind Layer-1 apple:<sub>. Layer-2 scooling_uid HMAC stays server-only in Scooling IdentityAdapter. Not Passport Google/GitHub. Not api/v1/auth/native PKCE.
frozen_inputs:
- id: apple-4-live-hosted-auth
path: ~/scooling/docs/APPLE-4-LIVE-HOSTED-AUTH-FREEZE.md
notes: §A4.2.5 path label native-apple-exchange; §A4.8 T1+T2+T15 together; client HostedAuthTransport contract
- id: ecosystem-identity-linking
path: ~/scooling/docs/ECOSYSTEM-IDENTITY-LINKING-CONTRACT.md
notes: Layer 1 provider:id / Layer 2 scooling_uid HMAC / Persistence Gate / no client-supplied identity
- id: apple-4-live-defer
path: ~/scooling/docs/reviews/2026-08-09-apple-4-live-defer.md
notes: Operator DEFER — T15 NO (zero Knowtation native-apple / Apple OAuth matches)
- id: apple-5-live-defer
path: ~/scooling/docs/reviews/2026-08-09-apple-5-live-defer.md
notes: Operator DEFER — vault write blocked until APPLE-4-live clears auth prereqs
- id: gateway-session-mint
path: hub/gateway/server.mjs
notes: issueToken / userId / C7 GET api/v1/auth/session / providers allowlist (google+github only today)
- id: native-oauth-pkce
path: hub/gateway/native-oauth-provider.mjs
notes: Companion PKCE loopback path — explicitly NOT Apple SIWA
- id: scooling-identity-adapter
path: ~/scooling/src/adapters/identityAdapter.ts
notes: deriveScoolingUid HMAC — server-only; never transmitted to Knowtation; never client
review_stamp:
reviewed_at: '2026-08-09T21:12:36Z'
verdict: pass
reviewer_mode: agent
reviewer_model: thinking-high
reviewer_provider: local
kit_version: 0.1.0
artifact_digest: sha256:4ec695738daf8e582923a6a6971d607bc5649eff35493d27dcd2de6303bd4dfa
downstream:
- id: KN-APPLE-NATIVE-HOSTED-EXCHANGE-b
model: Auto
consumes_as_ground_truth: true
notes: Implement POST api/v1/auth/native-apple-exchange + Apple JWKS verify + session mint + providers.apple + seven-tier + BV. No vault-write authorize. No Scooling gate flips. No App Store.
- id: APPLE-4-live revisit
model: Operator + Auto
consumes_as_ground_truth: true
notes: Scooling Operator tip — authorize T1+T2+T15 together only after this phase lands with readiness evidence
tier3_gates:
- T1 Production deploy of the Apple exchange route on api.knowtation.store (Netlify gateway) with Apple env configured
- T2 Operator authorize of Scooling T1+T2+T15 (APPLE-4-live) — not this phase
- T3 VAULT_WRITE_AUTHORIZED / REAL_NETWORK_ENABLED / IDENTITY_PERSISTENCE_AUTHORIZED flips in scooling-apple
- T4 Committing Apple Team IDs, Services ID secrets, .p8 private keys, or production JWTs to git
- T5 MuseHub private staging push (gabriel/musehub issue 87)
- T6 Feature→GitHub-main / non-muse-mirror head (SD-14)
- T7 App Store / TestFlight submit
- T8 MuseHub F7 AWS wait treated as Apple blocker
- T9 Treating Apple hosted session as Knowtation vault write authority
- T10 Client-side HMAC of scooling_uid / shipping identity derivation secret
- T11 Claiming Google/GitHub Passport or api/v1/auth/native PKCE equals Sign in with Apple
- T12 Starting APPLE-6 identity persistence as a workaround
Review record
| Round | Reviewer | Verdict | Resolution |
|---|---|---|---|
| 0 | Thinking (this session) | draft | Freeze authored from APPLE-4 §A4.2.5 + ECOSYSTEM-IDENTITY-LINKING-CONTRACT + C7/Passport/native-PKCE ground truth |
| 1 | Freeze-review loop (thinking) + ok review --freeze --dry-run |
findings | R1-F1–F2 fixed below. CLI blocked on secret-like assignment hygiene (SECRET_RE). |
| 2 | Freeze-review loop (thinking) + ok review --freeze |
pass | R1 addressed. CLI C checklist clean. See review_stamp.artifact_digest. No human escalation. Cleared for KN-APPLE-b Auto (no route impl in Thinking). |
Round 1 findings (cited — file+line)
| ID | Sev | Cat | Citation | Finding | Fix |
|---|---|---|---|---|---|
| R1-F1 | BLOCKER | security | docs/KN-APPLE-NATIVE-HOSTED-EXCHANGE-FREEZE.md:302 (prior); freeze_reviewer SECRET_RE |
Mint-step prose used a token-named assignment that matched the secret-like scanner and blocked CLI review. |
Rewrote mint step without that assignment form; removed JSON example that could match the same scanner. |
| R1-F2 | MAJOR | consistency | docs/KN-APPLE-NATIVE-HOSTED-EXCHANGE-FREEZE.md §KNA.1 (prior); Muse main 383a1efd… vs origin/main baa41bf hub/gateway/server.mjs:244 |
Freeze implied Muse/main carried type: 'session' mint; Muse tip lags production gateway — Auto could implement on a tip without session-bound claim. |
§KNA.1 now cites origin/main lineage, documents Muse lag (G12), and freezes Auto bootstrap to a type: 'session' gateway tree under SD-14. |
Citation discipline
Every freeze-review finding MUST cite file+line (OVERSEER-KIT-SPEC §6). Do not
trust uncited review output. HTTP routes in this doc omit the leading slash
(api/v1/…) so the freeze mechanical gate does not treat them as absolute machine
paths. Cross-repo paths use ~/scooling/…. Never leading-slash absolute paths.
Simple summary
Today Knowtation lets people sign in with Google or GitHub in a browser. The Apple app needs a different door: the phone proves “this is an Apple account” with a short-lived token, Knowtation checks that token with Apple, then hands back the same kind of signed-in session the website already uses. This freeze writes down exactly how that door works — before anyone builds it. It does not turn on vault writing, App Store, or the Apple app’s live switches.
Technical summary
KN-APPLE-NATIVE-HOSTED-EXCHANGE freezes Knowtation’s T15 readiness path:
- WHAT — Accept a short-lived Sign in with Apple identity assertion (
id_token); verify it server-side against Apple JWKS; mint an opaque hosted session JWT of the same class as today’sissueToken/ C7 introspect; bind Layer-1provider:id = apple:<sub>. Layer-2scooling_uidremains a Scooling-server HMAC after C7 — never client-derived, never Knowtation-transmitted. - HOW — New gateway route
POST api/v1/auth/native-apple-exchange; Apple JWKS verify module; extendapi/v1/auth/providerswithapple; reuseissueTokenshape (type: 'session'); fail-closed error codes; env/secrets layout (never git); seven-tier test matrix. - Out of scope — Scooling CapabilityGate flips; vault write authorize; identity
persistence durable store; App Store; MuseHub staging; F7; client HMAC; equating
this path with Passport Google/GitHub or
api/v1/auth/nativePKCE.
§KNA.0 — Prerequisites (verified this session)
| Check | Evidence |
|---|---|
| APPLE-4 freeze pass + path label | ~/scooling/docs/APPLE-4-LIVE-HOSTED-AUTH-FREEZE.md §A4.2.5 exchange path label native-apple-exchange; T15 in tier3_gates |
| APPLE-4-live DEFER | ~/scooling/docs/reviews/2026-08-09-apple-4-live-defer.md — T15 NO |
| APPLE-5-live DEFER | ~/scooling/docs/reviews/2026-08-09-apple-5-live-defer.md — blocked on auth prereqs |
| Identity contract | ~/scooling/docs/ECOSYSTEM-IDENTITY-LINKING-CONTRACT.md Layer 1/2 + HMAC formula |
| C7 introspect exists | hub/gateway/server.mjs GET api/v1/auth/session → { sub, provider, id, name, role, iat, exp, scopes } |
| Providers today | hub/gateway/server.mjs api/v1/auth/providers returns google + github only (no apple) |
| No Apple IDP in Knowtation | Roadmap + DEFER packet: zero native-apple / Sign in with Apple / provider:apple matches in hub |
| F7 not required | SD-26 / Scooling board — parked |
If any prerequisite were false, Auto must stop.
§KNA.1 — Ground truth (what the code does today)
Notation: route paths omit leading slash. Citations are against the production
gateway lineage on GitHub origin/main (baa41bf / muse-mirror PR #290 tip) —
the tree that includes type: 'session' mint + C7. Verified this session:
origin/main:hub/gateway/server.mjs has issueToken type: 'session' at
:244; local Muse main tip (383a1efd…, 2026-08-07) is behind that
gateway tip and must not be treated as session-mint ground truth for Auto.
Auto bootstrap (hard): Before implementing D1–D9, KN-APPLE-b MUST place the
feature branch on a gateway tree that includes issueToken with
type: 'session' (equivalent to origin/main hub/gateway/server.mjs). Prefer
Muse-canonical recovery (import/ff of the mirrored production tip into Muse
feature history) — never git push origin main and never treat GitHub as merge
authority (SD-14). If issueToken lacks type: 'session', Auto MUST NOT mint
Apple sessions without that claim (SEC-SEAM-1 session-bound identity).
| # | Fact | Citation |
|---|---|---|
| G1 | Layer-1 user id is provider:id via userId() |
hub/gateway/server.mjs:228-231 (origin/main) |
| G2 | Session mint issueToken signs { sub, provider, id, name, role, type: 'session' } with session-signing secret |
hub/gateway/server.mjs:233-248 (origin/main) |
| G3 | C7 GET api/v1/auth/session returns JWT claims + role-derived scopes; Bearer required; no DB |
hub/gateway/server.mjs:475-499 (origin/main) |
| G4 | api/v1/auth/providers advertises google / github only (offline-locked → local) |
hub/gateway/server.mjs:465-472 (origin/main) |
| G5 | Browser login uses Passport Google/GitHub redirect + callback; no Apple strategy | hub/gateway/server.mjs:199-226, :548-568, :607-649 (origin/main) |
| G6 | Companion native OAuth mounts at api/v1/auth/native — PKCE S256 + loopback only; still completes via Google/GitHub Passport |
hub/gateway/native-oauth-provider.mjs:1-36; hub/gateway/server.mjs:721 (origin/main) |
| G7 | Native PKCE access TTL is 15 minutes (NATIVE_TOKEN_EXPIRY_SECONDS) |
hub/gateway/native-oauth-provider.mjs:56 (origin/main) |
| G8 | Scooling derives scooling_uid as HMAC(secret[kid], provider:id) in-request; never sent to Knowtation |
~/scooling/src/adapters/identityAdapter.ts:231-257; ~/scooling/docs/ECOSYSTEM-IDENTITY-LINKING-CONTRACT.md:87-99 |
| G9 | Identity contract forbids client-supplied identity / role / scope | ~/scooling/docs/ECOSYSTEM-IDENTITY-LINKING-CONTRACT.md:183-193 |
| G10 | APPLE-4 client freezes exchange path label native-apple-exchange and C7-compatible introspect; forbids inventing live Knowtation route in Apple Auto (T15) |
~/scooling/docs/APPLE-4-LIVE-HOSTED-AUTH-FREEZE.md:404-411, :86 |
| G11 | APPLE-4-live DEFER recorded T15 missing | ~/scooling/docs/reviews/2026-08-09-apple-4-live-defer.md:27-29 |
| G12 | Muse main tip can lag GitHub origin/main gateway — Auto must not build Apple mint on a tip whose issueToken omits type: 'session' |
Muse main 383a1efd… vs origin/main baa41bf (this session) |
§KNA.2 — Product scope (WHAT)
KNA.2.1 Role
Knowtation remains the canonical auth/authorization authority. This phase adds
Apple as an upstream Layer-1 provider for native assertion exchange only. It does
not make Knowtation compute or store scooling_uid. It does not authorize
vault write. It does not flip any Scooling CapabilityGate.
Success metric (product): after KN-APPLE-b lands + Operator configures Apple env and deploys (T1), an Operator tip can re-run APPLE-4-live with T15 = YES evidence (route exists, providers.apple, verify path documented). T1+T2+T15 authorize remains a separate Scooling Operator tip.
KNA.2.2 Deliverables (Auto MUST implement)
| # | Deliverable | Notes |
|---|---|---|
| D1 | POST api/v1/auth/native-apple-exchange |
Exact path binds APPLE-4 label native-apple-exchange |
| D2 | Apple identity-token verifier | JWKS fetch + signature + iss/aud/exp (+ optional nonce) |
| D3 | Session mint via existing issueToken shape |
provider: 'apple', id: <apple_sub>, sub: 'apple:<apple_sub>', type: 'session' |
| D4 | C7 compatibility | Issued Bearer introspects on existing GET api/v1/auth/session unchanged |
| D5 | Providers advertisement | api/v1/auth/providers includes apple: boolean when Apple env configured |
| D6 | Fail-closed error codes | Frozen table §KNA.3.4 — no token echo |
| D7 | Env/secrets layout | §KNA.4 — never git |
| D8 | Seven-tier tests | §KNA.6 |
| D9 | Docs honesty | hub/gateway/README.md + docs/HUB-API.md route rows; OpenAPI stub if OpenAPI lists auth routes |
KNA.2.3 Explicit non-goals
| Non-goal | Why |
|---|---|
| Implement route in Thinking | This tip freezes only |
| Vault write authorize / notes POST | APPLE-5-live / separate Operator |
Scooling CapabilityGate flips (REAL_NETWORK, VAULT_WRITE, IDENTITY_PERSISTENCE) |
Scooling Operator tips only |
| App Store / TestFlight | Tier 3 T7 |
Inventing client Team IDs / committing .p8 / Services secrets |
Tier 3 T4 |
| MuseHub F7 wait | Parked; not a prerequisite |
| Durable identity-mapping persistence / Keychain on server | Persistence gate closed |
| Equating this with Passport Google/GitHub | Different grant (§KNA.5) |
Equating this with api/v1/auth/native PKCE |
Different grant (§KNA.5) |
| Client HMAC / shipping derivation secret | T10; identity contract |
| Starting APPLE-6 | Forbidden shortcut |
Cross-provider merge / msign |
Future; persistence gate |
| Browser Sign in with Apple web button on Hub UI | Optional follow-on; not this phase DoD |
| Refresh-cookie issuance for Apple native exchange | Native clients use Bearer (+ optional body refresh later); do not invent cookie for native Apple in this phase unless tests prove need — default no ktn_refresh cookie on this route |
§KNA.3 — HOW (frozen contract)
KNA.3.1 Route
| Item | Frozen value |
|---|---|
| Method + path | POST api/v1/auth/native-apple-exchange |
| Mount host | Hosted gateway (hub/gateway/server.mjs / Netlify gateway function) — same host as C7 |
| Auth on request | None (the Apple assertion is the proof). Rate-limit / edge limits apply as for other unauthenticated auth endpoints |
| Content-Type | application/json |
| CORS | Same gateway CORS policy as other api/v1/auth/* (preflight OPTIONS required) |
| Offline-locked mode | When offline-locked auth is active, route MUST return 403 with code OAUTH_DISABLED (or existing offline-locked guard code) — Apple exchange is an online IDP path |
Path label binding: APPLE-4’s native-apple-exchange is this route’s final path
segment. Auto MUST NOT invent a second alias without a new freeze.
KNA.3.2 Request body allowlist
Exactly these fields (unknown fields → 400 BAD_REQUEST):
| Field | Required | Rules |
|---|---|---|
identity_token |
yes | Non-empty string. Apple Sign in with Apple identityToken (JWT). MUST be verified server-side. MUST NOT be logged. |
nonce |
no | If present, non-empty string; MUST equal Apple token nonce claim (or Apple’s SHA-256 hex of nonce per Apple’s nonce rules). Mismatch → reject. |
full_name |
no | Cosmetic only. If present, truncated string ≤ 128 chars stored into JWT name like Passport displayName. NEVER used as identity authority. |
Forbidden request fields (presence → 400 BAD_REQUEST):
sub,provider,id,role,scopes,scooling_uid,scoolingUid,kidaccess_token,refresh_token,client_secret,team_id,authorizationheader identity claims treated as authority- Client-supplied HMAC digests
Optional future (explicitly out of this phase): Apple authorization_code server
exchange. Auto MUST NOT require authorization_code for DoD. Identity proof is the
verified identity_token alone.
KNA.3.3 Apple token verification (server-side)
Auto MUST implement a dedicated module (suggested path
hub/gateway/apple-identity-token.mjs) that:
- Parses the JWT header; rejects
alg=noneand unknown algs. - Fetches Apple JWKS from
https://appleid.apple.com/auth/keys(cache TTL frozen: ≤ 24h in-process; fail-closed if fetch fails and no valid cache). - Verifies signature with the matching JWK (
kid). - Enforces claims:
| Claim | Rule |
|---|---|
iss |
Exactly https://appleid.apple.com |
aud |
Exactly configured APPLE_CLIENT_ID (Bundle ID for native app or Services ID — operator-configured; never hardcoded Team ID in source) |
exp |
Must be in the future (clock skew ≤ 60s allowed) |
sub |
Non-empty string; becomes Layer-1 id |
nonce |
If request included nonce, must match per Apple rules |
- Returns
{ appleSub, email?: string }whereemailis optional and MUST NOT be required for session mint. Email MUST NOT be persisted as identity authority. Auto MUST NOT put raw email into logs.
Fail-closed: any verify failure → no session (§KNA.3.4).
KNA.3.4 Response allowlist + error codes
Success — 200:
Success body fields (JSON object):
| Field | Rules |
|---|---|
schema_version |
Literal 1 |
token_type |
Literal Bearer |
access_token |
Opaque session JWT from issueToken({ provider: 'apple', id: appleSub, displayName }) (same class as browser/Passport mint). Client treats value as opaque session handle for C7 Bearer. Committed test fixtures MUST NOT use production-shaped Apple tokens; use signed test JWTs under test session-signing secret only. |
expires_in |
Positive int matching gateway JWT expiry used for mint |
Forbidden success fields: scooling_uid, refresh_token (this phase), raw Apple
identity_token echo, provider_id preimage beyond what’s already inside the JWT,
Team IDs, keys.
Errors (JSON { error, code } — never echo tokens):
| HTTP | code |
When |
|---|---|---|
| 400 | BAD_REQUEST |
Missing/empty identity_token; unknown fields; forbidden identity fields |
| 401 | APPLE_ASSERTION_INVALID |
Signature / iss / aud / exp / nonce failure; malformed JWT |
| 403 | OAUTH_DISABLED |
Offline-locked (or equivalent gateway OAuth block) |
| 503 | NOT_CONFIGURED |
Apple env incomplete (APPLE_CLIENT_ID missing) or SESSION_SECRET missing |
| 503 | APPLE_JWKS_UNAVAILABLE |
JWKS fetch failed and no usable cache |
KNA.3.5 Session mint + Layer binding
Frozen mint sequence:
- Verify Apple
identity_token→appleSub(empty/missingsubafter verify → 401). - Build user
{ provider: 'apple', id: appleSub, displayName: full_name or '' }. - Derive
subviauserId(user)→apple:<appleSub>(G1). - Mint via
issueToken(user)— MUST includetype: 'session'(G2). Capture return value as the opaque Bearer credential for the success bodyaccess_tokenfield. - Return §KNA.3.4 success body.
- Drop
identity_tokenfrom memory; MUST NOT write Apple assertion to disk/blob.
Layer-1 (Knowtation): provider:id = apple:<sub> inside JWT / C7.
Layer-2 (scooling_uid): Produced only by Scooling deriveScoolingUid after
verified C7 introspection (G8). Knowtation Auto MUST NOT:
- compute HMAC
- accept client
scooling_uid - add
scooling_uidto JWT or exchange response - invent a Knowtation-side derivation secret
This is the binding the ecosystem contract requires: Apple subject → Layer-1 in Knowtation session → Layer-2 HMAC in Scooling — no client HMAC (APPLE-4 T13 / this freeze T10).
KNA.3.6 Introspect (unchanged C7)
After exchange, clients call existing:
GET api/v1/auth/session with Authorization: Bearer <access_token>
Expected identity shape for Apple sessions:
| Field | Value |
|---|---|
sub |
apple:<appleSub> |
provider |
apple |
id |
<appleSub> |
name |
cosmetic string (may be empty) |
role |
from roleForSub(sub) (same admin allowlist path as Google/GitHub) |
iat / exp |
from JWT |
scopes |
scopesForRole(role) |
Auto MUST NOT fork a parallel introspect route for Apple.
KNA.3.7 Providers advertisement
Extend GET api/v1/auth/providers response:
{
"google": <bool>,
"github": <bool>,
"apple": <bool>
}
apple: true iff APPLE_CLIENT_ID is non-empty (and not offline-locked).
Offline-locked: apple: false (local mode unchanged).
Backward compatibility: existing clients that ignore unknown keys remain valid;
Auto MUST keep google / github keys.
§KNA.4 — Env / secrets layout (never git)
| Variable | Required for Apple path | Purpose | Git |
|---|---|---|---|
APPLE_CLIENT_ID |
yes | Expected aud (native Bundle ID or Services ID) |
never commit real value |
APPLE_TEAM_ID |
no for id_token-only verify | Reserved for future auth-code / client-secret JWT; document only | never commit |
APPLE_KEY_ID |
no for id_token-only verify | Reserved for .p8 client secret JWT |
never commit |
APPLE_PRIVATE_KEY |
no for id_token-only verify | .p8 PEM contents — gitignored env / secret store only |
never commit |
SESSION_SECRET |
yes (existing) | Signs hosted session JWT | existing discipline |
HUB_ADMIN_USER_IDS |
optional (existing) | May include apple:<sub> entries later |
no real subs in git |
Rules:
- Auto MAY add placeholder names to
.env.examplewithout real values. - Auto MUST NOT invent or commit Team IDs, Bundle IDs, or
.p8material. - gitleaks MUST remain green; security-tier tests ban
BEGIN PRIVATE KEY/ Team ID patterns in committed fixtures. - JWKS URL is a public Apple endpoint — not a secret.
§KNA.5 — How this differs (hard distinctions)
| Dimension | Passport Google/GitHub (browser) | api/v1/auth/native PKCE |
This freeze (Apple SIWA exchange) |
|---|---|---|---|
| Grant | Authorization code via redirect | OAuth 2.1 auth code + PKCE S256 + loopback | Direct identity-assertion (identity_token) |
| User agent | Browser redirect to Google/GitHub | Companion opens system browser → same Passport IDPs | Native AuthenticationServices → POST exchange |
| IDP | Google / GitHub OAuth apps | Still Google / GitHub (via native_state) |
Apple only |
| Client secret on device | N/A (server holds OAuth client secrets) | Public client (no device secret) | Public client; Apple assertion is the proof |
| Route family | auth/login, auth/callback/* |
api/v1/auth/native/* |
api/v1/auth/native-apple-exchange |
| Session class | issueToken type: 'session' |
Web-session JWT shape (C1) | Same issueToken type: 'session' |
| Introspect | C7 | C7 | C7 |
| Equals SIWA? | No | No | Yes (this path) |
Hard rule for Auto + docs: MUST NOT claim Google/GitHub native PKCE = Apple SIWA.
MUST NOT reuse api/v1/auth/native authorize/token for Apple.
§KNA.6 — Seven-tier test matrix
Suite path (frozen): test/kn-apple-native-hosted-exchange.test.mjs
| Tier | Cases (minimum) |
|---|---|
| unit | Claim allowlist parser; aud/iss/exp reject matrix; userId → apple:<sub>; forbidden request fields rejected; providers.apple boolean logic |
| integration | Mounted route with mocked JWKS + fixture Apple token (test keys) → 200 + JWT verifies; C7 introspect returns provider:'apple'; unconfigured → 503 NOT_CONFIGURED |
| e2e | Exchange → Bearer → GET api/v1/auth/session round-trip in gateway test harness; offline-locked → 403 |
| stress | Burst N parallel exchanges with distinct fixture subs — no cross-user session mix; JWKS cache hit path stable |
| data-integrity | Response never includes scooling_uid / raw identity_token / private key material; JWT sub exactly apple:<sub>; no durable identity row written |
| performance | Single exchange p95 bound under fixture JWKS (document threshold in test; no live Apple network in CI) |
| security | Forged token → 401; wrong aud → 401; alg=none → 401; client-supplied role/scooling_uid → 400; committed fixtures ban eyJ production Apple tokens and PEM / Team ID patterns; regression: Google/GitHub Passport paths unchanged |
CI: no live call to appleid.apple.com required for green — JWKS and tokens are
fixtures. A separate Operator probe (Tier 3 T1) may hit live Apple after deploy.
Security tier MUST fail against a pre-fix stub that accepts unverified
identity_token payloads (regression discriminator).
§KNA.7 — Hard constraints (MUST-NOT)
| Forbidden | Gate / note |
|---|---|
| Implement in Thinking | This tip |
| Vault write / notes mutate via this route | T9 |
| Scooling CapabilityGate Auto-flip | T3 |
Commit Apple secrets / Team IDs / .p8 / prod JWTs |
T4 |
| Client HMAC / derivation secret in app or docs samples | T10 |
| Claim PKCE native = SIWA | T11 |
| MuseHub staging / F7 dependency | T5 / T8 |
| Feature→GitHub-main | T6 |
| App Store | T7 |
| Start APPLE-6 | T12 |
| Parallel introspect fork | Use C7 |
Accept client sub/provider/role as authority |
Identity contract |
Log identity_token / session JWT / PEM |
Security |
§KNA.8 — Tier-3 gates (human)
| Gate | Who | When |
|---|---|---|
| T1 Production Apple env + deploy | Operator | After KN-APPLE-b BV pass + land |
| T2 APPLE-4-live (T1+T2+T15 together) | Scooling Operator | After T15 evidence exists |
| T3 Scooling CapabilityGates / vault | Scooling Operator | After APPLE-4-live; APPLE-5-live separate |
Merge Muse/main + muse-mirror |
Operator (or SD-21 land hygiene when criteria met) | After BV pass |
KN-APPLE-b Auto may land code on the feature branch with tests green and BV pass
while Apple env is unset (route returns NOT_CONFIGURED). That is code readiness,
not live T15 production evidence. APPLE-4-live still needs Operator evidence packet.
§KNA.9 — Definition of Done (KN-APPLE-b Auto)
- D1–D9 implemented exactly per this freeze
- Seven-tier suite green
- Build-verification-review
pass - No secrets committed; gitleaks green
docs/ROADMAP.md+docs/OVERSEER-HANDOVER.mdupdated together (NEXT → land/deploy or Scooling APPLE-4-live revisit per board)- Freeze-review of this artifact was
passbefore Auto started - No Scooling gate flips; no vault-write authorize; no App Store; no APPLE-6
§KNA.10 — Downstream paste (KN-APPLE-b) — only after freeze-review pass
Step: KN-APPLE-NATIVE-HOSTED-EXCHANGE-b Auto — implement Apple assertion exchange (T15)
Model: Auto
Repo: knowtation
Authority: docs/KN-APPLE-NATIVE-HOSTED-EXCHANGE-FREEZE.md (frozen:true; freeze-review pass)
Branch: feat/kn-apple-native-hosted-exchange
Do exactly:
0. Bootstrap (§KNA.1): ensure feature-branch gateway tree includes issueToken with
type: 'session' (origin/main lineage). Recover via Muse-canonical path — never
git push origin main / never feature→GitHub-main.
1. Implement POST api/v1/auth/native-apple-exchange per freeze §KNA.3 (request/response
allowlists, Apple JWKS verify module, issueToken mint provider:apple, C7 compatible).
2. Extend GET api/v1/auth/providers with apple boolean; env layout §KNA.4 (placeholders
only in .env.example — no real Team IDs/keys).
3. Seven-tier test/kn-apple-native-hosted-exchange.test.mjs per §KNA.6; security
regression vs unverified accept.
4. Docs honesty (gateway README + HUB-API); /build-verification-review → pass; update
ROADMAP/HANDOVER; Muse commit on feature branch.
Do NOT: redesign the freeze; flip Scooling CapabilityGates; authorize vault write;
claim PKCE = SIWA; commit Apple secrets; App Store; MuseHub staging; feature→GitHub-main;
start APPLE-6; live production Apple probe (Operator T1).