Files
suaveplan/openspec/archive/2026-05-10-enterprise-otp/tasks.md
2026-05-27 23:29:53 +01:00

159 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.md` end-to-end
- [ ] 1.2 Read `openspec/AGENTS.md` Sections 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.md` from `openspec/templates/proposal.md`
- [ ] 1.5 Fill in `## Source mapping` table — every requirement cites an RFC clause OR appears in the "no source line" table with justification
- [ ] 1.6 Draft `specs/otp/spec.md` from `openspec/templates/spec-delta.md`
- [ ] 1.7 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` 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 via `workspace:^` / `catalog:`
- [ ] 2.3 `tsconfig.json` extends `@suaveplan/typescript-config/library.json`
- [ ] 2.4 `vite.config.ts` for build, `vitest.config.ts` from `@suaveplan/testing/config`
- [ ] 2.5 `README.md` with package description, install snippet, quick-start, `## Modules` table 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` — `HotpOptionsSchema` Zod 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?)` over `T = floor((now − T0) / period)` with injectable `Clock`
- [ ] 4.2 `src/totp/verify.ts` — `verify(code, secret, opts?)` with `±window` drift; 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)` for `otpauth://totp/...` and `hotp` variant
- [ ] 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` — `OtpUri` discriminated union (`type: "totp" | "hotp"`)
- [ ] 5.4 `src/uri/uri.schema.ts` — `OtpUriSchema` Zod 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?)` and `base32Decode(input)`; `padding=false` default 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`, `OcraMutualAuthServer` per §7.1
- [ ] 7.5 `src/ocra/ocra.types.ts` — `OcraSuite`, `OcraParams`, `OcraCryptoFunction`, `OcraDataInput`
- [ ] 7.6 `src/ocra/ocra.schema.ts` — `OcraParamsSchema` Zod 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, injectable `Rng`
- [ ] 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 `±maxSteps` outside 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_CODES` registry via `defineCodes("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 (no `export *`)
- [ ] 11.2 Verify exports map count matches feature module count (§13 step 8)
## 12. Documentation
- [ ] 12.1 Sibling `.md` for every implementation `.ts` (≥200 words; basic + advanced examples; full API reference; implementation notes)
- [ ] 12.2 README links every co-located `.md` under `## 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, `bigint` past 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; `delta` semantics
- [ ] 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; `hotp` variant; 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 injected `Rng`
- [ ] 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` — every `OTP_CODES` entry 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-memory `MeterProvider`
- [ ] 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, no `error` findings
- [ ] 14.5 `bunx turbo build --filter=@suaveplan/otp` — green
## 15. Pre-archive 7-row checklist (AGENTS.md Section 13)
- [ ] 15.1 Generics threaded; `expectTypeOf` assertions 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_CODES` thrown 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` — refresh `MANIFEST.md`
- [ ] 17.2 `bun scripts/generate-missing.ts` — refresh `MISSING.yml`
- [ ] 17.3 `STATUS.md` — move `@suaveplan/otp` from `Pending` to `Completed`
- [ ] 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/`