@certrev/cert-block 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +45 -8
- package/dist/builder/card-beacon.d.ts +73 -0
- package/dist/builder/card-beacon.d.ts.map +1 -0
- package/dist/builder/card-beacon.js +178 -0
- package/dist/builder/card-beacon.js.map +1 -0
- package/dist/builder/card-contract.d.ts +10 -2
- package/dist/builder/card-contract.d.ts.map +1 -1
- package/dist/builder/card-contract.js +27 -3
- package/dist/builder/card-contract.js.map +1 -1
- package/dist/builder/card-placement.d.ts +45 -0
- package/dist/builder/card-placement.d.ts.map +1 -0
- package/dist/builder/card-placement.js +193 -0
- package/dist/builder/card-placement.js.map +1 -0
- package/dist/builder/card-report.d.ts +26 -14
- package/dist/builder/card-report.d.ts.map +1 -1
- package/dist/builder/card-report.js +68 -14
- package/dist/builder/card-report.js.map +1 -1
- package/dist/builder/cert-review-card.d.ts +4 -4
- package/dist/builder/cert-review-card.d.ts.map +1 -1
- package/dist/builder/cert-review-card.js +77 -189
- package/dist/builder/cert-review-card.js.map +1 -1
- package/dist/builder/contract-card.d.ts +18 -1
- package/dist/builder/contract-card.d.ts.map +1 -1
- package/dist/builder/contract-card.js +60 -10
- package/dist/builder/contract-card.js.map +1 -1
- package/dist/builder/delivery-runtime.d.ts +13 -3
- package/dist/builder/delivery-runtime.d.ts.map +1 -1
- package/dist/builder/delivery-runtime.js +17 -4
- package/dist/builder/delivery-runtime.js.map +1 -1
- package/dist/builder/index.d.ts +4 -4
- package/dist/builder/index.js +4 -4
- package/dist/builder/ssr-seed.d.ts +5 -4
- package/dist/builder/ssr-seed.d.ts.map +1 -1
- package/dist/builder/ssr-seed.js +4 -3
- package/dist/builder/ssr-seed.js.map +1 -1
- package/dist/builder/version.d.ts +3 -0
- package/dist/builder/version.d.ts.map +1 -0
- package/dist/builder/version.js +3 -0
- package/dist/builder/version.js.map +1 -0
- package/dist/verify/get-verified-delivery.d.ts +4 -0
- package/dist/verify/get-verified-delivery.d.ts.map +1 -1
- package/dist/verify/get-verified-delivery.js +9 -3
- package/dist/verify/get-verified-delivery.js.map +1 -1
- package/package.json +1 -1
- package/src/builder/card-beacon.ts +201 -0
- package/src/builder/card-contract.ts +25 -3
- package/src/builder/card-placement.ts +208 -0
- package/src/builder/card-report.ts +79 -14
- package/src/builder/cert-review-card.tsx +100 -230
- package/src/builder/contract-card.tsx +65 -8
- package/src/builder/delivery-runtime.ts +17 -4
- package/src/builder/index.tsx +4 -4
- package/src/builder/ssr-seed.ts +5 -4
- package/src/builder/version.ts +2 -0
- package/src/verify/get-verified-delivery.ts +13 -3
|
@@ -24,6 +24,7 @@ import type { ResolvePublicKeyByKid } from '../contract/kernel.js'
|
|
|
24
24
|
import { TtlCache } from '../verify/cache.js'
|
|
25
25
|
import type { CachedDelivery } from '../verify/get-verified-delivery.js'
|
|
26
26
|
import { forgetKeySetsForTests, keySetResolver } from '../verify/key-set.js'
|
|
27
|
+
import { setCardBeaconForTests } from './card-beacon.js'
|
|
27
28
|
import { forgetCardDigestsForTests } from './card-contract.js'
|
|
28
29
|
import { forgetCardReportsForTests } from './card-report.js'
|
|
29
30
|
import { forgetFaceHashesForTests } from './delivered-face.js'
|
|
@@ -32,8 +33,17 @@ import { forgetPageSeedsForTests } from './ssr-seed.js'
|
|
|
32
33
|
|
|
33
34
|
/** The card's server read deadline. A brand's server waits this long once per cold placement. */
|
|
34
35
|
export const CARD_DELIVERY_TIMEOUT_MS = 8_000
|
|
35
|
-
/**
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* 2.0.1: how long a verified answer is fresh. With the stale window below, a card adds at most one minute to the Delivery
|
|
38
|
+
* API's own cache (`max-age=60, stale-while-revalidate=240`), so a revoked card is gone within ADR 0001 §7's five to
|
|
39
|
+
* six minutes (2.0.0's 60 s + 300 s here stacked to about twelve).
|
|
40
|
+
*/
|
|
41
|
+
export const CARD_DELIVERY_TTL_MS = 30_000
|
|
42
|
+
/**
|
|
43
|
+
* How long past its TTL a certificate may still paint while it is refreshed or CertREV is down (2.0.1: 30 s; 2.0.0:
|
|
44
|
+
* 300 s). Never longer: past it, a read that cannot reach CertREV prints nothing.
|
|
45
|
+
*/
|
|
46
|
+
export const CARD_DELIVERY_STALE_MS = 30_000
|
|
37
47
|
|
|
38
48
|
export interface CardDeliveryRuntime {
|
|
39
49
|
/** The baked kids: the package's own trust root (and the only keys that may sign a published key set). */
|
|
@@ -54,7 +64,7 @@ const bakedResolveKid: ResolvePublicKeyByKid = (kid) =>
|
|
|
54
64
|
|
|
55
65
|
function newCardCache(now?: () => number): TtlCache<CachedDelivery> {
|
|
56
66
|
return new TtlCache<CachedDelivery>({
|
|
57
|
-
ttlMs:
|
|
67
|
+
ttlMs: CARD_DELIVERY_TTL_MS,
|
|
58
68
|
negativeTtlMs: 5_000,
|
|
59
69
|
staleMs: CARD_DELIVERY_STALE_MS,
|
|
60
70
|
// A refresh the host runtime abandoned (workerd cancels a finished request's I/O) never
|
|
@@ -94,7 +104,8 @@ export function cardResolveKidFor(rt: CardDeliveryRuntime, baseUrl: string): Res
|
|
|
94
104
|
/**
|
|
95
105
|
* Test seam: replace parts of the runtime (a fresh cache unless one is given) and forget the page's envelope seeds
|
|
96
106
|
* (a fresh runtime is a fresh page), (1.3.0) every delivered face's memoized hashes and (2.0.0) every card
|
|
97
|
-
* document's digest and every published key set.
|
|
107
|
+
* document's digest and every published key set. 2.0.1: beacons go nowhere (a test that wants them sets its own
|
|
108
|
+
* transport with `setCardBeaconForTests` after this). Returns a restore.
|
|
98
109
|
*/
|
|
99
110
|
export function setCardDeliveryRuntimeForTests(overrides: Partial<CardDeliveryRuntime>): () => void {
|
|
100
111
|
forgetPageSeedsForTests()
|
|
@@ -102,6 +113,7 @@ export function setCardDeliveryRuntimeForTests(overrides: Partial<CardDeliveryRu
|
|
|
102
113
|
forgetCardDigestsForTests()
|
|
103
114
|
forgetKeySetsForTests()
|
|
104
115
|
forgetCardReportsForTests()
|
|
116
|
+
const restoreBeacon = setCardBeaconForTests()
|
|
105
117
|
const previous = runtime
|
|
106
118
|
const now = overrides.now ?? previous.now
|
|
107
119
|
runtime = {
|
|
@@ -111,5 +123,6 @@ export function setCardDeliveryRuntimeForTests(overrides: Partial<CardDeliveryRu
|
|
|
111
123
|
}
|
|
112
124
|
return () => {
|
|
113
125
|
runtime = previous
|
|
126
|
+
restoreBeacon()
|
|
114
127
|
}
|
|
115
128
|
}
|
package/src/builder/index.tsx
CHANGED
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
* (`face: { html, css }`, unsigned) and the verified envelope signs that face's sha256 digests for the
|
|
32
32
|
* def version served (`content.faceDigest`), an unpinned whole-card block paints that face instead;
|
|
33
33
|
* markup that does not hash to the signed digest is never painted. A revoked, expired, unverifiable or unreachable cert, a marker that
|
|
34
|
-
* is not exactly that shape, or a missing entry id renders nothing.
|
|
35
|
-
* (every entry exported before 1.1.0) renders
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
* is not exactly that shape, or a missing entry id renders nothing. 2.0.1: a block WITHOUT the marker
|
|
35
|
+
* (every entry exported before 1.1.0) renders nothing too, and reports `no_marker`: through 2.0.0 it
|
|
36
|
+
* painted its options as 1.0.3 did, unsigned export-time claims an editor could change. So deleting the
|
|
37
|
+
* marker no longer brings any face back.
|
|
38
38
|
*
|
|
39
39
|
* Three things the card DOES enforce, and they are the limit of it: no chrome at all without a
|
|
40
40
|
* `reviewerName` and a `verifyUrl`; a credential renders only welded to its dated verification
|
package/src/builder/ssr-seed.ts
CHANGED
|
@@ -41,9 +41,10 @@
|
|
|
41
41
|
* page carried for another version.
|
|
42
42
|
*
|
|
43
43
|
* WHEN THE SERVER HAD NO ENVELOPE TO PAINT (1.2.0) the same attribute carries a fallback record instead of a seed:
|
|
44
|
-
* `{ v: 1, baseUrl, externalId, design?, face }`, where `face` is `'
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* `{ v: 1, baseUrl, externalId, design?, face, reason? }`, where `face` is `'none'` (the server painted nothing:
|
|
45
|
+
* revoked, expired, unverifiable, for another entry, or, 2.0.1, CertREV not reached, with `reason` naming why) or, on a
|
|
46
|
+
* page a 2.0.0 server rendered, `'options'` (CertREV could not be reached and that server painted the block's options
|
|
47
|
+
* face; 2.0.1 prints nothing for it). A browser that renders the card again (hydration, or React's client re-render after a hydration
|
|
47
48
|
* error elsewhere on the page) paints what the record says, so it neither reads the Delivery API nor shows a card the
|
|
48
49
|
* server withheld. A record holds no payload, so `parseSeed` never takes one for a seed. A `'none'` record sits on a
|
|
49
50
|
* `hidden` element with nothing in it.
|
|
@@ -114,7 +115,7 @@ export interface PageSeed {
|
|
|
114
115
|
readonly raw: string
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
/** What the server painted with no verified envelope:
|
|
118
|
+
/** What the server painted with no verified envelope: nothing (2.0.1 never writes `'options'`; a 2.0.0 page may hold one). */
|
|
118
119
|
export type FallbackFace = 'options' | 'none'
|
|
119
120
|
|
|
120
121
|
/** The record a server writes where it had no envelope to paint (the file header). */
|
|
@@ -106,6 +106,8 @@ export type CachedDelivery =
|
|
|
106
106
|
readonly reason: CertSuppressReason
|
|
107
107
|
/** 1.2.0: true when CertREV was not reached (no answer about the credential); see the module header. */
|
|
108
108
|
readonly unreachable?: true
|
|
109
|
+
/** 2.0.1: the HTTP status, when CertREV answered with a non-OK status other than 404/410. */
|
|
110
|
+
readonly status?: number
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
/** The answer for one render: the verdict AT THIS RENDER'S CLOCK, and the def to paint it with. */
|
|
@@ -117,6 +119,8 @@ export interface VerifiedDelivery {
|
|
|
117
119
|
readonly signature?: CertSignature
|
|
118
120
|
/** 1.2.0: on a suppress verdict, true when CertREV was not reached; else undefined. */
|
|
119
121
|
readonly unreachable?: true
|
|
122
|
+
/** 2.0.1: with `unreachable`, the HTTP status when CertREV answered with one (a 5xx, a 429, ...); else undefined. */
|
|
123
|
+
readonly status?: number
|
|
120
124
|
/**
|
|
121
125
|
* 1.3.0: on a `render` verdict, the server-rendered face served beside the artifact, when the response carried a
|
|
122
126
|
* well-formed one; else undefined (never on a suppress verdict). UNVERIFIED: see `DeliveredFace`.
|
|
@@ -175,8 +179,13 @@ function suppressed(reason: CertSuppressReason): CachedDelivery {
|
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
/** A read that did not reach CertREV: the same least-wrong reason as before 1.2.0, marked `unreachable`. */
|
|
178
|
-
function unreachable(): CachedDelivery {
|
|
179
|
-
return {
|
|
182
|
+
function unreachable(status?: number): CachedDelivery {
|
|
183
|
+
return {
|
|
184
|
+
kind: 'suppressed',
|
|
185
|
+
reason: 'unsupported_contract_version',
|
|
186
|
+
unreachable: true,
|
|
187
|
+
...(status === undefined ? {} : { status }),
|
|
188
|
+
}
|
|
180
189
|
}
|
|
181
190
|
|
|
182
191
|
const isUnreachable = (v: CachedDelivery): boolean => v.kind === 'suppressed' && v.unreachable === true
|
|
@@ -312,7 +321,7 @@ async function loadDelivery(opts: GetVerifiedDeliveryOptions): Promise<CachedDel
|
|
|
312
321
|
// Nothing served for this placement: never certified, or withdrawn. A LIVE revocation is a 200
|
|
313
322
|
// tombstone, so a 4xx here genuinely means "no credential for this subject".
|
|
314
323
|
if (res.status === 404 || res.status === 410) return suppressed('subject_mismatch')
|
|
315
|
-
if (!res.ok) return unreachable()
|
|
324
|
+
if (!res.ok) return unreachable(res.status)
|
|
316
325
|
let body: unknown
|
|
317
326
|
try {
|
|
318
327
|
body = await res.json()
|
|
@@ -356,6 +365,7 @@ export function settleDelivery(cached: CachedDelivery, context: RenderContext):
|
|
|
356
365
|
verdict: { decision: 'suppress', reason: cached.reason },
|
|
357
366
|
renderDef: null,
|
|
358
367
|
...(cached.unreachable ? { unreachable: true as const } : {}),
|
|
368
|
+
...(cached.unreachable && cached.status !== undefined ? { status: cached.status } : {}),
|
|
359
369
|
}
|
|
360
370
|
}
|
|
361
371
|
const verdict = renderVerdict(cached.payload, context)
|