# Changelog for shomei-core
All notable changes to `shomei-core` are documented here. This package adheres to the
[PVP](https://pvp.haskell.org/) and is versioned independently of the other
Shōmei packages.
## 0.3.0.0 — 2026-11-05
- **Breaking:** the package and its test suite move to the 0.3 API series.
- **Upgrade:** migration 0043 repairs legacy machine/delegated provenance and makes omitted SQL provenance default to machine. Missing kinds fail closed on read; authorize also refuses a stored actor. Stop old issuers before migration; affected backing-account humans can establish a new interactive session by logging in. See deployment.md.
- **Breaking:** remove the unreachable `VerifiedAuthentication.cloneWarning` field. Cloned/regressed counters return typed `WebAuthnCounterCloned` and follow the shared MFA failure/audit path. Signed software-authenticator fixtures now exercise the production verifier and workflows.
- **Breaking:** remove the unused unbatched authorization-code expiry port; cleanup uses the bounded sweeper. Password-hash updates now advance credential `updatedAt` in the fake interpreter as well.
- **Breaking:** PostgreSQL assembly uses the managed `Shomei.Persistence.Pool.Postgres.Pool` and its `UsageError`, `use` and `release` operations. Interrupted/failed sessions discard their connection and restore capacity; the external pool dependency is removed.
- Machine and delegated mints atomically enforce the configurable active principal ceiling (default 1000), preserving existing sessions after lowering the cap. Expiry frees capacity; finite sweeper retention bounds ordinary history by issuance rate and retention.
- **Breaking:** database transactions use the bounded statement wrapper; session bulk revocation returns winning IDs and the audited unit of work returns a count. Administrative session HTTP listing returns a keyset page (default 50, maximum 200). Server transaction limits default to three attempts/five seconds.
- Enforce finite new-write identity, display-name, passkey-label and role/scope budgets; preserve legacy identity authentication and snapshots. Login passwords over 256 code points fail before credential proof accounting. PostgreSQL adds migration 0039; see `docs/user/input-limits.md`.
- **Breaking:** `SigningKeyConfig` gains `legacyActorUntilUnix` and `emitLegacyActor` for the
finite actor-format upgrade window; both default to strict object-form behavior.
- OAuth code exchange validates the RFC 7636 verifier length and ASCII alphabet before hashing.
- Add the shared RFC 6750 bearer parser for server and downstream consumers.
- **Breaking:** passkey creation requires an explicit enrollment ceiling and returns a typed
result; `ShomeiConfig.maxPasskeysPerUser` defaults to 20. In-memory enrollment enforces the
ceiling atomically and workflows publish success only for accepted enrollment.
- **Breaking:** `IdToken` now redacts `Show` and has no generic JSON instances. OAuth wire
responses explicitly unwrap it, consistent with access and refresh token diagnostics.
- **Breaking:** `Session`, `NewSession`, `TokenPair`, `LoginCommand` and `SessionOptions` gain a
`remembered`/`remember` field, and `issueSession` takes the remember choice. `ShomeiConfig` gains
`rememberedSessionTTL` (`defaultRememberedSessionTTL`, 365 days), used for remembered sessions.
`completeMfaWith` and `completePasswordlessLoginWith` take the choice; `completeMfa` and
`completePasswordlessLogin` keep their signatures and are not remembered. The refresh token issued
at sign-in no longer outlives its session.
## 0.2.0.0 — 2026-08-27
- **Breaking:** `AccessToken`, `RefreshToken`, and `TokenPair` no longer expose generic JSON
instances, and token `Show` output is redacted. `LoginFailedData` replaces the submitted
`loginId` with an optional hashed `accountKey` and resolved `userId`; resolved failures now
populate the audit envelope's `user_id` column.
- **Breaking:** `SmtpConfig` no longer contains the SMTP password and `WebhookConfig` no longer
contains the webhook signing secret. Runtime credentials belong to the server or embedding
host, so `Show`/`ToJSON` of `ShomeiConfig` cannot expose them.
- **Breaking:** `configSigningAlgorithm` now returns `Either Text SigningAlgorithm`; embedding
applications must reject invalid hand-built signing configuration instead of receiving an
implicit `ES256` fallback.
- **Breaking:** login-attempt recording, TOTP/passkey counters, user-status changes, session
revocation, and credential-reset tails now expose compare-and-swap or transactional operations.
Single-use transitions report whether they won, login failures are recorded and counted
atomically, and password reset/change revokes every affected session in the same unit of work.
- **Breaking:** `StoredSigningKey` gains `revokedAt`, and `SigningKeyStore` gains the atomic
`ReplaceActiveSigningKey` operation. The in-memory interpreter stamps every lifecycle timestamp.
- **Breaking:** `SigningKeyConfig` gains `allowedClockSkewSeconds`; the reserved custom-claim set
now also excludes `nbf` and `jti`.
- **Breaking:** `Session` and `NewSession` now carry a `kind` that records whether the session was
established interactively, by `client_credentials`, or by delegation.
- OAuth authorize accepts only a live interactive session and refuses machine, delegated, or
explicit-actor credentials.
- RFC 8693 exchange and impersonation always verify the presented session; impersonation also
requires an active operator account.
- Authorization-code exchange now honours `emailVerificationRequired`.
- OAuth sessions persist their granted scopes; refresh preserves them and refuses a minting-client
mismatch, including use through the bespoke refresh workflow.
- OAuth-client registration and authorize refuse reserved privilege scopes while service accounts
remain their intended holders.
- Authorization-code rows bind to their minted sessions; replay revokes the session and refresh
family and emits `oauth_code_replayed`. The core revocation policy models client and
service-account ownership plus the `shomei:admin` escape hatch.
## 0.1.0.0 — 2026-08-24
Initial release. The transport-agnostic heart of the Shōmei authentication
toolkit.
- Domain model for accounts, sessions, credentials, and audit events. The
principal is a free-form, case-insensitive `loginId` with email as an
optional attribute; email-first callers keep working because `loginId`
defaults to the email when only an email is supplied.
- `effectful` port interfaces for every side effect — user, credential,
session, refresh-token, one-time-token, role, service-account, OAuth client
and authorization-code, MFA, passkey, and signing-key stores, plus the
clock, token generator, audit publisher/reader, notifier, and breach
checker — with an in-memory interpreter (`Shomei.Test.InMemory`) for tests.
- Account lifecycle workflows: signup, login, refresh, logout, email
verification, and password reset/change. One-time token consumption and
refresh-token rotation are compare-and-swap operations, and the write tails
are made atomic by an `AuthUnitOfWork` port.
- Password policy with configurable rules, context-aware validation,
an embedded common-password dictionary, and a `PasswordBreached` violation
backed by a `PasswordBreachChecker` port.
- Authorization: a role registry, role-permission tables, expiring role
grants, claims enrichment from the role catalog at every mint, and a
reserved `permissions` claim.
- OAuth 2.0 / OpenID Connect: authorization-code, refresh, and
`client_credentials` grants, ID tokens, database-backed service accounts,
and the RFC 8693 token-exchange (delegation and impersonation) workflow
with an `act` actor claim.
- Multi-factor authentication: RFC 6238 TOTP, recovery codes, and the
WebAuthn passkey ceremony port for enrollment, step-up, and passwordless
login.
- Abuse protection: brute-force account lockout and per-IP/per-account
throttling. Absolute session expiry is enforced in refresh and token
verification, and login is free of an account-enumeration timing oracle.