Skip to content

feat(trust): adopt canonical primitives across 5 trust surfaces (Wave 2)#342

Open
ctol3r wants to merge 1 commit into
wave/trust-primitives-lane-bfrom
wave/trust-primitives-adoption
Open

feat(trust): adopt canonical primitives across 5 trust surfaces (Wave 2)#342
ctol3r wants to merge 1 commit into
wave/trust-primitives-lane-bfrom
wave/trust-primitives-adoption

Conversation

@ctol3r
Copy link
Copy Markdown
Owner

@ctol3r ctol3r commented May 12, 2026

Summary

Wave 2 of the trust-convergence migration. Stacked on #341 (Lane B primitives). Adopts the canonical primitives in 5 of the 6 brief-listed surfaces, replacing fragmented inline trust rendering. No layout redesign, no new data fetching, no auth/backend changes.

Surfaces adopted

Surface File Adoption
/passport/[id] app/passport/[id]/PassportEntityClient.tsx <TrustHeader variant="SNAPSHOT"> at the top of the surface
PassportWallet (identity row) components/passport/PassportWallet.tsx <CheckedAtStamp> replaces inline toLocaleDateString
LanePanel (proof) components/proof/LanePanel.tsx <CheckedAtStamp> for the Details + Receipt blocks; <RunIdentity> for the visible receipt id chip
ReadinessSurface app/holder/readiness/ReadinessSurface.tsx <TrustHeader variant="PREVIEW"> because this is the anonymous/pre-claim exploration view
ConsoleWrapper (employer review) app/review/[entityId]/ConsoleWrapper.tsx <TrustHeader variant="SNAPSHOT"> above EmployerDecisionConsole

Deferred (explicit follow-up)

Surface File Reason
ReviewClient components/review/ReviewClient.tsx 2093-line surface; multiple inline timestamp sites with complex layout context. Needs a focused follow-up PR.

Doctrine notes

  • Mandatory render order OBJECT → OWNERSHIP → CHECKED_AT → CHANNEL → REPLAY → RUN_ID is enforced by <TrustHeader> (the primitive itself, validated by tests in feat(trust): canonical institutional trust primitives (Lane B) #341).
  • OwnershipState defaults to UNCLAIMED because none of the 5 surfaces currently thread per-user ownership data — that plumbing is Lane D scope, not Wave 2.
  • runId falls back to entityId / loopId where no real run identifier is yet threaded. Surfaces gain a visible identifier chip today; tightening to actual run ids is Wave 10 (replay continuity & survivability).

Truth rules

  • Banned-strings scan: CLEAN
  • No new copy was authored — primitives carry the canonical labels

Validation

  • pnpm turbo run build --filter @vitalcv/web13/13 tasks, green
  • 5 files modified; 0 API contract changes; 0 schema changes

Stacked on wave/trust-primitives-lane-b (PR #341). Surgical adoption
only — replaces fragmented inline trust rendering with the canonical
primitives from apps/web/components/trust/. No layout redesign, no new
data fetching, no auth or backend changes.

Surfaces adopted (5/6 of the brief):
  /passport/[id]            PassportEntityClient.tsx
    + TrustHeader at top of the surface; institutional reading order
      (OBJECT → OWNERSHIP → CHECKED_AT → CHANNEL → REPLAY → RUN_ID)
      now precedes the existing PassportWallet body.
  PassportWallet            (identity row)
    + CheckedAtStamp replaces inline `new Date(...).toLocaleDateString()`
      so the timestamp is rendered consistently across surfaces.
  LanePanel                 (proof inspection pane)
    + CheckedAtStamp on the "Checked at" detail row + the receipt block
    + RunIdentity for the visible receipt id chip
  ReadinessSurface          (holder readiness)
    + TrustHeader variant=PREVIEW; matches the audit verdict that this
      surface is an anonymous/pre-claim exploration view.
  ConsoleWrapper            (employer review)
    + TrustHeader variant=SNAPSHOT above the EmployerDecisionConsole.

Deferred (not in this PR):
  ReviewClient.tsx — 2093-line surface with multiple inline timestamp
  sites; surgical adoption requires layout context this PR does not
  carry. Tracked as follow-up.

Validation:
  pnpm turbo run build --filter @vitalcv/web → 13/13 tasks green
  No banned strings introduced; no API contract changes.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vcv-web Ready Ready Preview, Comment May 12, 2026 0:57am
vitalcv Ready Ready Preview, Comment May 12, 2026 0:57am

ctol3r pushed a commit that referenced this pull request May 12, 2026
Adds the cryptographically-signed receipt surface that an external
verifier needs to validate a clinician passport snapshot end-to-end:

  1. fetch the receipt at /api/receipt/<npi>
  2. fetch the JWKS at /.well-known/jwks.json (from Wave 9)
  3. validate the JWT signature against the JWKS

The new route signs an ES256 JWT using the same getOrInitKeypair()
that produces the public key published at /.well-known/jwks.json
(Wave 9), so kid + key never diverge between issuance and
discoverability.

Receipt shape (ES256 JWT):
  header   { alg: 'ES256', kid: <active-kid>, typ: 'vc+jwt' }
  payload  { iss: <did:web issuer>, sub: 'npi:<npi>',
             jti: 'receipt:<runId>' (deterministic per snapshot),
             iat: <lastCheckedAt seconds>, nbf, exp = iat + 90d,
             vc: W3C VC 2.0 credential block (VitalCVTrustReceipt),
             vcv: VitalCV claim block incl. replay identity }

Determinism contract (test-pinned):
  - jti = 'receipt:<replay.runId>' so the same evidence snapshot
    yields the same receipt jti; the JWT signature is non-
    deterministic per ES256 spec but the payload is byte-identical.
  - iat is pinned to lastCheckedAt seconds so two receipts for the
    same snapshot share iat. A verifier joining receipts by
    (lineageKey, runId) gets unambiguous snapshot identity.

Verifier readability:
  - vcv.jwksUri and vcv.didDocumentUri point at the well-known
    surfaces #349 published, so a receipt is self-describing.
  - X-Receipt-Kid + X-Receipt-Issuer response headers let a verifier
    short-circuit JWKS resolution if it already has the key cached.
  - ?format=download adds Content-Disposition: attachment so a
    verifier engineer can save the JWT for offline validation.

Tests (10 vitest cases, all passing):
  - 400 on non-10-digit NPI, 502 on malformed upstream, propagated
    backend status on upstream non-ok
  - 200 + application/jwt + ES256 + kid header + 'vc+jwt' typ
  - W3C VC 2.0 + VitalCV claim block shape (credentialSubject,
    replay.{lineageKey,runId,schemeVersion}, jwksUri, didDocumentUri)
  - Determinism: same fetch → same jti, same iat
  - End-to-end verifier flow: receipt JWT validates against the
    JWKS the well-known route exposes (createLocalJWKSet + jwtVerify)
  - ?format=download adds Content-Disposition: attachment
  - Graceful no-replay fallback (jti from npi when replay absent)

Validation: 10/10 vitest passing; pnpm turbo run build --filter
@vitalcv/web → 13/13 tasks; truth-strings scan CLEAN.

Stacked on the rest of this branch (#349's well-known surfaces +
wellKnownIdentity helper). Brief 1 of this turn (replay-lineage
rendering) is structurally already shipped across #341 + #342 +
#343 + #345 — no rendering changes needed here.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants