# `hotp/hotp.schema` ## Purpose Zod boundary schema for `HotpOptions`. Strict-mode parsing rejects unknown keys (so silent typos in caller configuration surface immediately) and narrows `algorithm` and `digits` to their literal-union values. Mismatches flow through `mapZodToOtp` so the boundary error carries a specific code (`INVALID_DIGITS` for the `digits` field, `INVALID_OPTIONS` otherwise). ## Features - `algorithm` enum: `"SHA1" | "SHA256" | "SHA512"`. - `digits` literal union: `6 | 7 | 8`. - Strict-mode object — unknown keys are rejected. ## Usage: Basic Example ```ts import { HotpOptionsSchema } from "@suaveplan/otp"; HotpOptionsSchema.parse({ algorithm: "SHA1", digits: 6 }); ``` ## Usage: Advanced Example ```ts import { HotpOptionsSchema } from "@suaveplan/otp"; // Programmatic option builder — feed parsed result directly into hotp(). const opts = HotpOptionsSchema.parse(JSON.parse(userJson)); ``` ## API Reference - `HotpOptionsSchema` — Zod object schema with `.strict()`. - `HotpOptionsInput` — type alias for the parsed shape. ## Implementation Notes The strict-mode check is the second-line defence behind TypeScript's compile checks: callers using `as never` or constructing options from JSON can still slip values past the type system. Pinning the schema here keeps the runtime contract aligned with the type-level contract. The schema is intentionally narrow — it covers only the surface that HOTP itself needs. Callers building their own option bags around HOTP (e.g. a vault item type that wraps HOTP with extra metadata) should extend this schema using Zod's `.extend()` or compose it via a discriminated union, rather than relaxing the strict-mode setting here. Keeping the base schema tight prevents accidental option-shape drift across the OTP family and means every caller benefits from the same validation guarantees. The `algorithm` enum covers exactly the three hashes RFC 4226 / 6238 §1.2 allow for OTP. SHA-3 family hashes are not part of any OTP RFC and would be rejected at this boundary even though `@noble/hashes` provides them. The `digits` literal union mirrors RFC 4226 §5.3 (`6`) plus the RFC 6238 §1.2 widening (`7`, `8`). RFC 6287 OCRA additionally permits digits=4, which is handled in the OCRA suite parser rather than here because the HOTP primitive does not need to expose it directly.