10 KiB
10 KiB
Tasks — @suaveplan/otp (RFC 4226 / 6238 / 6287 + Key URI + recovery codes)
Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify.
1. Spec authoring
- 1.1 Read
../suaveplan/enterprise/otp.mdend-to-end - 1.2 Read
openspec/AGENTS.mdSections 13, 14, 14a - 1.3 Read RFC 4226 (HOTP), RFC 6238 (TOTP), RFC 6287 (OCRA), Google Authenticator Key URI Format wiki page
- 1.4 Draft
proposal.mdfromopenspec/templates/proposal.md - 1.5 Fill in
## Source mappingtable — every requirement cites an RFC clause OR appears in the "no source line" table with justification - 1.6 Draft
specs/otp/spec.mdfromopenspec/templates/spec-delta.md - 1.7 Every requirement in the spec delta carries a
**Verified by:**line naming concreteit(...)titles
2. Package scaffold
- 2.1
packages/vault/otp/directory structure (src/{hotp,totp,uri,base32,ocra,recovery,drift,errors,observability}/) - 2.2
package.json—@suaveplan/otp@0.0.0,type: "module",sideEffects: false, deps viaworkspace:^/catalog: - 2.3
tsconfig.jsonextends@suaveplan/typescript-config/library.json - 2.4
vite.config.tsfor build,vitest.config.tsfrom@suaveplan/testing/config - 2.5
README.mdwith package description, install snippet, quick-start,## Modulestable linking every co-located.md - 2.6 Subpath exports map covers
.,./uri,./base32,./ocra,./errors,./observability
3. Implementation — HOTP (RFC 4226)
- 3.1
src/hotp/hotp.ts—hotp(secret, counter, opts?)with dynamic truncation per §5.3 step 2 - 3.2
src/hotp/hotp.types.ts—HotpOptions,HotpAlgorithm,HotpDigits - 3.3
src/hotp/hotp.schema.ts—HotpOptionsSchemaZod boundary - 3.4 8-byte big-endian counter encoding helper in
src/hotp/encode-counter.ts - 3.5
src/hotp/index.ts— explicit named re-exports
4. Implementation — TOTP (RFC 6238)
- 4.1
src/totp/totp.ts—totp(secret, opts?)overT = floor((now − T0) / period)with injectableClock - 4.2
src/totp/verify.ts—verify(code, secret, opts?)with±windowdrift; constant-time loop - 4.3
src/totp/verify-many.ts—verifyMany(code, secrets[], opts?)constant-time across the secret list - 4.4
src/totp/totp.types.ts—TotpOptions,TotpVerifyOptions,TotpVerifyResult,TotpVerifyManyResult - 4.5
src/totp/totp.schema.ts—TotpOptionsSchema,TotpVerifyOptionsSchema(window∈ [0,10]) - 4.6
src/totp/index.ts— explicit named re-exports
5. Implementation — Key URI Format
- 5.1
src/uri/parse-uri.ts—parseUri(uri)forotpauth://totp/...andhotpvariant - 5.2
src/uri/format-uri.ts—formatUri(opts)round-trip with percent-encoding of label and issuer - 5.3
src/uri/uri.types.ts—OtpUridiscriminated union (type: "totp" | "hotp") - 5.4
src/uri/uri.schema.ts—OtpUriSchemaZod boundary - 5.5
src/uri/index.ts— explicit named re-exports
6. Implementation — Base32 (RFC 4648 §6)
- 6.1
src/base32/base32.ts—base32Encode(bytes, opts?)andbase32Decode(input);padding=falsedefault to match Google Authenticator - 6.2
src/base32/index.ts— explicit named re-exports
7. Implementation — OCRA (RFC 6287)
- 7.1
src/ocra/ocra.ts—ocra(suite, key, params)per §6 computation - 7.2
src/ocra/suite.ts—parseOcraSuite(suite)per §6.1 grammar - 7.3
src/ocra/verify-ocra.ts—verifyOcra(...)constant-time - 7.4
src/ocra/mutual-auth.ts—OcraMutualAuthClient,OcraMutualAuthServerper §7.1 - 7.5
src/ocra/ocra.types.ts—OcraSuite,OcraParams,OcraCryptoFunction,OcraDataInput - 7.6
src/ocra/ocra.schema.ts—OcraParamsSchemaZod boundary - 7.7
src/ocra/index.ts— explicit named re-exports
8. Implementation — Recovery codes
- 8.1
src/recovery/generate.ts—generateRecoveryCodes(count, opts?)Crockford-base32 grouping, injectableRng - 8.2
src/recovery/hash.ts—hashRecoveryCode(code, opts?)Argon2id via@suaveplan/crypto-hash - 8.3
src/recovery/verify.ts—verifyRecoveryCode(input, hashedCodes[])constant-time over the full list - 8.4
src/recovery/recovery.types.ts—RecoveryCodeOptions,RecoveryVerifyResult - 8.5
src/recovery/index.ts— explicit named re-exports
9. Implementation — Clock-drift detector
- 9.1
src/drift/detect.ts—detectDrift(code, secret, opts?)searches±maxStepsoutside the verify window - 9.2
src/drift/drift.types.ts—DriftDetectOptions,DriftResult - 9.3
src/drift/index.ts— explicit named re-exports
10. Implementation — Errors and observability
- 10.1
src/errors/codes.ts—OTP_CODESregistry viadefineCodes("OTP", [...]);OtpError extends SuaveplanError<OtpErrorCode> - 10.2
src/errors/index.ts— explicit named re-exports - 10.3
src/observability/metrics.ts— histograms and counters wired against@opentelemetry/api - 10.4
src/observability/index.ts— explicit named re-exports
11. Root barrel + subpath exports
- 11.1
src/index.ts— explicit named re-exports of HOTP, TOTP, verify, verifyMany, recovery, drift, errors, observability surfaces (noexport *) - 11.2 Verify exports map count matches feature module count (§13 step 8)
12. Documentation
- 12.1 Sibling
.mdfor every implementation.ts(≥200 words; basic + advanced examples; full API reference; implementation notes) - 12.2 README links every co-located
.mdunder## Modules
13. Tests (runner-neutral imports — @suaveplan/testing/runner only)
- 13.1
src/hotp/hotp.test.ts— RFC 4226 Appendix D 10 vectors, SHA-256/512 widening,bigintpast 2^53, digits 6/7/8 - 13.2
src/totp/totp.test.ts— RFC 6238 Appendix B 6 vectors across SHA-1/256/512 at digits=8 - 13.3
src/totp/verify.test.ts— window 0 / 1 / 10 / >10;deltasemantics - 13.4
src/totp/verify-many.test.ts— multi-secret happy path + miss path - 13.5
src/totp/constant-time.test.ts— every windowed step and every secret evaluated regardless of early match - 13.6
src/uri/parse-uri.test.ts— every documented parameter;hotpvariant; malformed paths - 13.7
src/uri/format-uri.test.ts— label / issuer percent-encoding;:in label rejected - 13.8
src/uri/round-trip.property.test.ts— fast-check property test - 13.9
src/base32/base32.test.ts— RFC 4648 §6 vectors; padded + unpadded; case-insensitivity; invalid alphabet - 13.10
src/ocra/ocra.test.ts— RFC 6287 Appendix C.1–C.3 vectors - 13.11
src/ocra/suite.test.ts— every §6.1 grammar branch - 13.12
src/ocra/mutual-auth.test.ts— three-message §7.1 round-trip - 13.13
src/recovery/generate.test.ts— Crockford-base32 grouping; deterministic with injectedRng - 13.14
src/recovery/verify.test.ts— Argon2id round-trip; constant-time over the full set - 13.15
src/drift/detect.test.ts— drift outside the verify window - 13.16
src/errors/codes.spec-conformance.test.ts— everyOTP_CODESentry thrown by at least one production path - 13.17
src/observability/metrics.spec-conformance.test.ts— every histogram and counter emitted; assertions against an in-memoryMeterProvider - 13.18
src/index.test.ts— every documented subpath resolves - 13.19 Every
it(...)carries{ timeout: 5000 }(or 30000 for genuinely async) - 13.20 Runner-neutral imports — CLAUDE.md §11 grep clean
14. Pre-archive verification (the 5-gate triple)
- 14.1
bun run check— Biome 0 errors / 0 warnings / 0 info across the whole monorepo - 14.2
bunx turbo lint typecheck test --filter=@suaveplan/otp— green - 14.3
bunx turbo test:coverage --filter=@suaveplan/otp— 100/100/100/100 - 14.4
bun scripts/openspec-conformance.ts @suaveplan/otp— exit 0, noerrorfindings - 14.5
bunx turbo build --filter=@suaveplan/otp— green
15. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 15.1 Generics threaded;
expectTypeOfassertions on every literal-type claim (HotpAlgorithm,HotpDigits,OtpUri.type) - 15.2 Zod schemas wired at every declared boundary (
HotpOptionsSchema,TotpOptionsSchema,TotpVerifyOptionsSchema,OtpUriSchema,OcraParamsSchema) - 15.3 Every error code in
OTP_CODESthrown by at least one production path - 15.4 Strict-mode behaviors tested both on and off (window cap, base32 padding, label
:rejection) - 15.5 OTel histograms and counters emitted from production paths and asserted with a real in-memory
MeterProvider - 15.6 Lifecycle dedup — N/A (pure functions; no shared mutable state)
- 15.7 No retroactive tick — every box above reflects work done in this change
16. Benchmarks (mitata, src/__benchmarks__/)
- 16.1
totp_generate.bench.ts - 16.2
totp_verify_window_1.bench.ts - 16.3
totp_verify_window_10.bench.ts - 16.4
parse_uri.bench.ts - 16.5
format_uri.bench.ts - 16.6 Baseline numbers published in README
17. Root-doc refresh + markdownlint (AGENTS.md §13b)
- 17.1
bun scripts/folder-reorg/generate-manifest.ts— refreshMANIFEST.md - 17.2
bun scripts/generate-missing.ts— refreshMISSING.yml - 17.3
STATUS.md— move@suaveplan/otpfromPendingtoCompleted - 17.4
SUMMARY.md— add per-package paragraph - 17.5
CLAUDE.md— add package listing under "Vaulted" / vault-orchestration section - 17.6
bunx markdownlint-cli2 --fix— auto-fix; re-stage - 17.7
bunx markdownlint-cli2— second pass exit 0
18. Archive
- 18.1 Fold spec delta into
openspec/specs/otp/spec.md(canonical, status ACTIVE) - 18.2 Move
openspec/changes/enterprise-otp/→openspec/archive/<yyyy-mm-dd>-enterprise-otp/