# Changelog

Release history, and the compatibility promise for each part of the API.

## What counts as a breaking change

Knowing what is covered matters more than the version numbers, so it comes
first.

  
- **Error codes** (`stable`) — 
    A `PasskeyErrorCode` is never renamed or removed in a minor release. New
    codes may be added, so treat an unrecognised code as a generic failure
    rather than an impossible state.
  
  
- **PasskeyStore** (`stable`) — 
    Method signatures do not change in a minor release. A new method would be a
    major, because every existing implementation would break.
  
  
- **Stored record shapes** (`stable`) — 
    `PasskeyCredential` and `PasskeyUser` fields are not removed or retyped in a
    minor release. Your database schema is safe across minors.
  
  
- **Wire types** (`stable`) — 
    The JSON that travels between `passkify/client` and the server. Mixed
    versions across a deploy must keep working.
  
  
- **Error messages** (`not stable`) — 
    Written for developers and reworded as the documentation improves. Branch
    on `code`, never on message text.
  
  
- **Low-level exports** (`not stable`) — 
    `parseAuthenticatorData`, `decodeCBOR` and the rest may change shape in a
    minor. They are for tooling; the ceremony methods are the API.
  

> **rpID is outside versioning entirely**
>
> 
Changing `rpID` in your own configuration orphans every credential your users
have registered, in any version. It is the one setting with no migration path.
See [how passkeys work](/docs/how-passkeys-work#relying-party-id).

## Releases

### 0.2.0

> **Two breaking changes**
>
> 
`counter_regression` answers **403** rather than 401 — branch on the code, not
the status. **RS1** (COSE `-65535`, RSA with SHA-1) is gone, and a config that
lists it in `supportedAlgorithms` now throws at construction.

**The verification path is pure WebCrypto**

No `node:` built-in is imported anywhere a ceremony can reach, which is what
makes the Cloudflare Workers, Deno and Vercel Edge support real rather than
aspirational. A test walks every source file on each run and fails if one
appears. X.509 handling is written here rather than delegated, and ECDSA
signatures are converted from DER by a deliberately strict parser — a lenient
one there is a signature-malleability bug.

**Server**

- WebAuthn Level 3: the three signal methods, `getClientCapabilities()`,
  Related Origin Requests with the published file derived from `origin`,
  conditional create, and `hints`/`extensions` pass-through including `prf`.
- Adapters for Next.js, Hono, Fastify, SvelteKit, Remix and Cloudflare Workers.
- Stores for Postgres, Prisma, Redis, MongoDB and SQLite, each taking the driver
  you already have so none of them becomes a dependency.
- A [check registry](/docs/server/registration#the-checks) the verifiers index
  into, plus `explain: true` and an `onCheck` hook that return the trace.
- `PasskeyError` gained `publicMessage` and `details`, so text written for a
  developer is not sent to an unauthenticated caller. Adapters take
  `verbose: true` to restore the old behaviour in development.

**Browser**

- `passkify/react`: `useRegister`, `useLogin`, `usePasskeyAutofill`,
  `usePasskeys`, `usePasskeySupport`.
- Client APIs for the credential-management routes that previously existed only
  on the server: `listPasskeys`, `renamePasskey`, `deletePasskey`,
  `syncPasskeys`.

**Tooling**

- `npx passkify` — `init`, `doctor`, `skills install`, and `mcp`, an MCP server
  whose `validate_config` runs the real validator.
- `passkify/store-conformance`, 19 cases including a genuine concurrency race on
  `takeChallenge`, so a read-then-delete store fails rather than silently
  permitting replay.
- `passkify/testing`, the virtual authenticator the library's own suite uses.
- `/llms.txt`, `/llms-full.txt` and a `.md` twin of every documentation page.

**Fixed**

- An attestation format passkify cannot verify no longer fails a registration
  the site never asked for attestation on. `verifyAttestation` ran on every
  registration and threw `unsupported_feature` (500) for any format without a
  verifier, so TPM-backed Windows Hello could not register at all.
- An oversized request body returns **413** and is drained rather than left
  resolving to `undefined` with the connection open.
- Declaration files are emitted for the CommonJS build as `.d.cts`, with `types`
  per condition — the old layout was reported as "masquerading as ESM" for every
  `require()`.

### 0.1.0

Initial release.

**Server**

- `PasskeyServer` with `startRegistration`, `finishRegistration`,
  `startAuthentication` and `finishAuthentication`.
- Credential management: `listCredentials`, `renameCredential`,
  `deleteCredential`.
- Full WebAuthn §7.1 and §7.2 verification, 29 checks across the two
  ceremonies.
- Hand-written CBOR decoder, COSE key parsing, and signature verification for
  ES256/384/512, RS256/384/512, RS1, PS256/384/512 and EdDSA.
- Attestation verification for `none`, `packed`, `fido-u2f` and `apple`.
- `MemoryStore`, and the `PasskeyStore` interface.
- Express middleware and a standards `Request` handler.
- Eager configuration validation for `rpID` and `origin`.

**Browser**

- `register`, `login`, `signInWithAutofill`.
- `isSupported`, `isPlatformAuthenticatorAvailable`, `isAutofillAvailable`.
- `createCredential` and `getAssertion` for use against your own endpoints.
- Automatic cancellation of a superseded ceremony.

**Both**

- `PasskeyError` with stable codes on either side of the wire, including
  `last_credential` (409) when removing an account's only passkey is refused.
- Zero runtime dependencies. ESM and CommonJS builds.
- 92 tests, most of them tampered responses driven by a virtual authenticator.

## Upgrading

**0.1.0 to 0.2.0.** Two things to check, neither of which touches your database:

1. If any code branches on the HTTP status of a counter regression, move it to
   `error.code === 'counter_regression'`. The status changed from 401 to 403.
2. If `supportedAlgorithms` lists RS1 (`-65535`), remove it. The server now
   refuses to construct rather than accepting a SHA-1 signature.

Stored `PasskeyCredential` and `PasskeyUser` records are unchanged, as are the
wire types, so a mixed deploy keeps working while it rolls.
