8.7 KiB
8.7 KiB
Tasks — complete-webauthn-mds3
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Package scaffolding
- 1.1 Create
packages/auth/webauthn-mds3/per genesis package layout. - 1.2
package.jsonatversion: 0.0.0,sideEffects: false,workspace:^for internal deps,catalog:*for external. - 1.3
tsconfig.jsonextending@suaveplan/typescript-configuniversal preset. - 1.4
vitest.config.tsviacreateTestConfig()from@suaveplan/testing/config; coverage 100/100/100/100;provider: 'istanbul'. - 1.5 Subpath exports:
.,./fetch,./verify,./cache,./bootstrap,./refresher,./resolver,./status-filter,./events,./errors. - 1.6 Root
README.mdwith one-paragraph description, install, quick-start, Modules section.
2. Constants + FIDO root pinning
- 2.1
src/constants/fido-root.ts+.md— pin FIDO Alliance MDS3 root CA DER bytes + SHA-256 fingerprint; exportFIDO_MDS3_DEFAULT_URL,DEFAULT_REFRESH_INTERVAL_MS,DEFAULT_DENY_STATUSES,JITTER_WINDOW_MS.
3. Error codes (≥12; current: 14)
- 3.1
src/errors/error-codes.ts+.md—defineCodes("WEBAUTHN_MDS3", [BLOB_FETCH_FAILED, BLOB_NOT_MODIFIED, BLOB_MALFORMED, BLOB_SIGNATURE_INVALID, BLOB_CHAIN_UNTRUSTED, BLOB_EXPIRED, BLOB_ALG_DISALLOWED, BLOB_SCHEMA_MISMATCH, CACHE_READ_FAILED, CACHE_WRITE_FAILED, CACHE_ROLLBACK_REFUSED, RESOLVER_LOOKUP_FAILED, REFRESHER_ALREADY_RUNNING, REFRESHER_ABORTED]). - 3.2
src/errors/errors.test.ts— assert every code is reachable from a production path.
4. Schemas
- 4.1
src/schemas/metadata-blob.schemas.ts+.md—MetadataBlobPayloadSchema,MetadataStatementSchema,AuthenticatorStatusSchema,StatusReportSchema,BiometricStatusReportSchema,EcdaaTrustAnchorSchema,MetadataBlobEntrySchematracking FIDO MDS3 §3.1.
5. Fetch (conditional)
- 5.1
src/fetch/fetch-blob.ts+.md—fetchMetadataBlob({ fetch, url?, signal?, cache? })returningFetchOutcome; sendsIf-None-Match/If-Modified-Sincewhen validators present; maps304→{ kind: "not-modified" }; non-2xx (excluding 304) →BLOB_FETCH_FAILED. - 5.2
src/fetch/fetch-blob.test.ts— happy, 304, non-2xx, abort, conditional headers asserted.
6. Verify
- 6.1
src/verify/verify-blob.ts+.md— JWS via@suaveplan/jws; alg allow-list{RS256, ES256, ES384}; chain to pinned FIDO root via@suaveplan/x509; decode + Zod-validate payload; reject expired. - 6.2
src/verify/verify-blob.test.ts— happy, signature-flipped, chain-broken, expired, alg-disallowed, schema-mismatch.
7. Cache (rollback-protected)
- 7.1
src/cache/metadata-cache.ts+.md—MetadataCacheoverStorageLike<CachedMetadataEnvelope>; namespacewebauthn-mds3:blob:v1; rejects writes whosepayload.no < cached.payload.nowithCACHE_ROLLBACK_REFUSED. - 7.2
src/cache/metadata-cache.test.ts— round-trip, rollback refusal, read/write failure mapping.
8. Bootstrap blob
- 8.1
src/bootstrap/blob.ts+.md— exportBOOTSTRAP_BLOB: string(base64 JWS) plus a verifiedBOOTSTRAP_PAYLOAD: MetadataBlobPayloadderived at module load. - 8.2
src/bootstrap/blob.test.ts—verifyMetadataBlob(BOOTSTRAP_BLOB)resolves; bootstrap is overridden by first successful refresh.
9. Refresher
- 9.1
src/refresher/metadata-refresher.ts+.md—createMetadataRefresher({ fetch, storage, clock, logger, eventBus?, intervalMs?, signal? }); reentrancy guard; jittered schedule atnextUpdate - jitter; conditional fetch; EventBus + OTel. - 9.2
src/refresher/metadata-refresher.test.ts— schedule, fallback intervalMs, reentrancy, abort, 304-skips-verify, verify-fail keeps prior envelope, EventBus + OTel emitted.
10. Resolver
- 10.1
src/resolver/types.ts+.md—MetadataResolver,MetadataLookup. - 10.2
src/resolver/memory.ts+.md+.test.ts—createMemoryResolver({ payload }); O(1) primary map by AAGUID; secondary O(1) map by SKI hex; constant-time AAGUID equality. - 10.3
src/resolver/cached.ts+.md+.test.ts—createCachedResolver({ cache, refresher?, bootstrap? }); lazy refresh on first lookup; falls back to bootstrap when cache empty AND fetch fails. - 10.4
src/resolver/manual.ts+.md+.test.ts—createManualResolver({ jws }); runsverifyMetadataBlobthen delegates to memory resolver.
11. Status filter
- 11.1
src/status-filter/status-filter.ts+.md—createStatusFilter({ allow?, deny? }); deny wins; defaultsDEFAULT_DENY_STATUSES. - 11.2
src/status-filter/status-filter.test.ts— allow-only, deny-only, both, default deny set, deny-wins precedence.
12. Events
- 12.1
src/events/topics.ts+.md— typed topicswebauthn-mds3.refresh.success,webauthn-mds3.refresh.failure,webauthn-mds3.lookup.hit,webauthn-mds3.lookup.missvia@suaveplan/events. - 12.2
src/events/topics.test.ts— every topic fires from its production call site.
13. Observability
- 13.1
src/observability/metrics.ts+.md— countersrefresh_total{outcome},lookup_total{outcome}; histogramblob_age_ms. No-op when no exporter. - 13.2
src/observability/spans.ts+.md— spansfetch,verify,refresh,lookupwith attribute set + status mapping. - 13.3
src/observability/{metrics,spans}.test.ts— every metric and span records on its production path.
14. Fixtures
- 14.1
src/__fixtures__/mds3-sample-blob.jwt— captured FIDO Alliance conformance sample. - 14.2
src/__fixtures__/mds3-corrupted-blob.jwt— signature-flipped. - 14.3
src/__fixtures__/mds3-rolled-back-blob.jwt— same payload withnodecremented. - 14.4
src/__fixtures__/index.ts— typed exports.
15. Benchmarks
- 15.1
src/__benchmarks__/verify-blob.bench.ts— JWS verify throughput. - 15.2
src/__benchmarks__/resolve-aaguid.bench.ts— AAGUID lookup throughput.
16. Tier-purity + subpath harnesses
- 16.1
src/dep-graph.test.ts— zero DOM / Node-only imports across the public surface. - 16.2
src/index.test.ts— every documented subpath resolves.
17. Integration into @suaveplan/webauthn
- 17.1 Extend
VerifyAttestationInputwith optionalmetadata?: MetadataResolverandstatusFilter?: StatusFilter(typed viaimport type— no runtime dep). - 17.2 Extend
AttestationVerifyResultwith optionalmetadata?: MetadataLookup. - 17.3 Wire post-verifier resolution: AAGUID for non-U2F; SKI for
fido-u2f/ all-zero AAGUID; downgradetrusted=falsewhen filter rejects status. - 17.4 Update
packages/auth/webauthn/src/verifier/verify-attestation.test.ts— add six scenarios per the modified delta. - 17.5 Refresh
verify-attestation.mdand thepacked.md/fido-u2f.md/none.mdanchors to point atMetadataResolveras the canonical seam. - 17.6 Add
@suaveplan/webauthn-mds3topackages/auth/webauthn/README.mdModules section.
18. Pre-publish gate
- 18.1
bunx turbo lint typecheck test --filter=@suaveplan/webauthn-mds3green. - 18.2
bunx turbo test:coverage --filter=@suaveplan/webauthn-mds3100/100/100/100. - 18.3
bunx turbo lint typecheck test --filter=@suaveplan/webauthngreen (dependent sweep). - 18.4 Banned-pattern scan: zero matches (§13 step 4).
- 18.5 Barrel-shape scan: zero matches (§13 step 4a).
- 18.6 Test-runner import scan: zero matches (§13 step 4b).
- 18.7 Missing-
.mdscan: zero misses. - 18.8
bunx markdownlint-cli2 --fixthen no-fix pass exits 0.
19. Repo-root manifest refresh (§13b)
- 19.1
bun scripts/folder-reorg/generate-manifest.ts→MANIFEST.md. - 19.2
bun scripts/generate-missing.ts→MISSING.yml. - 19.3
STATUS.mdadd@suaveplan/webauthn-mds3row. - 19.4
SUMMARY.mdparagraph added.
20. Fold + archive
- 20.1 Fold
specs/webauthn-mds3/spec.mdintoopenspec/specs/webauthn-mds3/spec.md. - 20.2 Fold
specs/webauthn/spec.md## MODIFIED Requirementsintoopenspec/specs/webauthn/spec.md. - 20.3 Move
openspec/changes/complete-webauthn-mds3/→openspec/archive/<yyyy-mm-dd>-complete-webauthn-mds3/.