# Playstack

> Composable TypeScript packages, UI, and integrations for building software applications.

Playstack is in private preview. Documentation describes source contracts, not a guarantee of registry availability or production readiness. Check [release availability](https://playstack.dev/docs/packages#release-availability) before installing.

[Product and documentation index](https://playstack.dev/llms.txt) · [Full offering and documentation text](https://playstack.dev/llms-full.txt)

## Playstack

Source: https://playstack.dev/index.md

TypeScript packages for authentication, billing, storage, and more. Add what you need to the framework you already use.

Start with one capability; Playstack does not require adopting the whole stack.

[Features](/features) · [Frameworks](/frameworks) · [Integrations](/integrations) · [Pricing and access](/pricing) · [Getting started](/docs/getting-started)

## Pricing and access

Source: https://playstack.dev/pricing.md

104 packages at one fixed version: 51 Free under MIT, with public npm as the intended account-free channel. 51 packages are assigned Pro; 2 paid packages await tier allocation. Check preview availability before installing. Plus is planned and has no exclusive packages yet.

## Free

51 Free packages

MIT-licensed application foundations, with public npm as the intended account-free distribution channel. Check preview availability before installing.

- Auth, API keys, and subscription billing
- Mail, queues, storage, cache, and search
- Events, localization, validation, and rate limits
- NestJS, Next.js, and React bindings included

[Browse Free packages](/docs/free)

## Plus

Planned

A future middle tier shaped around the needs we hear from early teams.

- No exclusive packages yet
- Shaped around what early teams ask for
- Same fixed versions as Free and Pro
- Early announcements through the access list

[See the Plus plan](/docs/plus)

## Pro

51 Pro packages

The capabilities assigned to Pro, for individuals and teams, under the Pro License. Request preview access before setting up registry installation.

- Team accounts, seats, commerce, and licensing
- Private registry, audiences, and analytics
- App UI, Admin UI, and Chakra compositions
- Releases you subscribed to stay installable

[Request pricing access](/pricing#pricing-access)

## The intended licence model

Paid licences are intended to be perpetual for eligible releases, with one year of updates and optional annual recurring renewal—not per-project charges. Solo covers one named developer; Team includes five seats, with optional five-seat packs.

Launch allocation is planned for the first 100 buyers of each enabled base offer. The lower launch renewal applies only during uninterrupted renewal; canceling and later rejoining does not restore it.

Checkout is not enabled. Licence fulfilment, final terms and live billing qualification must be completed before sale. Plus remains unavailable until packages are assigned to it. This is a preview of the intended terms, not an offer to purchase.

[Licensing overview and draft status](/licensing). [Request private-preview information](/pricing#pricing-access). Confirming an email does not create an account, grant paid access or start a subscription.

## Frequently asked questions

Source: https://playstack.dev/faq.md

Start here for the basics. These answers describe the current offering, not a
promise that every package, integration or commercial option is live. The
[package reference](/docs/packages), [status page](/status) and
[changelog](/changelog) provide the more detailed picture.

## What is Playstack?

Playstack is a collection of TypeScript packages for application capabilities
such as authentication, billing, storage, queues and notifications. Portable
contracts hold the core behavior; framework and provider adapters connect those
contracts to your application. Start with the [feature overview](/features) or
try the [getting-started example](/docs/getting-started).

## Do I need to replace my framework or adopt the whole stack?

No. You can add one capability to an existing project and expand from there.
Playstack is not a required application framework or a hosted backend that takes
ownership of your product's data. Your application still owns its routes,
authorization policy, deployment and provider accounts. Browse the
[framework guides](/frameworks) for the bindings that fit your project.

## What is the difference between Free, Plus and Pro?

The [Free baseline](/docs/free) is MIT-licensed. [Pro](/docs/pro) contains the
assigned paid capabilities, including team accounts and application UI
compositions. Plus is planned and does not currently have an exclusive package
allocation. A package's tier describes its access policy; it does not prove that
a particular release is published or that an integration is production-qualified.
Check [package access](/docs/packages#access-policy) before installing.
The [licensing overview](/licensing) links the MIT text and clearly identifies
the Pro commercial licence as an unreviewed draft, not finalized sale terms.

## Can I purchase a licence now?

Public checkout is not enabled. The [pricing page](/pricing) explains the intended
licence and updates model and lets you request private-preview information.
That explanation is not an offer to purchase. Final terms, licence fulfilment
and live billing qualification must be completed before sale; a preview request
does not start a paid subscription.

## How do I join the private preview?

Use the [private-preview request form](/pricing#pricing-access) and confirm your
address using the email link. Confirmation adds you to the confirmed list and
queues a welcome message explaining that access instructions will follow.
Requesting or confirming a place does not create an application account, grant
paid package access or guarantee an admission date. If a confirmation email does
not arrive, check spam and try the form again later, or contact us below.

## Is a Free account the same as private-preview or Pro access?

No. Where dashboard signup is enabled, a verified Free account can prepare
projects and create an ordinary read-only registry token. That token can read
eligible published Free packages, not every paid package. Paid access and any
separately approved preview access are checked independently. The email request
list is not a registry credential. See [accounts, tokens and access](/docs/registry-access).

## How do I install packages from the private registry?

Configure the Playstack registry scope and provide your token through an
environment variable, as shown in [getting started](/docs/getting-started#install-only-what-you-use).
Keep the real token in your shell or CI secret store, never in Git or browser
code. Hosting builds that install private packages need their own configured
build secret. A scoped registry setting applies to every `@playstack/*` request;
it does not automatically fall back to public npm for a missing release.

## Which package versions should I use together?

Keep the selected `@playstack/*` graph on the same exact, verified release.
Read the changelog and migration guidance before upgrading. Documentation can
describe upcoming APIs before their release is available from your approved
package source; local builds and Yalc snapshots do not prove registry publication.
Check [release availability](/docs/packages#release-availability), rather than
assuming the newest source or documentation version is ready to install.

## Do I need Prisma or a particular infrastructure provider?

Only when your chosen composition requires it. Packages expose specific
persistence and provider adapters; selecting an adapter can add peer dependencies,
configuration or a schema migration. The CLI can preview those requirements, but
it does not choose your production database, apply migrations or configure provider
credentials for you. See [CLI configuration](/docs/cli/configuration) and the
[integration catalog](/integrations).

## Does installing a package make that feature production-ready?

No. Your application must still compose authorization, transaction boundaries,
workers, provider credentials, migrations and deployment settings as applicable.
Package tests and local fixtures are not qualification of your deployed service.
The [status page](/status) explains how implementation, documentation and adapter
status are presented; use each package's guide for its remaining host obligations.

## Can I stop preview and release emails?

Use the unsubscribe link in a list email, or reply to ask for help. The site's
preview list and the dashboard's optional product-update preference are separate;
you can change the dashboard preference in Account Settings. Neither is implied
by registration, purchase or token creation. Required account-security messages
are separate from optional updates. Read the [privacy note](/privacy) for details.

## Where can I ask a question or report an integration problem?

Email [hello@playstack.dev](mailto:hello@playstack.dev). Tell us which packages,
versions, framework and runtime you use, what you expected and what happened.
Include a small reproduction or redacted error if possible. Do not send registry
tokens, provider secrets, passwords or private customer data. Useful recurring
questions will help shape future documentation and FAQ entries.

## Licensing

Source: https://playstack.dev/licensing.md

Playstack distinguishes package licensing from package availability, registry
access and the site's privacy policy. This overview does not change any package
licence or grant access to unpublished or paid releases.

## Free packages: MIT

The [Free baseline](/docs/free) uses the MIT licence. You can
[read the full MIT licence text](/licenses/mit.txt), including its copyright notice
and conditions. Check the licence included with the exact package version you
install; a Free classification is not proof that a release is publicly available.

## Pro packages: commercial draft

> **Draft — not yet reviewed by counsel.** The existing Playstack Pro License is
> Draft 0.1, dated September 19, 2026. It is published here for review, not as
> finalized commercial sale terms.

[Read the Playstack Pro License draft](/licenses/playstack-pro-draft.md).
This is the same draft source currently used for paid package licence files,
not a new or approved agreement.

The draft still requires legal review, a legal entity name, governing law and
approval before packages are sold under it. It also needs reconciliation with
the current intended renewal, retained-release access and seat policies. Do not
treat either the draft or this overview as confirmation that those commercial
terms have been finalized.

The [Pro catalog](/docs/pro) identifies assigned paid capabilities. Plus remains
planned, without an exclusive package allocation. The [pricing page](/pricing)
describes the intended model; public checkout is not enabled. Requesting preview
information or confirming an email does not create a paid licence or grant Pro
package access.

## Check the package and its dependencies

Use the licence shipped with your selected package version when reviewing its
terms. Third-party dependencies carry their own licences; this page does not
replace them. The [open-source acknowledgments](/withoss) identify projects used
by Playstack, and [package access guidance](/docs/packages#access-policy) explains
how tier classification differs from release availability and installation.

The full-text files linked above are generated from the same canonical sources
as the package licence files and checked for exact parity in CI. Publishing a
copy of the Pro draft does not approve it or activate checkout.

## Privacy and questions

The [Privacy Policy](/privacy) describes how the service handles information.
Package licensing is separate from that policy and from website Terms of Service;
this page is not a substitute for those service terms.

For licensing questions or to discuss preview access, contact
[hello@playstack.dev](mailto:hello@playstack.dev).

## Features

Source: https://playstack.dev/features.md

- [Authentication](/features/authentication): Compose credentials, magic links, sessions, passkeys, MFA, and connected-provider access without handing application identity to a framework.
- [Accounts and tenancy](/features/accounts-tenancy): Model organizations, memberships, invitations, ownership, active-account switching, and fail-closed data scope.
- [Email delivery](/features/email-delivery): Render typed application email and deliver it through explicit SES, Mailgun, Postmark, Resend, SendGrid, SMTP, or capture-test providers.
- [Events and workflows](/features/events-workflows): Carry typed domain events, operation context, transactional delivery, and replay-safe processing across application boundaries.
- [Feeds and syndication](/features/feeds-syndication): Publish RSS, Atom, JSON Feed, sitemaps, and related discovery surfaces from portable feed contracts.
- [Rate limiting](/features/rate-limiting): Apply fail-closed limits with trustworthy subjects and explicit Redis, Prisma, or Durable Object storage.
- [Analytics](/features/analytics): Collect consent-aware application events and deliver them through explicit browser and server boundaries.
- [Connections](/features/connections): Authorize, encrypt, refresh, and revoke third-party credentials without moving provider APIs or product workflows into a generic abstraction.
- [Devices and trusted sessions](/features/devices): Register application devices, manage push destinations, establish bounded trust, and revoke device-backed access from one explicit identity boundary.
- [Client authentication](/features/client-authentication): Authenticate browser extensions, CLIs, desktop apps, and mobile clients through system-browser authorization, PKCE, rotation, and device-aware revocation.
- [Device pairing](/features/device-pairing): Establish same-user peer trust across mobile, desktop, and extension surfaces through a versioned protocol with selectable discovery and transport profiles.
- [Browser extensions](/features/browser-extensions): Build Playstack-connected browser extensions with MV3-safe runtime state, typed messaging, portable storage, and system-browser authentication.
- [Billing and entitlements](/features/billing-entitlements): Synchronize subscription state, project purchased access into stable grants, and enforce capabilities without scattering plan-name checks through application code.
- [API keys and audit trails](/features/api-access-audit): Issue scoped machine credentials, authenticate them at the server boundary, and record security-relevant actions without turning logs into policy.
- [Commerce](/features/commerce): Compose server-authored products, one-time checkout, finite stock, immutable orders, and physical fulfillment without turning one provider into the domain model.
- [Notifications and delivery](/features/notifications-delivery): Define typed notifications, resolve recipient preferences, deliver across explicit channels, and retain one provider-independent history of every send.
- [Audiences and subscriber lifecycle](/features/audiences): Build waitlists, newsletters, release lists, and preference centers with explicit consent, segmentation, confirmation, and delivery boundaries.
- [Content and AI](/features/content-ai): Model portable content operations and compose AI-assisted generation through explicit providers, schemas, policy, and application-owned publication workflows.
- [Caching](/features/caching): Use namespaced portable caches with typed serialization, tag invalidation, and explicit stampede-protection capabilities across Redis, Workers, and local runtimes.
- [File storage](/features/file-storage): Store and retrieve application objects through explicit filesystem, S3, or R2 providers with validated keys, metadata, streaming bodies, and signed access.
- [Search](/features/search): Build product-specific search on portable query, ranking, transport, and framework contracts without coupling records to a provider.
- [Local-first](/features/local-first): Share durable local mutations and synchronization semantics across web, Expo, and desktop clients while keeping product policy explicit.
- [Webhooks](/features/webhooks): Verify provider requests from raw bytes, claim events transactionally, enqueue normalized work exactly once, and keep provider parsing outside domain handlers.
- [Error handling](/features/error-handling): Carry stable application errors across server and client boundaries, report normalized failures, and retain framework-native recovery without leaking sensitive context.
- [GitHub App access](/features/github-apps): Authenticate GitHub App installations and compose application-owned repository access workflows with scoped, short-lived tokens.
- [Realtime and WebRTC](/features/realtime-webrtc): Compose authenticated realtime channels, resumable client state, WebRTC session control, and ICE or relay infrastructure without conflating events, signalling, and media.
- [Licensing](/features/licensing): Issue signed licenses, activate device-bound seats, verify access offline, rotate refresh credentials, and revoke grants without coupling product policy to the token protocol.
- [Progressive web apps](/features/progressive-web-apps): Compose installability, application-shell caching, update coordination, and push registration without confusing a service worker with local-first application data.
- [Application interfaces](/features/application-ui): Compose application-owned React and Next surfaces from Chakra themes, controlled App UI and scoped account/audience operator sections.
- [Localization](/features/localization): Share locale policy and message catalogs across application UI, APIs and delivery without choosing a translation vendor.

## Authentication

Source: https://playstack.dev/features/authentication.md

Compose credentials, magic links, sessions, passkeys, MFA, and connected-provider access without handing application identity to a framework.

Preview composition; review its documented limitations before adopting.

## One identity boundary, several entry paths

Authentication is not one screen or one provider. Playstack keeps credential verification, session behavior, client state, request guards, device identity, and step-up proofs as explicit contracts that can be composed for the paths an application actually supports.

The portable service remains the authority. React, Next.js, NestJS, Auth.js, databases, and delivery providers connect at the application edge.

## Start with the method

Choose a sign-in or verification method first, then follow its compatible framework and service paths. Each method identifies the package set and infrastructure it needs so email delivery, persistence, browser state, and server enforcement stay visible.

| Method | Portable authority | Application edges |
| --- | --- | --- |
| Email and password | Credential verification, attempt limits, breach checks, and session issue | Form UI, request validation, cookies, persistence |
| Magic link | Single-use token lifecycle and session issue | Email template, Postmark or SMTP delivery, callback route |
| Auth.js provider sign-in | Normalized identity and linking policy | Provider configuration, callback route, provider secrets |
| Passkeys and MFA | Enrollment, challenge, recovery, and step-up contracts | WebAuthn UI, trusted-device policy, persistence |
| OAuth connections | Encrypted provider credentials after sign-in | Consent UI, provider scopes, API client creation |

OAuth connections are deliberately separate from provider sign-in. A GitHub connection used to call the GitHub API should not silently become the application's primary identity policy.

Security pages can list token-free session activity and revoke an individual
session. Refresh-token reuse revokes the compromised session family by default,
or every active user session when the application selects the stricter policy.
A bounded maintenance operation removes expired and retained consumed
credentials under an application scheduler.

Select cleanup kinds for the installed schema; optional features do not require
unused staff/OAuth tables. Refresh replay evidence remains retained through its
original absolute expiry. For protected writes racing deactivation, native
transaction-bound active-user validation locks the identity on the same physical
transaction; an ordinary user lookup is not that authority check. See the
[Auth adoption guide](/docs/packages/identity/auth#transaction-bound-active-identity-checks).

## Compose email and password end to end

The server authenticates the credential and issues the session. The browser receives only the safe session projection through the React transport.

~~~ts
import { createCredentialService, databaseSessions } from '@playstack/auth'

export const credentials = createCredentialService({
  persistence,
  crypto,
  passwordHasher,
  breachChecker,
  limiter,
  delivery,
  stepUp,
  events,
  clock,
  ids,
  dummyPasswordHash,
})

export const sessions = databaseSessions({
  persistence,
  crypto,
  events,
  clock,
  ids,
})

export async function signInWithPassword(input, request) {
  const user = await credentials.authenticatePassword(
    input.email,
    input.password,
    { ip: requestIp(request) },
  )
  const tokens = await sessions.issue(user.id, {
    deviceId: input.deviceId,
  })

  await writeApplicationSessionCookies(tokens)
  return resolveSafeSessionView(tokens.accessToken)
}
~~~

~~~tsx
import { useAuth } from '@playstack/auth-react'

export function SignInForm() {
  const auth = useAuth()

  return (
    <form onSubmit={(event) => {
      event.preventDefault()
      const data = new FormData(event.currentTarget)
      void auth.signIn({
        method: 'password',
        email: data.get('email'),
        password: data.get('password'),
      })
    }}>
      {/* Application-owned fields, errors, and recovery links. */}
    </form>
  )
}
~~~

`createFetchAuthTransport` sends that input to the application-owned sign-in endpoint. Playstack does not choose the URL handler, cookie names, or product UI.

## Swap the entry path, keep the session boundary

A magic-link request calls `credentials.requestToken('magicLink', email, { ip })`; the callback redeems it with `credentials.redeemMagicLink(token)` and then issues the same session shape. Auth.js provider callbacks can enter through [`@playstack/auth-authjs`](/integrations/authjs). Both paths can reuse the same React session provider, Next.js SSR handoff, device checks, account resolution, and server authorization.

For a separately hosted browser and API, `createBearerAuthTransport()` keeps the
refresh token in a secure API cookie and holds the returned opaque access token
only in memory. Same-origin deployments retain the host-only cookie transport.

## Security boundary

Client guards are navigation behavior, not authorization. Server operations validate sessions, CSRF where cookies are ambient, account scope, and any step-up requirement before acting.

## Explicit identity and replay policies

`providerIdentityPolicy: { linking: 'explicit', email: 'optional' }` supports email-less provider users without silently linking a matching email. Trusted server normalization and step-up-backed identity linking remain required. The compatible default is verified-email linking with required email. Safe user/session projections and registration-event email may be null; first-contact email enrollment remains an application-owned verified workflow.

New provider users emit the same transactional registration event as password signup. Required account/audit provisioning must join the actual transaction, not a post-login callback.

`refreshReplayPolicy: 'strict'` rejects every consumed-token replay without returning replacement material. The default bounded-grace policy retains encrypted replacement credentials for a bounded interval. `reusePolicy: 'all-user-sessions'` separately widens revocation scope. Strict mode rejects `refreshGraceMs` and requires shared client refresh coordination.

Authorization-code consumption and session issuance can commit together through required transaction-aware issuance/authorization seams with account context. A committed code cannot be replayed to recover lost credentials. Optional device authorization follows the same one-time boundary. See [Client authentication](/features/client-authentication).

## Approaches

### Email and password

Password-grade credentials, breach checks, attempt limits, verification, reset, and session rotation.

Implemented in source; check the exact release before adopting.

- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/auth-next](/docs/packages/identity/auth-next)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)

### Magic links

Single-use email credentials delivered through an application-owned mail boundary.

Implemented in source; check the exact release before adopting.

- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)
- [@playstack/mail](/docs/packages/infrastructure/mail)

### Sessions

Opaque access and rotating refresh tokens with inventory, revocation, reuse policy, browser, SSR, and request-guard adapters.

Implemented in source; check the exact release before adopting.

- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/auth-next](/docs/packages/identity/auth-next)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)
- [@playstack/devices](/docs/packages/identity/devices)

### Split-origin browser sessions

Keep refresh credentials in an API cookie and short-lived bearer access only in browser memory when web and API origins differ.

Implemented in source; check the exact release before adopting.

- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)

### Passkeys and MFA

Passkey-first step-up, TOTP, recovery codes, recent-auth proofs, and trusted-device decisions.

Implemented in source; check the exact release before adopting.

- [@playstack/mfa](/docs/packages/identity/mfa)
- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/auth](/docs/packages/identity/auth)

### Auth.js provider sign-in

Bridge Auth.js callbacks into Playstack identity and session contracts while the application owns provider configuration.

Preview composition; review its documented limitations before adopting.

- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/auth-authjs](/docs/packages/identity/auth-authjs)

### OAuth connections

Encrypted, refreshable credentials for third-party APIs after sign-in; this is distinct from using OAuth as the primary login method.

Implemented in source; check the exact release before adopting.

- [@playstack/connections](/docs/packages/identity/connections)
- [@playstack/auth](/docs/packages/identity/auth)

## Accounts and tenancy

Source: https://playstack.dev/features/accounts-tenancy.md

Model organizations, memberships, invitations, ownership, active-account switching, and fail-closed data scope.

Preview composition; review its documented limitations before adopting.

## Keep tenant context explicit

Accounts model the tenant a user is acting within and the membership that permits that scope. Framework adapters carry the validated result; they do not infer account access from request parameters.

## Compose from the request inward

Authenticate first, resolve account membership second, then apply roles and fail-closed persistence scope before executing product behavior.

## Compose the account lifecycle

The service exposes member and invitation lists, account name/slug updates,
self-service leaving, and account-scoped session switching. Ownership changes
use a delivered single-use proposal rather than an immediate administrative
mutation, keeping the owner pointer and both membership roles in one
transaction.

Signup can register `userRegistrationHandler()` on auth’s transactional
registration event to create the initial account and owner membership before
the user commits. Recoverable deletion hides an account immediately, supports
restoration during the configured grace period, and leaves scheduling plus
cross-database erasure to the application.

## Restrict service policy without replacing the core

`administration: 'owners-only'` restricts membership administration; the default remains owners-and-admins. `mutationPolicy.assertAllowed(input, transaction)` can enforce product-owned personal-account rules before writes. Hooks restrict built-in permissions rather than grant extra authority, and must use the supplied transaction.

`slugPolicy` supports normalization and reserved-slug decisions. `createAccountPolicy()` provides named permissions for the Nest guard; service policy and resource authorization remain separate. Single-owner behavior is still the shipped model: hooks do not implement multi-owner accounts or automatic personal-to-team conversion.

An optional Nest `resolveRequestAccount` strategy supports user-wide sessions with current membership checks. A session already scoped to an account cannot select another; ordinary account switching retains its session-rotation behavior.

## Approaches

### Memberships and roles

Resolve active membership and application-defined roles for every account-scoped operation.

Implemented in source; check the exact release before adopting.

- [@playstack/accounts](/docs/packages/identity/accounts)
- [@playstack/nest-accounts](/docs/packages/identity/nest-accounts)

### Invitations

Issue expiring invitation tokens through an application-owned delivery bridge.

Implemented in source; check the exact release before adopting.

- [@playstack/accounts](/docs/packages/identity/accounts)
- [@playstack/mail](/docs/packages/infrastructure/mail)

### Active-account switching

Reissue account-scoped sessions and synchronize the selected account in application UI.

Implemented in source; check the exact release before adopting.

- [@playstack/accounts](/docs/packages/identity/accounts)
- [@playstack/accounts-react](/docs/packages/identity/accounts-react)
- [@playstack/auth](/docs/packages/identity/auth)

### Ownership transfer

Propose, deliver, accept, or cancel a single-use ownership transfer while preserving the one-owner invariant.

Implemented in source; check the exact release before adopting.

- [@playstack/accounts](/docs/packages/identity/accounts)
- [@playstack/mail](/docs/packages/infrastructure/mail)

### Recoverable deletion

Hide an account immediately, permit restoration during grace, then run bounded application cascades before purge.

Implemented in source; check the exact release before adopting.

- [@playstack/accounts](/docs/packages/identity/accounts)

## Email delivery

Source: https://playstack.dev/features/email-delivery.md

Render typed application email and deliver it through explicit SES, Mailgun, Postmark, Resend, SendGrid, SMTP, or capture-test providers.

Implemented in source; check the exact release before adopting.

## Rendering belongs to the application

Playstack validates envelopes and separates rendering failure from provider failure. Chakra Email or another renderer can implement the typed template without becoming a dependency of the mail contract.

## Delivery remains swappable

Choose SES, Mailgun, Postmark, Resend, or SendGrid for rendered mail and provider templates; SMTP for portable rendered delivery; or the capture provider for deterministic tests. Every provider exposes its exact native client when the portable surface is deliberately smaller than the provider.

## Approaches

### Rendered application email

Produce typed subject, HTML, and text before provider I/O.

Implemented in source; check the exact release before adopting.

- [@playstack/mail](/docs/packages/infrastructure/mail)

### Provider templates

Use provider-owned templates only when the selected driver declares that capability.

Implemented in source; check the exact release before adopting.

- [@playstack/mail](/docs/packages/infrastructure/mail)

## Events and workflows

Source: https://playstack.dev/features/events-workflows.md

Carry typed domain events, operation context, transactional delivery, and replay-safe processing across application boundaries.

Preview composition; review its documented limitations before adopting.

## Events connect capabilities

Typed events let packages remain focused while applications decide what must happen synchronously, transactionally, or after commit.

## Context survives the boundary

Operation context makes causation and observability explicit rather than rebuilding it independently in every transport.

## Queue work behind the same boundaries

`@playstack/queues` carries typed jobs, retry policy, terminal-failure hooks, and trace context into background execution. The Inngest adapter can serve the same job definitions from Next.js or NestJS while leaving provider-specific workflows accessible through the native client.

The [Cloudflare Queues adapter](/integrations/cloudflare-queues) now handles Worker producer/batch boundaries. It acknowledges successes individually and requires durable terminal-failure handling before acknowledging poison messages. It does not implement BullMQ's progress, scheduling or administration surfaces.

## Approaches

### Domain events

Emit typed facts with correlation, causation, actor, scope, and trace context.

Implemented in source; check the exact release before adopting.

- [@playstack/events](/docs/packages/events-operations/events)
- [@playstack/nest-events](/docs/packages/events-operations/nest-events)

### Event storage and replay

Persist ordered streams and expose replay through explicit storage and framework adapters.

Implemented in source; check the exact release before adopting.

- [@playstack/event-store](/docs/packages/events-operations/event-store)
- [@playstack/nest-event-store](/docs/packages/events-operations/nest-event-store)
- [@playstack/events-prisma](/docs/packages/events-operations/events-prisma)

### Background jobs and workflows

Dispatch validated, retry-aware jobs through a portable queue contract and serve them from the application edge.

Implemented in source; check the exact release before adopting.

- [@playstack/queues](/docs/packages/infrastructure/queues)

## Feeds and syndication

Source: https://playstack.dev/features/feeds-syndication.md

Publish RSS, Atom, JSON Feed, sitemaps, and related discovery surfaces from portable feed contracts.

Preview composition; review its documented limitations before adopting.

## Publish from one content model

Portable feed contracts keep entry selection and canonical URLs in application code while adapters handle framework responses, streaming, and regeneration triggers.

## Approaches

### RSS, Atom, and JSON Feed

Generate standards-based feeds from application-owned entries.

Implemented in source; check the exact release before adopting.

- [@playstack/feeds](/docs/packages/content-intelligence/feeds)
- [@playstack/feeds-next](/docs/packages/content-intelligence/feeds-next)
- [@playstack/nest-feeds](/docs/packages/content-intelligence/nest-feeds)

### Sitemaps and discovery

Expose application content through sitemap and discovery endpoints.

Implemented in source; check the exact release before adopting.

- [@playstack/feeds](/docs/packages/content-intelligence/feeds)
- [@playstack/feeds-next](/docs/packages/content-intelligence/feeds-next)

## Rate limiting

Source: https://playstack.dev/features/rate-limiting.md

Apply fail-closed limits with trustworthy subjects and explicit Redis, Prisma, or Durable Object storage.

Preview composition; review its documented limitations before adopting.

## Policy stays portable

Subjects, windows, costs, and decisions belong to the application-facing contract. Storage adapters implement atomic counters for the runtime already in use.

## Approaches

### Request limits

Enforce policy after trustworthy identity and proxy context have been established.

Implemented in source; check the exact release before adopting.

- [@playstack/rate-limit](/docs/packages/events-operations/rate-limit)
- [@playstack/nest-rate-limit](/docs/packages/events-operations/nest-rate-limit)

### Distributed counters

Choose Redis, Prisma, or Durable Objects without changing application policy.

Implemented in source; check the exact release before adopting.

- [@playstack/rate-limit-redis](/docs/packages/events-operations/rate-limit-redis)
- [@playstack/rate-limit-prisma](/docs/packages/events-operations/rate-limit-prisma)
- [@playstack/rate-limit-do](/docs/packages/events-operations/rate-limit-do)

## Analytics

Source: https://playstack.dev/features/analytics.md

Collect consent-aware application events and deliver them through explicit browser and server boundaries.

Preview composition; review its documented limitations before adopting.

## Treat analytics as application behavior

Consent, event shape, and delivery failure policy remain visible instead of being hidden behind a global vendor snippet.

## Approaches

### Browser analytics

Compose consent and client event capture through a headless React adapter.

Implemented in source; check the exact release before adopting.

- [@playstack/analytics](/docs/packages/events-operations/analytics)
- [@playstack/analytics-react](/docs/packages/events-operations/analytics-react)

### Server delivery

Deliver trusted application events through explicit NestJS lifecycle bindings.

Implemented in source; check the exact release before adopting.

- [@playstack/analytics](/docs/packages/events-operations/analytics)
- [@playstack/nest-analytics](/docs/packages/events-operations/nest-analytics)

## Connections

Source: https://playstack.dev/features/connections.md

Authorize, encrypt, refresh, and revoke third-party credentials without moving provider APIs or product workflows into a generic abstraction.

Implemented in source; check the exact release before adopting.

## Own credential lifecycle once

`@playstack/connections` provides purpose-bound OAuth state, PKCE, encrypted credential persistence, refresh serialization, and revocation. The core returns a valid access token while the application calls provider SDKs and owns product actions such as publishing, importing, or repository management.

```ts
const connections = createConnections({
  persistence,
  crypto,
  events,
  clock,
  ids,
  providers: [github, mastodon, x],
  scope: 'account',
  redirectUri: 'https://app.example/api/connections/callback',
  returnUrlOrigins: ['https://app.example'],
})
```

GitHub, LinkedIn, Mastodon, and X have named providers. A standards-based OAuth 2 factory covers conventional authorization-code providers while preserving explicit identity lookup and native driver access.

## Respect unusual protocols

AT Protocol is not flattened into a bearer-token preset. `@playstack/atproto` wraps the official OAuth client so discovery, PAR, PKCE, DPoP keys and nonces, rotating refresh tokens, and shared session storage keep their native semantics.

## Select persistence ownership

Playstack publishes user, account, and account-member Prisma variants. Mature applications can instead implement the portable persistence contract against an existing schema and keep identifier or table mappings application-owned.

## Approaches

### OAuth connections

Run purpose-bound PKCE authorization and keep encrypted rotating credentials in user or account scope.

Implemented in source; check the exact release before adopting.

- [@playstack/connections](/docs/packages/identity/connections)

### Named providers

Use GitHub, LinkedIn, Mastodon, and X drivers or the standards-based OAuth 2 factory.

Implemented in source; check the exact release before adopting.

- [@playstack/connections](/docs/packages/identity/connections)

### AT Protocol sessions

Preserve official AT Protocol discovery, PAR, PKCE, DPoP, nonce, and session-store behavior.

Implemented in source; check the exact release before adopting.

- [@playstack/atproto](/docs/packages/identity/atproto)

## Devices and trusted sessions

Source: https://playstack.dev/features/devices.md

Register application devices, manage push destinations, establish bounded trust, and revoke device-backed access from one explicit identity boundary.

Implemented in source; check the exact release before adopting.

## Registered devices are application clients

A registered device represents an installation signed into an application. It gives authentication, MFA, and notifications a shared device identity without pulling peer discovery or transport into those packages. Device-to-device trust is a separate [pairing capability](/features/device-pairing).

## Register and resolve from the server

The application owns persistence and request identity. Playstack owns lifecycle rules, credential protection, and the events other capabilities consume.

~~~ts
import { createDevices } from '@playstack/devices'

export const devices = createDevices({
  persistence,
  cipher,
  events,
  clock,
})

const device = await devices.registerDevice({
  userId: session.userId,
  appId: 'dashboard',
  installationId: input.installationId,
  platform: input.platform,
  name: input.name,
})

await devices.registerPushToken({
  userId: session.userId,
  deviceId: device.id,
  provider: 'apns',
  token: input.pushToken,
})
~~~

Notification workers ask the device service for current targets at send time. A token invalidated after enqueue therefore cannot receive a later push.

## Revoke the boundary, not one symptom

Revocation removes the active device, invalidates its push destinations, and emits the event authentication uses to revoke device-backed sessions. Trust has an absolute expiration and never becomes permanent merely because a device remains active.

## Approaches

### Device registration and inventory

Attach an installation to a user and application without conflating account clients with peer-to-peer pairing.

Implemented in source; check the exact release before adopting.

- [@playstack/devices](/docs/packages/identity/devices)

### Push destinations

Rotate encrypted push credentials and resolve valid delivery targets without exposing provider tokens to callers.

Implemented in source; check the exact release before adopting.

- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/notifications](/docs/packages/communication/notifications)

### Trusted-device sessions

Grant explicitly bounded device trust for MFA policy and revoke the device, its sessions, and push tokens atomically.

Implemented in source; check the exact release before adopting.

- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/mfa](/docs/packages/identity/mfa)

## Client authentication

Source: https://playstack.dev/features/client-authentication.md

Authenticate browser extensions, CLIs, desktop apps, and mobile clients through system-browser authorization, PKCE, rotation, and device-aware revocation.

Preview composition; review its documented limitations before adopting.

## Public clients cannot keep a secret

Extension bundles, desktop binaries, mobile applications, and installed CLIs are inspectable. Client authentication therefore uses authorization code with PKCE rather than pretending a bundled client secret is confidential.

The client opens the product's own authorization page in the system browser. Existing sessions, passkeys, MFA, SSO, and consent remain web application responsibilities.

## One contract, client-specific launchers

~~~ts
import {
  createClientAuth,
  createFetchTokenTransport,
} from '@playstack/client-auth'

const auth = createClientAuth({
  clientId: 'desktop-app',
  authorizationEndpoint: 'https://app.example.com/oauth/authorize',
  redirectUri,
  scopes: ['profile', 'devices'],
  transport: createFetchTokenTransport({
    tokenEndpoint: 'https://api.example.com/oauth/token',
    revocationEndpoint: 'https://api.example.com/oauth/revoke',
  }),
  storage: platformCredentialStorage,
  launcher: systemBrowserLauncher,
})

await auth.authorize()
const response = await auth.request(request, apiTransport)
await auth.signOut()
~~~

Browser extensions replace the launcher and callback with `launchWebAuthFlow`; mobile uses the platform authentication session; a CLI can fall back to the device authorization flow when a loopback callback is unavailable.

## Authentication is not pairing

Client authentication establishes a session between one client and the application API. [Device pairing](/features/device-pairing) establishes mutual trust between two peers. A desktop companion and extension may need both, and the contracts compose without sharing secrets or pretending they solve the same problem.

## Package boundary

The authorization-server additions belong to `@playstack/auth` and `@playstack/nest-auth`. `@playstack/client-auth` owns launch, callback, exchange, storage, refresh, authenticated-request retry, and revocation on the installed client. Its Node-only loopback entrypoint binds an ephemeral `127.0.0.1` callback, accepts one response, and closes. Device authorization now ships through `@playstack/auth/device`, `@playstack/client-auth/device` and Nest integration. Successful exchange is one-time: a lost committed token response requires a new authorization flow, not recovery of the same secret.

## Approaches

### Authorization code with PKCE

Reuse the product's web authentication, MFA, SSO, and consent screens without embedding credentials in a public client.

Implemented in source; check the exact release before adopting.

- [@playstack/client-auth](/docs/packages/identity/client-auth)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)

### Native and extension callbacks

Complete authorization through extension redirects, mobile sessions, or verified loopback listeners with explicit state handling.

Implemented in source; check the exact release before adopting.

- [@playstack/client-auth](/docs/packages/identity/client-auth)

### Device authorization flow

S256-bound device grants, explicit consent, bounded polling and transactional one-time issuance.

Preview composition; review its documented limitations before adopting.

- [@playstack/client-auth](/docs/packages/identity/client-auth)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)

### Token rotation and revocation

Store credentials in platform-appropriate boundaries, rotate refresh families, and connect every client session to a revocable device.

Implemented in source; check the exact release before adopting.

- [@playstack/client-auth](/docs/packages/identity/client-auth)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/devices](/docs/packages/identity/devices)

## Device pairing

Source: https://playstack.dev/features/device-pairing.md

Establish same-user peer trust across mobile, desktop, and extension surfaces through a versioned protocol with selectable discovery and transport profiles.

Planned; not an available capability.

## Pair peers, not accounts

A registered device is a client authenticated to an application. A paired device has established mutual trust with another device. Both peers may belong to the same user, but signing in does not automatically authorize direct device-to-device communication.

## Separate trust, discovery, and transport

The protocol fixes trust establishment and identifies compatible discovery and transport profiles. A QR code, mDNS record, or account lookup helps locate a peer; none of them proves its identity. The authenticated handshake does that.

~~~ts
import { createPairing } from '@playstack/pairing'

const pairing = createPairing({
  identity: platformIdentityKey,
  devices,
  protocolVersion: 1,
  discovery: mdnsDiscovery(),
  transport: lanWithRelayFallback({ relay }),
})

const request = await pairing.begin({ peerDeviceId })
await pairing.confirm(request.id, displayedShortCode)
~~~

## Cross-language by construction

Desktop, mobile, and companion products span TypeScript, Swift, and Rust. The normative artifact is therefore the versioned wire protocol and its shared conformance vectors. `@playstack/pairing` is one implementation, not the definition every other language must reverse engineer.

## Planned implementation

The protocol, discovery profiles, transport profiles, and deterministic vectors are specified. The feature remains planned until the TypeScript, Swift, and Rust implementations agree on those vectors and at least one real product integration validates revocation and relay fallback.

## Approaches

### Peer trust establishment

Use versioned Noise handshakes, explicit human confirmation, and application device identity to establish revocable peer trust.

Planned; not an available capability.

- @playstack/pairing
- [@playstack/devices](/docs/packages/identity/devices)

### Discovery profiles

Locate peers through account lookup, mDNS, QR codes, native host manifests, or port files without treating discovery as proof.

Planned; not an available capability.

- @playstack/pairing

### Transport profiles

Carry encrypted application payloads over LAN sockets, native messaging, localhost WebSockets, ICE, or a server relay.

Planned; not an available capability.

- @playstack/pairing

## Browser extensions

Source: https://playstack.dev/features/browser-extensions.md

Build Playstack-connected browser extensions with MV3-safe runtime state, typed messaging, portable storage, and system-browser authentication.

Preview composition; review its documented limitations before adopting.

## Build with the platform lifecycle

Manifest V3 service workers stop after inactivity. Any extension that keeps tokens, cached state, or initialization flags only in module memory will eventually fail outside development. Playstack makes reconstructible state and top-level event registration the default.

~~~ts
import {
  createExtensionTokenStorage,
  createWebLockRefreshCoordinator,
  defineMessages,
  registerMessageHandlers,
} from '@playstack/extension'

// Access tokens live in storage.session, refresh tokens in storage.local;
// nothing durable lives in service-worker module memory.
export const tokens = createExtensionTokenStorage({ storage: browser.storage })
export const refreshes = createWebLockRefreshCoordinator({ locks: navigator.locks })

export const messages = defineMessages({
  'session.read': { request: sessionReadRequest, response: sessionReadResponse },
})

// Registered at the top level, so a restarted worker answers again.
registerMessageHandlers(browser.runtime, messages, {
  'session.read': async () => readSession(tokens),
})
~~~

## Let WXT own the build

The package is a runtime layer, not another extension framework. WXT owns entrypoints, manifests, cross-browser output, and bundling. Playstack owns its application-facing authentication profile, typed messaging, storage conventions, API client, and React bindings.

## Reuse application authentication

An extension opens the product authorization page with `browser.identity.launchWebAuthFlow`. The web application performs its normal login, MFA, passkey, or SSO policy and redirects back with a one-time code. The extension never renders or receives the user's password.

## Implemented boundary

The extension and shared client-auth packages now provide authorization launch,
split session/local credential storage, cross-context refresh coordination,
validated messages and storage, optional permission requests, React session
state, and MV3 interruption tests. The feature remains preview-level while it
is exercised against a production WXT extension; WXT continues to own project
generation and build output.

## Approaches

### Extension authentication

Authenticate through the application's web flow and exchange a short-lived PKCE code without exposing passwords to extension UI.

Implemented in source; check the exact release before adopting.

- [@playstack/extension](/docs/packages/identity/extension)
- [@playstack/client-auth](/docs/packages/identity/client-auth)

### MV3 runtime lifecycle

Reconstruct service-worker state on wake and use alarms, storage, and retryable clients instead of process-lifetime assumptions.

Implemented in source; check the exact release before adopting.

- [@playstack/extension](/docs/packages/identity/extension)

### Typed messaging and storage

Connect popup, options, content-script, and worker contexts through validated messages and explicit local or synchronized storage.

Implemented in source; check the exact release before adopting.

- [@playstack/extension](/docs/packages/identity/extension)
- [@playstack/extension-react](/docs/packages/identity/extension-react)

### Extension policy and linting

Detect unsafe permissions, remote code, placement mistakes, and service-worker lifetime assumptions before review.

Implemented in source; check the exact release before adopting.

- [@playstack/extension](/docs/packages/identity/extension)
- [@playstack/lint](/docs/packages/foundation/lint)

## Billing and entitlements

Source: https://playstack.dev/features/billing-entitlements.md

Synchronize subscription state, project purchased access into stable grants, and enforce capabilities without scattering plan-name checks through application code.

Implemented in source; check the exact release before adopting.

## Purchases are not authorization checks

Billing answers what an account bought. Entitlements answer what a subject may use now. Keeping that boundary explicit means application routes ask for `packages.pro` or `projects.maximum`, not whether a subscription happens to be named `pro`.

## Define stable grants

~~~ts
import { defineEntitlements } from '@playstack/entitlements'

export const entitlements = defineEntitlements({
  'packages.plus': { kind: 'boolean' },
  'packages.pro': { kind: 'boolean' },
  'projects.maximum': { kind: 'number' },
})
~~~

The billing projection can replace only the grants sourced from one subscription. Manual access, registration benefits, and future licence grants remain isolated and survive an unrelated billing update.

## Treat provider state as authoritative

Checkout redirects do not grant access. The billing service consumes verified provider events, projects their latest object state idempotently, and then replaces the matching entitlement source.

~~~ts
await billing.consumeWebhook(verifiedEvent)

const access = await entitlementService.resolve({
  type: 'account',
  id: request.account.id,
})

access.require('packages.pro')
~~~

Reconciliation uses the same path, so replaying current subscription state should be a no-op rather than a second implementation.

Each provider event carries its immutable ID and occurrence time. Exact retries
and stale deliveries cannot overwrite newer state, while accepted projections
append revision history. Invoice payment, payment failure, cancellation,
resumption, scheduled plan changes, and trial-ending events are normalized for
notification and audit consumers.

Billing requires an explicit entitlement consistency mode. A shared database
can project grants on the billing transaction; separate stores use durable,
idempotent eventual projection. `resolveMany()` and `snapshot()` support account
settings and administration views without one database read per entitlement.

## Approaches

### Subscription lifecycle

Keep a rebuildable local projection of provider-owned subscription state through idempotent webhooks and reconciliation.

Implemented in source; check the exact release before adopting.

- [@playstack/billing](/docs/packages/identity/billing)
- [@playstack/nest-billing](/docs/packages/identity/nest-billing)

### Entitlement projection

Translate billing, registration, licence, or staff sources into independently replaceable boolean and numeric grants.

Implemented in source; check the exact release before adopting.

- [@playstack/billing](/docs/packages/identity/billing)
- [@playstack/entitlements](/docs/packages/identity/entitlements)

### Request enforcement

Resolve user or account grants behind NestJS guards while keeping authentication and entitlement policy separate.

Implemented in source; check the exact release before adopting.

- [@playstack/entitlements](/docs/packages/identity/entitlements)
- [@playstack/nest-entitlements](/docs/packages/identity/nest-entitlements)

### No-card product trials

Project full trial access and a smaller read-only grace set with immutable per-account eligibility.

Implemented in source; check the exact release before adopting.

- [@playstack/billing](/docs/packages/identity/billing)
- [@playstack/entitlements](/docs/packages/identity/entitlements)

### Billing operations

Inspect current state and revision history, surface scheduled changes, and reconcile from provider truth.

Implemented in source; check the exact release before adopting.

- [@playstack/billing](/docs/packages/identity/billing)
- [@playstack/nest-billing](/docs/packages/identity/nest-billing)

## API keys and audit trails

Source: https://playstack.dev/features/api-access-audit.md

Issue scoped machine credentials, authenticate them at the server boundary, and record security-relevant actions without turning logs into policy.

Implemented in source; check the exact release before adopting.

## Human sessions and machine credentials share policy, not secrets

API keys identify integrations, automation, and service clients. They do not masquerade as user sessions. Once authenticated, both can enter the same account, entitlement, rate-limit, and audit boundaries through a normalized request subject.

## Issue once, verify repeatedly

~~~ts
import { createApiKeys } from '@playstack/api-keys'

const apiKeys = createApiKeys({ persistence, crypto, clock, ids, events })

const issued = await apiKeys.issue({
  subject: { type: 'account', id: account.id },
  name: 'Deployment automation',
  scopes: ['releases:read', 'releases:write'],
})

// `issued.secret` is shown once; persistence receives only its verifier.
~~~

The Nest binding authenticates the key, resolves its canonical subject and scopes, and places that result into request context. Application handlers still enforce the capability they require.

## Audit facts, not diagnostic prose

~~~ts
await audit.record({
  actor: request.actor,
  action: 'release.published',
  target: { type: 'release', id: release.id },
  accountId: request.account.id,
  context: request.operation,
})
~~~

Audit records are durable product facts with explicit retention and access policy. Operational logs remain a separate stream and should not become the only account of who changed access. Actor types include users, API keys, staff, and system activity while remaining extensible for application principals such as partners or services.

## Approaches

### API key lifecycle

Issue once-visible secrets, store only verifiers, rotate credentials, and revoke keys without changing application identity.

Implemented in source; check the exact release before adopting.

- [@playstack/api-keys](/docs/packages/identity/api-keys)

### Scoped machine authentication

Resolve key subjects and scopes into NestJS request context before account and entitlement enforcement.

Implemented in source; check the exact release before adopting.

- [@playstack/api-keys](/docs/packages/identity/api-keys)
- [@playstack/nest-api-keys](/docs/packages/identity/nest-api-keys)

### Structured audit recording

Persist actor, action, target, scope, and request context for security and support workflows.

Implemented in source; check the exact release before adopting.

- [@playstack/audit](/docs/packages/identity/audit)
- [@playstack/nest-audit](/docs/packages/identity/nest-audit)

## Commerce

Source: https://playstack.dev/features/commerce.md

Compose server-authored products, one-time checkout, finite stock, immutable orders, and physical fulfillment without turning one provider into the domain model.

Implemented in source; check the exact release before adopting.

## Keep commercial responsibilities separate

Catalog owns the terms offered for sale. Payments owns provider projections. Inventory owns stock promises. Commerce owns immutable order snapshots and coordinates their transaction boundaries. Fulfillment owns what happens to paid physical lines next.

~~~ts
const commerce = createCommerce({
  persistence,
  catalog,
  payments,
  inventory,
  entitlements,
  fulfillment,
  events,
  ids,
  orderNumbers,
  clock,
  allowedRedirectOrigins: ['https://shop.example.com'],
})

const result = await commerce.createCheckout({
  buyer: { accountId, userId, email },
  items: [{ offerId: 'controller-stand-black', quantity: 1 }],
  paymentProvider: { provider: 'stripe', instance: 'store' },
  successUrl,
  cancelUrl,
  idempotencyKey,
})
~~~

The browser submits offer identities and quantities, never authoritative prices. Order creation and stock reservation can share one application transaction. Hosted checkout starts only after that commit.

## Confirm only verified provider facts

Success redirects improve the buyer experience but never confirm an order. Verified, queued webhook projections flow through `@playstack/payments` before `@playstack/commerce` advances payment state. Amount drift quarantines the order for reconciliation.

~~~ts
await payments.handleWebhook(verifiedPaymentEvent)
await commerce.applyPaymentProjection(paymentProjection)
~~~

Provider and configured instance are both persisted identities, so one application can safely use multiple Stripe accounts or mix future providers.

## Fulfill through application-owned edges

`@playstack/fulfillment` encrypts address material behind an application codec. Its manual driver supports real staff workflows, while the Nest worker boundary can run through BullMQ, Inngest, or another queue. Subscriptions remain a separate [`Billing and entitlements`](/features/billing-entitlements) path and can coexist with one-time purchases.

## Approaches

### Catalog and quotes

Resolve immutable server-authored offers against an explicit payment provider and configured merchant instance.

Implemented in source; check the exact release before adopting.

- [@playstack/catalog](/docs/packages/commerce/catalog)
- [@playstack/nest-catalog](/docs/packages/commerce/nest-catalog)

### One-time checkout

Persist an order and optional stock reservation before creating hosted provider checkout with repeat-safe commands.

Implemented in source; check the exact release before adopting.

- [@playstack/commerce](/docs/packages/commerce/commerce)
- [@playstack/payments](/docs/packages/commerce/payments)
- [@playstack/inventory](/docs/packages/commerce/inventory)
- [@playstack/nest-commerce](/docs/packages/commerce/nest-commerce)

### Refunds and reconciliation

Project verified payment, refund, and dispute events while keeping browser redirects outside payment authority.

Implemented in source; check the exact release before adopting.

- [@playstack/payments](/docs/packages/commerce/payments)
- [@playstack/nest-payments](/docs/packages/commerce/nest-payments)
- [@playstack/webhooks](/docs/packages/communication/webhooks)

### Physical fulfillment

Create encrypted fulfillment requests and run manual or provider-backed delivery work independently of payment state.

Implemented in source; check the exact release before adopting.

- [@playstack/fulfillment](/docs/packages/commerce/fulfillment)
- [@playstack/nest-fulfillment](/docs/packages/commerce/nest-fulfillment)

## Notifications and delivery

Source: https://playstack.dev/features/notifications-delivery.md

Define typed notifications, resolve recipient preferences, deliver across explicit channels, and retain one provider-independent history of every send.

Implemented in source; check the exact release before adopting.

## Decide what to say before choosing a provider

A notification definition owns validated input, category, eligible channels, and rendering. Recipient preference narrows those channels. The delivery layer then applies provider-independent suppression and dispatch policy before any send leaves the application.

~~~ts
import { defineNotifications } from '@playstack/notifications'

export const notifications = defineNotifications({
  'account.invitation.accepted': {
    input: invitationAcceptedSchema,
    category: 'transactional',
    channels: ['mail', 'inApp'],
    digestible: true,
    render: {
      mail: ({ input }) => renderInvitationAcceptedEmail(input),
      inApp: ({ input }) => ({
        title: 'Invitation accepted',
        body: `${input.memberName} joined ${input.accountName}.`,
        href: `/accounts/${input.accountId}/members`,
      }),
    },
  },
})
~~~

## Resolve twice

Preferences are checked when a notification is queued and again when its worker runs. An opt-out made between those moments must win. Transactional status may bypass a marketing preference, but it never bypasses invalid destinations, abuse policy, or an explicit stop-all suppression.

~~~ts
await notificationService.enqueue(
  'account.invitation.accepted',
  input,
  { type: 'user', id: member.id },
  operationContext,
)
~~~

One immutable job is created per recipient and channel. Caller-keyed admission
and the native PostgreSQL queue can retain independent replay receipts after job
deletion. Stable producer keys and downstream durable handoff remain required;
ordinary unkeyed enqueue is a new intent. The operation trace follows the job into
delivery feedback.

## Commit the handoff before sending

The [native queue](/docs/packages/communication/notifications#native-queue-and-retained-replay-receipts)
joins Delivery intent, protected content and outbox writes on the consumer's
physical transaction. It never calls a provider inside that transaction. A later
Delivery worker needs a newly committed begin and current authority/suppression.
Confirmed temporary rejection can use a provider-specific retry classifier;
timeouts, malformed receipts and uncertain acknowledgments remain quarantined.
Confirmed permanent rejection records a distinct terminal outcome, without retry
or implicit suppression. Unknown outcomes must never be relabeled as refusal.
Retained replay receipts prevent duplicate handoff, not exactly-once remote effects
or automatic cross-store erasure.

## Prepare without unused infrastructure

`createNotificationPreparer` exposes only `prepareInTransaction`, using the native
registry, transaction-aware preference/locale/recipient resolvers, clock, IDs and
optional safe link origins. It needs no dummy root resolvers, queue, Delivery,
inbox or event ports. Native validation, bounded immutable snapshots and original
transaction-context lifetime checks still apply.

Preparation makes no provider calls. The host retains source/identity locking,
templates, current suppression and atomic protected-content/Delivery/outbox or
inbox writes on that same handle. See the [transactional preparation guide](/docs/packages/communication/notifications#prepare-on-the-callers-transaction)
before replacing application orchestration.

## Keep a single send history

Mail providers, push services, the in-app inbox, SMS, and outbound webhooks all report different events. Delivery history normalizes those outcomes around the send, masks or encrypts destinations according to application policy, and gives support one place to answer what happened.

## Data-only push does not require an inbox

Delivery exposes Expo, APNs, FCM and Web Push seams; Devices manages encrypted targets. Applications can send bounded sync hints without installing the notifications inbox. Recipient/enrollment checks, origin-device exclusion, coalescing, retry scheduling and permanent-token invalidation remain explicit application policy.

Provider acceptance is not proof of device execution. Keep content and credentials out of payloads and qualify each provider's response lifecycle and native background behavior.

## Approaches

### Typed notification definitions

Validate input, declare eligible channels, and render mail or in-app content from one application-owned registry.

Implemented in source; check the exact release before adopting.

- [@playstack/notifications](/docs/packages/communication/notifications)
- [@playstack/nest-notifications](/docs/packages/communication/nest-notifications)

### In-app inbox

Persist recipient-scoped notifications and expose headless listing, read, archive, and retention behavior to React clients.

Implemented in source; check the exact release before adopting.

- [@playstack/notifications](/docs/packages/communication/notifications)
- [@playstack/notifications-react](/docs/packages/communication/notifications-react)
- [@playstack/nest-notifications](/docs/packages/communication/nest-notifications)

### Multi-channel delivery

Route resolved sends through independent mail, push, in-app, SMS, or webhook slots behind one suppression gate.

Implemented in source; check the exact release before adopting.

- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/nest-delivery](/docs/packages/communication/nest-delivery)
- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/mail](/docs/packages/infrastructure/mail)
- [@playstack/queues](/docs/packages/infrastructure/queues)

### Delivery history and feedback

Correlate provider acceptance, delivery, bounce, complaint, and suppression outcomes without storing message bodies.

Implemented in source; check the exact release before adopting.

- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/delivery-history](/docs/packages/communication/delivery-history)

## Audiences and subscriber lifecycle

Source: https://playstack.dev/features/audiences.md

Build waitlists, newsletters, release lists, and preference centers with explicit consent, segmentation, confirmation, and delivery boundaries.

Implemented in source; check the exact release before adopting.

## Name the audience, not one output format

A launch waitlist, changelog list, customer newsletter, and product announcement audience share the same subscription lifecycle. The application decides what each list means; Playstack supplies portable membership, consent, token, preference, and delivery seams.

## Compose a public subscription flow

This composition example assumes server-owned native persistence, token issuance,
normalization, abuse and suppression adapters. `confirmationDelivery` persists an
encrypted confirmation intent in the subscription transaction; it does not send
mail inline. Create separate lists/topics for early access and newsletters.

~~~ts
import { createAudiences } from '@playstack/audiences'

export const audiences = createAudiences({
  persistence,
  tokens,
  normalizer,
  abuse,
  suppression: delivery,
  segments,
  events,
  clock,
  ids,
  confirmationDelivery,
})

await audiences.createList({
  key: 'launch-list',
  name: 'Early access',
  topic: 'early-access',
  authority: 'local',
  doubleOptIn: true,
})

await audiences.subscribe('launch-list', {
  email: input.email,
  source: 'homepage',
})
~~~

The public Nest controller returns a uniform accepted response, never a token or
subscriber identifier. Recover the encrypted intent through the native
[confirmation producer](/docs/packages/communication/audiences#durable-confirmation-mail)
and durable notification queue. Ordinary jobs/events carry opaque references,
not confirmation proofs, rendered mail or email addresses.

## Keep local and remote authority honest

A local audience updates inside the application's transaction. A provider-owned audience is eventually consistent through an outbox command and remains pending until the provider confirms it. The package never pretends an external API participated in the database transaction.

## Deliver through the shared gate

Audience expansion resolves subscription edges into recipient references, then hands sends to the delivery layer. Suppressions, bounces, complaints, and provider history stay shared with transactional messages without allowing a marketing send to reuse transactional reputation accidentally.

## Keep operator actions separate from consent

Native early-access invitation batches support preview, commit, verified-recipient
acceptance and durable proof delivery; confirming a subscription alone grants no
account or product admission. [Admin UI](/docs/packages/ui/admin-ui) supplies
optional list/detail/filter and invitation controls with host-owned operator
authorization. Authorized export, soft removal and all-list contact erasure are
separate opt-in workflows, not direct row deletion. Linked stores, providers and
backups still require a reviewed cleanup and replay policy.

## Approaches

### Subscription lifecycle

Expose rate-limitable subscribe, double-opt-in confirmation, unsubscribe, and preference flows without leaking signed tokens.

Implemented in source; check the exact release before adopting.

- [@playstack/audiences](/docs/packages/communication/audiences)
- [@playstack/audiences-react](/docs/packages/communication/audiences-react)
- [@playstack/nest-audiences](/docs/packages/communication/nest-audiences)

### Consent and preferences

Retain consent evidence and model list membership as an independent edge that can be removed, restored, and audited.

Implemented in source; check the exact release before adopting.

- [@playstack/audiences](/docs/packages/communication/audiences)
- [@playstack/nest-audiences](/docs/packages/communication/nest-audiences)

### Segmentation

Evaluate application-owned tags and segment plans without placing business data or provider query languages in the core model.

Implemented in source; check the exact release before adopting.

- [@playstack/audiences](/docs/packages/communication/audiences)

### Broadcast delivery

Resolve recipients into suppression-aware queued sends while keeping audience authority separate from delivery providers.

Implemented in source; check the exact release before adopting.

- [@playstack/audiences](/docs/packages/communication/audiences)
- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/mail](/docs/packages/infrastructure/mail)
- [@playstack/queues](/docs/packages/infrastructure/queues)

## Content and AI

Source: https://playstack.dev/features/content-ai.md

Model portable content operations and compose AI-assisted generation through explicit providers, schemas, policy, and application-owned publication workflows.

Implemented in source; check the exact release before adopting.

## Keep the content model authoritative

AI can propose, classify, extract, and transform. It should not quietly become the source of publication truth. Playstack keeps application-owned content records at the center and treats model calls as typed operations with observable inputs and outputs.

## Validate both sides of generation

~~~ts
import { createAi } from '@playstack/ai'

const ai = createAi({
  persistence,
  events,
  connections,
  rateLimiter,
  entitlements,
  connectionScope: 'account',
  allowPlatformFallback: false,
  providers: [
    {
      id: 'anthropic',
      createModel: (modelId, { apiKey }) => anthropic(modelId, { apiKey }),
    },
  ],
  prices,
  clock,
  ids,
})

const call = await ai.beginCall('anthropic:configured-model', {
  accountId,
  actor: { type: 'system', id: 'article-summary' },
})
// Application-owned helper runs the SDK and maps measured numeric usage.
const result = await summarizeArticleWithSdk(
  call.model,
  articleSummaryInput.parse({ title: article.title, body: article.body }),
)
await ai.completeCall(call, { usage: result.usage })
const summary = articleSummaryOutput.parse(result.value)
~~~

The SDK and application own generation and input/output validation. Playstack
owns the selected account/actor context, policy preflight and reported usage.
System calls use account-scoped credentials and a separate system rate-limit
policy. The application maps known, partial or unknown usage instead of replacing
missing tokens with zero; raw provider payloads do not become accounting records.
Keep provider execution failures separate from accounting failures, and reconcile
crashes before a terminal report. The call/model stays server-owned and is never
serialized. See [AI accounting](/docs/packages/content-intelligence/ai).

## Publish through explicit workflow steps

~~~ts
await contentWorkflow.enqueue('article.prepare', {
  contentId: article.id,
  revision: article.revision,
})
~~~

A worker can enrich metadata, request human review, and publish the accepted revision to a site or feed. Idempotency keys include the content revision so a retry cannot publish an obsolete generation over a newer edit.

## Approaches

### Content contracts

Represent source, normalized metadata, transformation state, and publication intent without binding the domain to a CMS or framework.

Implemented in source; check the exact release before adopting.

- [@playstack/content](/docs/packages/content-intelligence/content)

### Structured AI generation

Validate model input and output against application schemas while keeping provider selection and native clients explicit.

Implemented in source; check the exact release before adopting.

- [@playstack/ai](/docs/packages/content-intelligence/ai)

### Content workflows

Carry reviewable operations through events and queues before publishing to application-owned stores, pages, or feeds.

Implemented in source; check the exact release before adopting.

- [@playstack/content](/docs/packages/content-intelligence/content)
- [@playstack/ai](/docs/packages/content-intelligence/ai)
- [@playstack/events](/docs/packages/events-operations/events)
- [@playstack/queues](/docs/packages/infrastructure/queues)
- [@playstack/feeds](/docs/packages/content-intelligence/feeds)

## Caching

Source: https://playstack.dev/features/caching.md

Use namespaced portable caches with typed serialization, tag invalidation, and explicit stampede-protection capabilities across Redis, Workers, and local runtimes.

Implemented in source; check the exact release before adopting.

## Own the namespace

Every cache instance receives an application and environment identity. Physical keys therefore remain safe when multiple products, previews, workers, or test suites share one provider.

~~~ts
import { createCache } from '@playstack/cache'
import { RedisCacheStorage } from '@playstack/cache/redis'

export const cache = createCache({
  app: 'billing',
  environment: 'production',
  driver: new RedisCacheStorage(redis),
  lockTtlMs: 30_000,
})

const plan = await cache.remember(
  'plans:pro',
  60_000,
  () => plans.load('pro'),
)
~~~

## Invalidate by domain fact

~~~ts
const accountCache = cache.tags('accounts', `account:${account.id}`)

await accountCache.put('summary', summary, 60_000)
await accountCache.flush()
~~~

Tag versions avoid provider key scans. An adapter declares whether invalidation is immediate or eventual and whether stampede protection is process-local or distributed, so an application can reject a capability mismatch during configuration.

## Keep failure policy visible

Serialization, lock timeouts, and provider unavailability have distinct stable errors. The application decides whether a specific read may fail open, retry, or bypass the cache; the package does not silently turn an infrastructure failure into a miss.

## Layer memory over Redis deliberately

The native [Cache guide](/docs/packages/infrastructure/cache#layer-memory-over-redis)
covers direct ioredis binding, bounded memory/Redis storage, native batches,
remaining-TTL fills and combined logical statistics. Configure a finite L1
staleness ceiling; another process's overwrite can remain stale until that ceiling.
This is not a coherent authorization or revocation store.

Opt-in background writes support immediate provisional reads through their own
helper, with separate memory/pending-work budgets and an explicit shutdown drain.
Admission is lossy, not a Redis acknowledgment. Unknown write failure stops the
helper and clears provisional values rather than automatically retrying an
uncertain write; ordinary cache writes and `remember` remain acknowledged.

## Approaches

### Namespaced key-value caching

Keep application and environment boundaries in every physical key while preserving direct access to the configured storage client.

Implemented in source; check the exact release before adopting.

- [@playstack/cache](/docs/packages/infrastructure/cache)

### Stampede-protected remember

Load a missing value once with process-local or distributed locking declared by the selected adapter.

Implemented in source; check the exact release before adopting.

- [@playstack/cache](/docs/packages/infrastructure/cache)

### Tag invalidation

Invalidate related keys through versioned tags without provider-specific key scans or hidden global state.

Implemented in source; check the exact release before adopting.

- [@playstack/cache](/docs/packages/infrastructure/cache)

## File storage

Source: https://playstack.dev/features/file-storage.md

Store and retrieve application objects through explicit filesystem, S3, or R2 providers with validated keys, metadata, streaming bodies, and signed access.

Implemented in source; check the exact release before adopting.

## Keep object policy above the provider

The storage boundary handles bytes, streams, metadata, prefixes, and provider capabilities. The application still decides who may upload, which content types are permitted, how records refer to objects, and when deletion is safe.

~~~ts
import { createStorage } from '@playstack/storage'
import { S3StorageProvider } from '@playstack/storage/s3'

export const storage = createStorage({
  app: 'media',
  environment: 'production',
  provider: new S3StorageProvider({ client: s3, bucket }),
})

await storage.put(`accounts/${account.id}/avatars/${file.id}`, body, {
  contentType: file.contentType,
  cacheControl: 'private, max-age=3600',
  metadata: { ownerId: account.id },
})
~~~

## Sign only supported operations

~~~ts
const uploadUrl = await storage.signedUrl(objectKey, {
  operation: 'put',
  contentType: input.contentType,
  expiresInSeconds: 300,
})
~~~

Capabilities state whether a provider supports public URLs, signed downloads, and signed uploads. Unsupported behavior fails before an invalid URL reaches a client.

## Stream across runtime boundaries

Returned objects expose Web `ReadableStream` bodies so portable code works in server and Worker runtimes. Node adapters accept familiar byte and stream inputs without forcing Node-only types into the core contract.

## Approaches

### Portable object storage

Put, inspect, stream, and delete objects through an application-prefixed driver without hiding its native client.

Implemented in source; check the exact release before adopting.

- [@playstack/storage](/docs/packages/infrastructure/storage)

### Signed download and upload access

Issue bounded get or put URLs only when the provider declares the required signing capability.

Implemented in source; check the exact release before adopting.

- [@playstack/storage](/docs/packages/infrastructure/storage)

### Local and test storage

Use filesystem and in-memory contracts with the same key and metadata behavior as production object stores.

Implemented in source; check the exact release before adopting.

- [@playstack/storage](/docs/packages/infrastructure/storage)

## Search

Source: https://playstack.dev/features/search.md

Build product-specific search on portable query, ranking, transport, and framework contracts without coupling records to a provider.

Implemented in source; check the exact release before adopting.

## Keep the document model in the product

`@playstack/search` owns query execution, weighted lexical ranking, stable tie-breaking, transport validation, cancellation, and safe HTTP failures. The application maps its records, filters, groups, and result shape into those contracts.

```ts
import { createLexicalSearchEngine } from '@playstack/search'

const search = createLexicalSearchEngine(documents, {
  document: (document) => ({
    id: document.id,
    fields: {
      title: document.title,
      body: document.body,
      tags: document.tags,
    },
    result: {
      title: document.title,
      href: document.href,
    },
  }),
  fields: { title: 10, tags: 6, body: 1 },
})
```

Chakra Docs can therefore retain its document and navigation vocabulary while reusing Playstack's generic search mechanics.

## Choose an engine independently

The built-in lexical engine is dependency-free. `@playstack/search-minisearch` adds a MiniSearch-backed provider for larger in-memory indexes without changing the application protocol.

## Expose one protocol across frameworks

`@playstack/search-next` supplies explicit App Router and Pages Router handlers. `@playstack/nest-search` supplies controller and injection helpers for NestJS. Authentication, authorization, cache policy, and route placement remain application decisions.

This lets a browser query the same product-defined search contract whether the server boundary lives in Next.js or NestJS.

## Approaches

### Weighted lexical search

Map product records into weighted fields, synonyms, filters, groups, and stable results.

Implemented in source; check the exact release before adopting.

- [@playstack/search](/docs/packages/content-intelligence/search)

### MiniSearch indexing

Use a maintained in-memory provider while preserving the portable engine and result contracts.

Implemented in source; check the exact release before adopting.

- [@playstack/search-minisearch](/docs/packages/content-intelligence/search-minisearch)

### Next and Nest bindings

Expose the same validated protocol through App Router, Pages Router, or NestJS application routes.

Implemented in source; check the exact release before adopting.

- [@playstack/search-next](/docs/packages/content-intelligence/search-next)
- [@playstack/nest-search](/docs/packages/content-intelligence/nest-search)

## Local-first

Source: https://playstack.dev/features/local-first.md

Share durable local mutations and synchronization semantics across web, Expo, and desktop clients while keeping product policy explicit.

Implemented in source; check the exact release before adopting.

## Share the synchronization contract

`@playstack/local-first` is runtime-neutral. A product supplies its entities, mutation and change vocabulary, conflict policies, storage, transport, runtime coordination, clock, and identifiers.

```ts
const client = createLocalFirstClient({
  definition,
  storage,
  transport,
  runtime,
  scope: () => ({ type: 'account', id: currentAccountId }),
  clock,
  ids,
})

await client.mutate('draft.save', draft)
await client.sync()
```

Every mutation updates local state and the durable outbox within one storage transaction. Synchronization then acquires a scope lease, pushes a bounded batch, records retry or conflict outcomes, and applies pull pages and cursors atomically.

## Adapt real platform differences

Use `@playstack/local-first-indexeddb` for browser and PWA persistence. Use `@playstack/local-first-expo` for Expo SQLite storage and React Native lifecycle coordination. Both satisfy the same core conformance contracts without pretending IndexedDB and SQLite have identical native APIs.

Tauri SQLite, shared React bindings, and a Nest server package remain planned. Applications continue to own authentication, server change sources, attachments, retention, and background-execution policy.

## Keep invariant-heavy writes online

Local-first support does not make payments, limited inventory, or other invariant-heavy commands automatically safe offline. Products choose which mutations can be accepted speculatively and how conflicts are surfaced or recovered.

## Approaches

### Atomic local mutations

Commit product state and its outbound mutation together so crashes cannot split the two records.

Implemented in source; check the exact release before adopting.

- [@playstack/local-first](/docs/packages/infrastructure/local-first)

### Portable synchronization

Push idempotent mutation batches and apply cursor-based change pages under an explicit runtime lease.

Implemented in source; check the exact release before adopting.

- [@playstack/local-first](/docs/packages/infrastructure/local-first)

### Platform storage

Persist the same contracts with IndexedDB in browsers or Expo SQLite in React Native applications.

Implemented in source; check the exact release before adopting.

- [@playstack/local-first-indexeddb](/docs/packages/infrastructure/local-first-indexeddb)
- [@playstack/local-first-expo](/docs/packages/infrastructure/local-first-expo)

## Webhooks

Source: https://playstack.dev/features/webhooks.md

Verify provider requests from raw bytes, claim events transactionally, enqueue normalized work exactly once, and keep provider parsing outside domain handlers.

Implemented in source; check the exact release before adopting.

## Preserve the raw request

Signature verification happens before JSON parsing, body transformation, or domain dispatch. Framework adapters must expose the original bytes and normalized headers at this boundary.

~~~ts
import {
  createWebhooks,
  githubWebhookProvider,
} from '@playstack/webhooks'

const webhooks = createWebhooks({
  providers: [githubWebhookProvider({ secret: config.github.webhookSecret, crypto, clock })],
  persistence,
  queue,
})

const result = await webhooks.receive({ provider: 'github', instance: 'default' }, {
  headers: requestHeaders,
  body: rawBody,
})
~~~

## Accept once, process safely

The receipt claim and queue handoff use the same transaction kind. A provider retry returns `duplicate` rather than creating another job, while a rolled-back enqueue leaves the event claim available for the next attempt.

## Normalize transport, not business meaning

The provider adapter identifies the provider, event ID, event type, verified payload, and receipt time. Billing, repository automation, delivery feedback, and other consumers remain responsible for interpreting the event under their own policies.

## Compose optional outbound notifications

Native outbound PostgreSQL persistence supplies leases, history, retry deadlines,
attempt/age budgets and terminal compaction. Slack/Discord profiles, encrypted
editable settings and shared route/global cooldowns are explicit compositions;
Discord's dedicated Node transport also validates bounded body-only retry metadata.
Optional webhook outages must not block core consent or email operation.

The [Webhooks guide](/docs/packages/communication/webhooks) separates supplied
contracts from live provider/egress qualification. Unknown acknowledgments can
duplicate remote effects; administrative resends stay unavailable without
independent non-delivery evidence and fencing of all prior senders.

## Approaches

### Raw-request verification

Verify signatures against untouched request bytes through provider adapters before parsing or trusting payloads.

Implemented in source; check the exact release before adopting.

- [@playstack/webhooks](/docs/packages/communication/webhooks)

### Transactional receipt claims

Claim provider event IDs and enqueue normalized work in one application-owned persistence transaction.

Implemented in source; check the exact release before adopting.

- [@playstack/webhooks](/docs/packages/communication/webhooks)
- [@playstack/queues](/docs/packages/infrastructure/queues)

### Provider normalization

Translate GitHub, Stripe, or custom event envelopes into one stable receipt shape while leaving domain interpretation to consumers.

Implemented in source; check the exact release before adopting.

- [@playstack/webhooks](/docs/packages/communication/webhooks)
- [@playstack/events](/docs/packages/events-operations/events)

## Error handling

Source: https://playstack.dev/features/error-handling.md

Carry stable application errors across server and client boundaries, report normalized failures, and retain framework-native recovery without leaking sensitive context.

Implemented in source; check the exact release before adopting.

## Keep one error identity across boundaries

Domain code should not know whether a failure becomes an HTTP response, a React fallback, a queue retry, or a provider event. Stable error codes carry safe meaning while adapters translate them at the application edge.

~~~ts
import { PlaystackError, createErrorCode } from '@playstack/core/errors'

class ReleaseNotFound extends PlaystackError {
  readonly code = createErrorCode('releases', 'not_found')
  readonly retryable = false
  constructor() { super('Release not found') }
}

throw new ReleaseNotFound()
~~~

## Normalize before reporting

~~~ts
import { configureErrors } from '@playstack/errors'

export const errors = configureErrors({
  environment: config.environment,
  release: config.release,
  drivers: [sentryDriver],
  trace,
})

errors.captureError(error, {
  accountId: request.account?.id,
  tags: { operation: 'release.publish' },
})
~~~

The reporter never throws into product behavior. Driver, trace, and input failures are isolated through a diagnostic callback, and sensitive context is scrubbed before any external provider receives it.

Install `sanitizeSentryEvent` in every Sentry SDK runtime's final `beforeSend`
hook, including automatically captured exceptions. It covers credential aliases,
sensitive property names, nested user/request payloads, nonopaque-looking user IDs
and absolute or relative URL query/fragment data. It preserves safe diagnostic
structure without claiming universal secret detection. Host-specific telemetry
policy and genuinely opaque identifiers remain required; see the [Sentry composition](/integrations/sentry).

## Let frameworks recover natively

NestJS maps safe failures to transport responses. React renders an application-provided fallback and owns reset behavior. Both use the same portable error identity without forcing server concerns into the browser or UI choices into domain code.

## Approaches

### Stable application errors

Represent safe codes, causes, severity, and public details without coupling domain failures to HTTP or React.

Implemented in source; check the exact release before adopting.

- [@playstack/errors](/docs/packages/foundation/errors)

### NestJS error boundary

Map thrown failures through one filter into safe responses and normalized reporting context.

Implemented in source; check the exact release before adopting.

- [@playstack/errors](/docs/packages/foundation/errors)
- [@playstack/nest-errors](/docs/packages/foundation/nest-errors)

### React recovery boundaries

Capture render failures, expose application-owned fallback UI, and preserve reset behavior without embedding a reporting vendor.

Implemented in source; check the exact release before adopting.

- [@playstack/errors](/docs/packages/foundation/errors)
- [@playstack/errors-react](/docs/packages/foundation/errors-react)

### Provider-independent reporting

Scrub and normalize error, release, environment, trace, user, and account context before configured drivers receive it.

Implemented in source; check the exact release before adopting.

- [@playstack/errors](/docs/packages/foundation/errors)

## GitHub App access

Source: https://playstack.dev/features/github-apps.md

Authenticate GitHub App installations and compose application-owned repository access workflows with scoped, short-lived tokens.

Preview composition; review its documented limitations before adopting.

## Provision from desired access

GitHub is an application edge, not the source of subscription or entitlement truth. The application resolves desired access first, obtains the narrowest installation token it needs, and performs repository or team operations through its own GitHub client.

~~~ts
import { createGitHubApp } from '@playstack/github-app'

const github = createGitHubApp({
  appId: config.github.appId,
  privateKey: config.github.privateKey,
  crypto: githubJwtSigner,
  client: githubInstallationClient,
  cache,
  clock,
})

const access = await github.getInstallationToken({
  installationId: account.githubInstallationId,
  repositoryIds: account.repositoryIds,
  permissions: { contents: 'read' },
})
~~~

## Keep provider tokens temporary

Installation access tokens are short-lived implementation details. The package scopes, caches, and refreshes them when needed, then returns the token to the application boundary that performs provider operations. It does not wrap Octokit, store installations, or decide which repositories a subscriber may access.

## Reconcile after every edge case

Invitation acceptance is asynchronous, users rename accounts, and organization policy can reject an otherwise valid request. A queue-backed reconciler should record desired versus observed state and retry only operations that remain current. That orchestration remains application-owned today rather than being claimed as an implemented `@playstack/github-app` service.

## Approaches

### Installation authentication

Create scoped, short-lived installation tokens from application-owned GitHub App credentials without persisting them.

Implemented in source; check the exact release before adopting.

- [@playstack/github-app](/docs/packages/identity/github-app)

### Repository access provisioning

Translate entitlement changes into explicit invitations and removals for application-configured repositories and teams.

Preview composition; review its documented limitations before adopting.

- [@playstack/github-app](/docs/packages/identity/github-app)
- [@playstack/nest-github-app](/docs/packages/identity/nest-github-app)

### Reconciliation workflows

Compare desired and observed access, retry transient provider failures, and record durable outcomes.

Planned; not an available capability.

- [@playstack/github-app](/docs/packages/identity/github-app)
- [@playstack/events](/docs/packages/events-operations/events)
- [@playstack/queues](/docs/packages/infrastructure/queues)

## Realtime and WebRTC

Source: https://playstack.dev/features/realtime-webrtc.md

Compose authenticated realtime channels, resumable client state, WebRTC session control, and ICE or relay infrastructure without conflating events, signalling, and media.

Planned; not an available capability.

## Keep three boundaries visible

Realtime channels carry application events and commands over persistent sockets. WebRTC signalling coordinates peer or SFU sessions. Media tracks, codecs, simulcast, and recording remain provider-native concerns. Combining all three produces an abstraction that is either too weak or quietly shaped like one vendor.

## Authenticate sockets without credential URLs

~~~ts
import { createRealtimeClient } from '@playstack/realtime'

const realtime = createRealtimeClient({
  url,
  getTicket: () => api.createRealtimeTicket(),
  protocolVersion: 1,
})

const unsubscribe = realtime.subscribe(
  `account:${accountId}`,
  handleAccountEvent,
)
~~~

The HTTP session mints a short-lived, single-use ticket. The socket presents it in the protocol handshake rather than a query string, and the server revalidates session expiry while the connection remains open.

## Resume, then refetch

Channel events carry bounded cursors. A reconnect resumes from the last cursor while it remains in the server window; a larger gap tells the client to refetch over HTTP. Realtime never turns itself into an unbounded event store.

## Declare media capabilities

~~~ts
const session = await webrtc.createSession({
  topology: 'sfu',
  require: {
    simulcast: true,
    serverRecording: false,
    dataChannels: true,
  },
})
~~~

Unsupported requirements fail before provider calls. The planned packages will expose native clients for media operations while standardizing the session, signalling, and relay surfaces that genuinely repeat across providers.

## Approaches

### Authenticated realtime channels

Subscribe through single-use session tickets, authorize every channel server-side, and reconnect with bounded cursor replay.

Planned; not an available capability.

- @playstack/realtime
- @playstack/realtime-react
- @playstack/nest-realtime
- [@playstack/auth](/docs/packages/identity/auth)

### WebRTC sessions and signalling

Normalize session creation, signalling, participants, and provider capabilities while leaving media tracks in provider-native APIs.

Planned; not an available capability.

- @playstack/webrtc
- @playstack/webrtc-react
- @playstack/nest-webrtc

### ICE and relay coordination

Issue bounded relay credentials and select compatible connectivity profiles without embedding one media provider's room model.

Planned; not an available capability.

- @playstack/webrtc
- @playstack/nest-webrtc

## Licensing

Source: https://playstack.dev/features/licensing.md

Issue signed licenses, activate device-bound seats, verify access offline, rotate refresh credentials, and revoke grants without coupling product policy to the token protocol.

Implemented in source; check the exact release before adopting.

## Verify a paid application offline

The application embeds issuer public keys and supplies its explicit product, device, trusted-time, grace, and revocation policy to the portable verifier.

~~~ts
import {
  RECOMMENDED_OFFLINE_GRACE_SECONDS,
  WebCryptoLicenseVerifier,
  createLicensing,
} from '@playstack/licensing'

const licensing = createLicensing(
  new WebCryptoLicenseVerifier({
    subtle: crypto.subtle,
    keys: { 'production-2026': issuerPublicKey },
  }),
)

const result = await licensing.validate({
  token,
  product: 'quickzones',
  deviceId,
  now: Math.floor(Date.now() / 1_000),
  highestTrustedTime,
  offlineGraceSeconds: RECOMMENDED_OFFLINE_GRACE_SECONDS,
  isRevoked: (licenseId) => revokedIds.has(licenseId),
})
~~~

Offline verification cannot observe immediate revocation, so payload validity and grace are bounded product policy. Omitting `offlineGraceSeconds` means zero grace.

## Issue without surrendering key custody

`@playstack/licensing-issuer` owns issuance, activation-code digests, device seats, refresh rotation, deactivation, and revocation. The application supplies purchase authorization, encrypted response storage, distribution, and a signing seam backed by its KMS, HSM, or remote signer.

~~~ts
const issuer = createLicensingIssuer({
  persistence,
  authorizer: applicationLicensePolicy,
  distribution: transactionalDeliveryOutbox,
  signer: kmsBackedEd25519Signer,
  secrets,
  responseVault,
  events,
  clock,
  ids,
})
~~~

`@playstack/nest-licensing` exposes separate client activation and authorized administration services. Your controllers, authentication, product catalog, payment source, mail templates, and exact signed-license distribution policy remain application-owned.

## Compose direct and store purchases honestly

A direct license and an App Store receipt are different credentials. Native applications can project both into one application-owned access layer while retaining separate verification paths. Deterministic vectors from `@playstack/licensing/vectors` let TypeScript, Swift, and Rust implementations execute the same protocol cases.

## Approaches

### Offline license verification

Validate versioned Ed25519-signed payloads locally with explicit clock, grace, product, device, and revocation inputs.

Implemented in source; check the exact release before adopting.

- [@playstack/licensing](/docs/packages/identity/licensing)

### Issuance and activation

Turn application-owned purchase policy into single-display activation codes, bounded signed payloads, and device seats.

Implemented in source; check the exact release before adopting.

- [@playstack/licensing-issuer](/docs/packages/identity/licensing-issuer)
- [@playstack/nest-licensing](/docs/packages/identity/nest-licensing)

### Refresh and revocation

Rotate refresh credentials, detect reuse, deactivate seats, and apply revocation state when connectivity returns.

Implemented in source; check the exact release before adopting.

- [@playstack/licensing](/docs/packages/identity/licensing)
- [@playstack/licensing-issuer](/docs/packages/identity/licensing-issuer)
- [@playstack/nest-licensing](/docs/packages/identity/nest-licensing)

### Entitlement projection

Translate an edition and limits into the stable grants also used by subscriptions, registration, and staff access.

Implemented in source; check the exact release before adopting.

- [@playstack/licensing-issuer](/docs/packages/identity/licensing-issuer)
- [@playstack/entitlements](/docs/packages/identity/entitlements)

## Progressive web apps

Source: https://playstack.dev/features/progressive-web-apps.md

Compose installability, application-shell caching, update coordination, and push registration without confusing a service worker with local-first application data.

Planned; not an available capability.

## Installability is not local-first data

A service worker can install an application shell and cache revisioned assets. It should not become a second authority for domain reads and pending writes owned by a local-first store. Those capabilities compose, but neither implies the other.

## Start with safe cache boundaries

~~~ts
import { definePwa } from '@playstack/pwa'

export const pwa = definePwa({
  manifest: {
    name: 'Timespans',
    shortName: 'Timespans',
    startUrl: '/',
    display: 'standalone',
  },
  routes: {
    assets: 'cache-first',
    documents: 'stale-while-revalidate',
    localDataApi: 'network-only',
  },
})
~~~

Workbox or the selected framework integration owns service-worker mechanics. Playstack supplies the configuration boundary and conventions that prevent API caches from fighting application data state.

## Let the user choose an update moment

A newly installed worker waits while old tabs remain open. The React binding exposes that state so the product can present an update banner, activate only after confirmation, and avoid reloading a page with unsaved work.

## Register push through devices

The PWA layer obtains a Web Push subscription after a user action and registers it with `@playstack/devices`. Notification preference and delivery remain in the notification stack; the service worker does not become a second notification policy engine.

## Planned until another concrete consumer earns it

The boundaries are specified, but the package remains deferred until a second product needs web installability strongly enough to validate the shared interface.

## Approaches

### Manifest and installation

Generate portable manifest and platform assets, capture install prompts, and provide explicit iOS installation guidance.

Planned; not an available capability.

- @playstack/pwa
- @playstack/pwa-react

### Application-shell caching

Apply revisioned asset and document strategies while keeping local-first domain reads and mutations outside service-worker caches.

Planned; not an available capability.

- @playstack/pwa

### Safe update coordination

Surface waiting service workers and let the product choose when to activate and reload without discarding unsaved work.

Planned; not an available capability.

- @playstack/pwa
- @playstack/pwa-react

### Web Push registration

Request permission in response to user intent and attach the resulting subscription to the shared device boundary.

Planned; not an available capability.

- @playstack/pwa
- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/notifications](/docs/packages/communication/notifications)

## Application interfaces

Source: https://playstack.dev/features/application-ui.md

Compose application-owned React and Next surfaces from Chakra themes, controlled App UI and scoped account/audience operator sections.

Preview composition; review its documented limitations before adopting.

## A small UI stack

[Chakra](/docs/packages/ui/chakra) supplies semantic themes; [App UI](/docs/packages/ui/app-ui) supplies shells, navigation, settings, collections, commands, Kanban and resizable panes; [Admin UI](/docs/packages/ui/admin-ui) supplies a shell, member administration and audience operator sections.

Prebuilt and custom sections share ordinary Next routes. The host owns sessions, data fetching, authorization, router state and persistent preferences. No auth backend, alternate routing framework or mandatory table/DnD engine is installed.

These are preview compositions, not a finished SaaS UI or hosted staff service.
Global staff authority and impersonation remain host-owned; virtualized editing,
pointer Kanban ordering and tours are still to come.

## Opt into operator workflows

Audience list/detail/filter views, early-access batch controls, private export,
soft removal, all-list erasure review and masked Slack/Discord settings are
separate host-composed controls. Every backend independently checks current
operator authority; customer account ownership and UI permission hints grant
no deployment-list access. Installations do not automatically enable sending,
downloads or erasure. Playstack's own admin app uses independently configured
read-only audience and product-update consent views, not every packaged action.

## Localized package copy

App UI and Admin UI now share [localization catalogs](/features/localization), with editable English artifacts, explicit caller overrides and a host-selected ICU formatter. The rich AccountPicker adds searchable account choices, avatars/badges and create/manage actions alongside the existing native AccountSwitcher. Neither localization nor account controls replace server authority.

## Approaches

### Theme composition

Neutral/warm presets and application-owned Chakra overrides.

Preview composition; review its documented limitations before adopting.

- [@playstack/chakra](/docs/packages/ui/chakra)

### Prebuilt and custom administration

Scoped member and audience controls alongside ordinary custom product routes.

Preview composition; review its documented limitations before adopting.

- [@playstack/admin-ui](/docs/packages/ui/admin-ui)
- [@playstack/app-ui](/docs/packages/ui/app-ui)

## Localization

Source: https://playstack.dev/features/localization.md

Share locale policy and message catalogs across application UI, APIs and delivery without choosing a translation vendor.

Preview composition; review its documented limitations before adopting.

[Localization](/docs/packages/foundation/i18n) supplies immutable policies, typed catalog IDs, bounded language negotiation, fallback metadata and native Intl formatting. React and Nest bindings can be adopted independently.

[App UI and Admin UI](/docs/frameworks/ui-localization) ship editable English source catalogs and consume a shared message provider. Applications retain translations, ICU formatting, locale routing, recipient preferences and language/direction setup. Extraction, an ICU compiler and native catalog generation remain outside the shipped surface.

## Approaches

### Locale and catalog contracts

Context-specific resolution, typed flat messages and effective-locale fallback.

Preview composition; review its documented limitations before adopting.

- [@playstack/i18n](/docs/packages/foundation/i18n)

### UI and API composition

Isolated React providers and stateless Nest locale resolution.

Preview composition; review its documented limitations before adopting.

- [@playstack/i18n-react](/docs/packages/foundation/i18n-react)
- [@playstack/nest-i18n](/docs/packages/foundation/nest-i18n)

## Frameworks

Source: https://playstack.dev/frameworks.md

- [React](/frameworks/react): Headless domain bindings plus optional Chakra application and admin compositions, with server authority kept outside the client.
- [Next.js](/frameworks/nextjs): Compose Playstack server contracts, React bindings, SSR handoff, and route adapters at the Next.js application edge.
- [NestJS](/frameworks/nestjs): Connect portable Playstack capabilities to dependency injection, guards, decorators, request context, workers, and lifecycle hooks.
- [Expo](/frameworks/expo): Use Playstack authentication and local-first contracts with Expo SecureStore, SQLite, and React Native lifecycle adapters.
- [Cloudflare Workers](/frameworks/cloudflare-workers): Run portable contracts in Web-standard runtimes with explicit Workers KV, R2, Queues and Durable Object adapters.

## React

Source: https://playstack.dev/frameworks/react.md

Headless domain bindings plus optional Chakra application and admin compositions, with server authority kept outside the client.

Implemented in source; check the exact release before adopting.

## Client composition without client authority

React adapters expose application state and actions through providers and hooks for authentication, accounts, analytics, audiences, errors, and notifications. Server-side authentication, authorization, tenant scope, and persistence remain authoritative.

## Start with a provider boundary

Install only the adapter for the feature being rendered, pass an application-owned transport, and hydrate it from a validated server projection when available.

## Application UI compositions

The optional [UI stack](/features/application-ui) now includes Chakra themes, app shells/navigation, settings, tables, commands, Kanban and resizable panes, plus the first account-admin shell and members section. These are React DOM compositions, separate from the headless domain bindings.

Prebuilt and custom Next routes use an application-owned shell and server authorization. Create Chakra systems in client-owned modules; keep interactive callbacks behind Client Component boundaries. Global staff and impersonation flows are still to come; verify browser behavior in your own product. See [UI packages](/docs/packages/ui).

## Shared locale context

[i18n-react](/docs/packages/foundation/i18n-react) provides isolated typed catalog hooks and a package-message bridge. App UI and Admin UI consume the same application catalogs and formatter; English defaults work without a provider. Locale detection, routing and translations stay host-owned.

## Packages

- [@playstack/i18n-react](/docs/packages/foundation/i18n-react)
- [@playstack/chakra](/docs/packages/ui/chakra)
- [@playstack/app-ui](/docs/packages/ui/app-ui)
- [@playstack/admin-ui](/docs/packages/ui/admin-ui)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/accounts-react](/docs/packages/identity/accounts-react)
- [@playstack/analytics-react](/docs/packages/events-operations/analytics-react)
- [@playstack/audiences-react](/docs/packages/communication/audiences-react)
- [@playstack/errors-react](/docs/packages/foundation/errors-react)
- [@playstack/notifications-react](/docs/packages/communication/notifications-react)
- [@playstack/validation](/docs/packages/foundation/validation)

## Next.js

Source: https://playstack.dev/frameworks/nextjs.md

Compose Playstack server contracts, React bindings, SSR handoff, and route adapters at the Next.js application edge.

Implemented in source; check the exact release before adopting.

## Install the edges you use

Next.js can host browser UI, server rendering, route handlers, and application composition. Playstack adapters describe the router and runtime boundary they actually support instead of claiming every Next.js surface implicitly.

```sh
npm install @playstack/auth @playstack/auth-next @playstack/auth-react
```

| Next.js boundary                                | Playstack package                                                                    | Responsibility                                                                  |
| ----------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Browser providers and forms                     | [`@playstack/auth-react`](/docs/packages/identity/auth-react)                        | Safe session state and application-owned auth transport                         |
| Pages Router SSR                                | [`@playstack/auth-next`](/docs/packages/identity/auth-next)                          | Optional or required session handoff                                            |
| App Router server components and route handlers | [`@playstack/auth-next`](/docs/packages/identity/auth-next)                          | Validated session resolution and application-owned unauthenticated policy       |
| App Router content lookup                       | [`@playstack/content-next`](/docs/packages/content-intelligence/content-next)        | Static params and document lookup with direct or promised route params          |
| Shared ISR and signed revalidation              | [`@playstack/cache-next`](/docs/packages/infrastructure/cache-next)                  | Redis-backed cache handling, tag invalidation, and router-specific revalidation |
| App and Pages Router search                     | [`@playstack/search-next`](/docs/packages/content-intelligence/search-next)          | Validated search handlers with explicit request context and cache policy        |
| API route or route handler                      | [`@playstack/auth`](/docs/packages/identity/auth)                                    | Portable credential and session authority composed by the application           |
| App Router feed route                           | [`@playstack/feeds-next`](/docs/packages/content-intelligence/feeds-next)            | Feed and sitemap response adapters                                              |
| Navigation analytics                            | [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react)     | Explicit Pages and App Router navigation adapters                               |
| Audience forms and preferences                  | [`@playstack/audiences-react`](/docs/packages/communication/audiences-react)         | Headless signup and token-scoped preference state                               |
| Notification inbox                              | [`@playstack/notifications-react`](/docs/packages/communication/notifications-react) | Headless inbox, pagination, unread state, and optimistic mutations              |

## Connect the browser to application routes

```tsx
import { PlaystackAuthProvider, createFetchAuthTransport } from '@playstack/auth-react'

const transport = createFetchAuthTransport({
  sessionEndpoint: '/api/auth/session',
  refreshEndpoint: '/api/auth/refresh',
  signInEndpoint: '/api/auth/sign-in',
  signOutEndpoint: '/api/auth/sign-out',
  headers: () => ({ 'x-csrf-token': readCsrfCookie() }),
})

export function ApplicationProviders({ children, session }) {
  return (
    <PlaystackAuthProvider transport={transport} initialSession={session}>
      {children}
    </PlaystackAuthProvider>
  )
}
```

Those four routes belong to the application. They translate HTTP and cookies into calls to the configured portable auth service, validate CSRF where cookies are ambient, and return only `AuthSessionView`. When the API is a separate origin, use `createBearerAuthTransport` with the API's `baseUrl` and drop the CSRF header: the API answers `{ session, accessToken, accessExpiresAt }` and protects its cookie routes with `Origin` and `Sec-Fetch-Site` checks for browser requests (`PlaystackOriginGuard` in `@playstack/nest-auth`), while non-browser clients reach the same routes with a bearer token.

## Compose SSR session handoff

```ts
import { createAuthNextHelpers } from '@playstack/auth-next/pages'

export const { requireSession, withSession } = createAuthNextHelpers({
  resolveSession: ({ req }) => resolveSafeSessionView(req),
})
```

Hydrate the same validated session into PlaystackAuthProvider for client navigation without an unauthenticated flash.

## Choose the router boundary explicitly

For a Pages Router page, use `withSession` when authentication is optional and `requireSession` when the page must redirect before rendering.

For an App Router application, create helpers from `@playstack/auth-next/app`. The resolver still belongs in a server-only application module, while Next's `redirect()` or a route-handler response is supplied as the explicit unauthenticated boundary. This supports Server Components and route handlers without hiding cookie access, cache policy, middleware, or server-action authorization.

```tsx
import { redirect } from 'next/navigation'

import { createAppRouterAuthHelpers } from '@playstack/auth-next/app'

const auth = createAppRouterAuthHelpers({
  resolveSession: () => resolveSessionViewFromCookies(),
})

export default async function AccountPage() {
  const session = await auth.requireSession({
    redirectTo: '/login',
    onUnauthenticated: (destination) => redirect(destination ?? '/login'),
  })

  return <Account userId={session.user.id} />
}
```

Provider sign-in follows the same rule. Auth.js may own its provider handshake while [`@playstack/auth-authjs`](/integrations/authjs) normalizes the callback into Playstack identity policy. Email/password and magic-link routes can call the portable credential service directly. See the [Authentication feature guide](/features/authentication) to choose the entry path.

## Add capabilities independently

Account switching adds `@playstack/accounts-react`; feeds and sitemaps add `@playstack/feeds-next`. Shared ISR adds `@playstack/cache-next`, while search routes can use `@playstack/search-next` independently of either. None requires rebuilding the authentication boundary. Follow each supported feature above to see its frontend, server, persistence, and provider dependencies.

## Router boundaries

Auth, content, search, and cache adapters publish explicit router boundaries where their contracts differ. Analytics publishes `./next-pages` and `./next-app`, while feeds produces App Router-compatible route handlers. Provider SDKs, cookies, redirects, cache policy, environment loading, and database clients remain in the application composition root.

## Application UI compositions

The optional [UI stack](/features/application-ui) now includes Chakra themes, app shells/navigation, settings, tables, commands, Kanban and resizable panes, plus the first account-admin shell and members section. These are React DOM compositions, separate from the headless domain bindings.

Prebuilt and custom Next routes use an application-owned shell and server authorization. Create Chakra systems in client-owned modules; keep interactive callbacks behind Client Component boundaries. Global staff and impersonation flows are still to come; verify browser behavior in your own product. See [UI packages](/docs/packages/ui).

## Localized UI without another router

Use [i18n-react](/docs/packages/foundation/i18n-react) for shared App UI/Admin UI catalogs, or compose its message provider alongside the application's existing locale library. Keep SSR/client locale, time zone and direction consistent. Playstack does not install a locale router or ICU runtime. See [UI localization](/docs/frameworks/ui-localization).

## Packages

- [@playstack/i18n-react](/docs/packages/foundation/i18n-react)
- [@playstack/chakra](/docs/packages/ui/chakra)
- [@playstack/app-ui](/docs/packages/ui/app-ui)
- [@playstack/admin-ui](/docs/packages/ui/admin-ui)
- [@playstack/auth-authjs](/docs/packages/identity/auth-authjs)
- [@playstack/auth-next](/docs/packages/identity/auth-next)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/accounts-react](/docs/packages/identity/accounts-react)
- [@playstack/analytics-react](/docs/packages/events-operations/analytics-react)
- [@playstack/audiences-react](/docs/packages/communication/audiences-react)
- [@playstack/cache-next](/docs/packages/infrastructure/cache-next)
- [@playstack/content-next](/docs/packages/content-intelligence/content-next)
- [@playstack/errors-react](/docs/packages/foundation/errors-react)
- [@playstack/feeds-next](/docs/packages/content-intelligence/feeds-next)
- [@playstack/notifications-react](/docs/packages/communication/notifications-react)
- [@playstack/search-next](/docs/packages/content-intelligence/search-next)
- [@playstack/validation](/docs/packages/foundation/validation)

## NestJS

Source: https://playstack.dev/frameworks/nestjs.md

Connect portable Playstack capabilities to dependency injection, guards, decorators, request context, workers, and lifecycle hooks.

Implemented in source; check the exact release before adopting.

## Keep module ownership visible

Every Playstack NestJS module receives an application-configured service or factory. Applications retain database clients, queues, provider SDKs, global guard placement, and shutdown order.

## Compose request boundaries in order

Authentication establishes identity before account scope, authorization, rate limits, and handlers. Guards and decorators expose that order rather than hiding it in global registration.

## Name infrastructure explicitly

`@playstack/nest-storage` registers multiple named stores with stable injection tokens. `@playstack/nest-queues` discovers typed handlers, registers named queues, and owns optional worker startup and shutdown. `@playstack/nest-search` exposes an application-configured search service through a controller boundary without choosing product records, authentication, or route policy.

These modules preserve provider-native configuration and clients. Nest owns dependency injection and lifecycle; portable Playstack packages continue to own capability contracts.

## Integration and migration boundaries

Package-authored integration manifests expose module/provider, route, environment, raw-body, health and test requirements to Megamono. Generated wiring is ejectable application code, not automatically completed controllers. The optional Queuebert bridge reuses reviewed native queues; see [Queuebert](/integrations/queuebert).

Auth now supports device authorization and transactional code issuance; account guards can use a validated request-account resolver while still rechecking membership and permissions. Account administration policies and live API-key authorization remain explicit service hooks. UI packages do not register Nest routes or global guards.

## Stateless localization

[nest-i18n](/docs/packages/foundation/nest-i18n) registers a locale policy and request/recipient resolver through ordinary sync/async DI. It adds no global locale state, routes or database. Applications own authorized account preferences, headers and cache isolation.

## Packages

- [@playstack/nest-i18n](/docs/packages/foundation/nest-i18n)
- [@playstack/nest-accounts](/docs/packages/identity/nest-accounts)
- [@playstack/nest-analytics](/docs/packages/events-operations/nest-analytics)
- [@playstack/nest-api-keys](/docs/packages/identity/nest-api-keys)
- [@playstack/nest-audiences](/docs/packages/communication/nest-audiences)
- [@playstack/nest-audit](/docs/packages/identity/nest-audit)
- [@playstack/nest-auth](/docs/packages/identity/nest-auth)
- [@playstack/nest-billing](/docs/packages/identity/nest-billing)
- [@playstack/nest-catalog](/docs/packages/commerce/nest-catalog)
- [@playstack/nest-commerce](/docs/packages/commerce/nest-commerce)
- [@playstack/nest-delivery](/docs/packages/communication/nest-delivery)
- [@playstack/nest-entitlements](/docs/packages/identity/nest-entitlements)
- [@playstack/nest-errors](/docs/packages/foundation/nest-errors)
- [@playstack/nest-event-store](/docs/packages/events-operations/nest-event-store)
- [@playstack/nest-events](/docs/packages/events-operations/nest-events)
- [@playstack/nest-feeds](/docs/packages/content-intelligence/nest-feeds)
- [@playstack/nest-fulfillment](/docs/packages/commerce/nest-fulfillment)
- [@playstack/nest-github-app](/docs/packages/identity/nest-github-app)
- [@playstack/nest-inventory](/docs/packages/commerce/nest-inventory)
- [@playstack/nest-licensing](/docs/packages/identity/nest-licensing)
- [@playstack/nest-notifications](/docs/packages/communication/nest-notifications)
- [@playstack/nest-payments](/docs/packages/commerce/nest-payments)
- [@playstack/nest-queues](/docs/packages/infrastructure/nest-queues)
- [@playstack/nest-rate-limit](/docs/packages/events-operations/nest-rate-limit)
- [@playstack/nest-search](/docs/packages/content-intelligence/nest-search)
- [@playstack/nest-storage](/docs/packages/infrastructure/nest-storage)
- [@playstack/validation](/docs/packages/foundation/validation)

## Expo

Source: https://playstack.dev/frameworks/expo.md

Use Playstack authentication and local-first contracts with Expo SecureStore, SQLite, and React Native lifecycle adapters.

Implemented in source; check the exact release before adopting.

## Keep mobile tokens outside browser storage

`@playstack/auth-react/expo` adapts Expo SecureStore to Playstack's `TokenStorage` seam without installing or hiding Expo. It stores only opaque access and refresh tokens with numeric expiries, rejects expanded session records, and exposes the injected SecureStore client.

```ts
import * as SecureStore from 'expo-secure-store'

import { createExpoTokenStorage } from '@playstack/auth-react/expo'

export const tokenStorage = createExpoTokenStorage(SecureStore)
```

The application's transport uses that storage when exchanging and refreshing tokens against its Playstack-enabled API. Native navigation, deep links, biometric policy, and server authorization remain application boundaries.

## Persist local-first state in SQLite

`@playstack/local-first-expo` adapts Expo SQLite transactions and React Native lifecycle/connectivity signals to the runtime-neutral `@playstack/local-first` engine. The product shares mutation envelopes, retry and conflict policy, cursor semantics, and migrations with its web and desktop clients while retaining native background-execution and file-handling policy.

## Delivery and what stays with your app

The separate `@playstack/delivery/expo` seam supplies push transport integration; it does not require the notifications inbox. Device enrollment, silent hints, coalescing, provider receipts and background scheduling remain product-owned.

SecureStore at rest does not prove that tokens never enter JavaScript. Native refresh ownership, callbacks, restart and cancellation, and real-device delivery are yours to verify on the devices you ship to. The new Chakra/App UI/Admin UI packages target React DOM and are not Expo-native components.

## Packages

- [@playstack/client-auth](/docs/packages/identity/client-auth)
- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/auth-react](/docs/packages/identity/auth-react)
- [@playstack/local-first](/docs/packages/infrastructure/local-first)
- [@playstack/local-first-expo](/docs/packages/infrastructure/local-first-expo)

## Cloudflare Workers

Source: https://playstack.dev/frameworks/cloudflare-workers.md

Run portable contracts in Web-standard runtimes with explicit Workers KV, R2, Queues and Durable Object adapters.

Implemented in source; check the exact release before adopting.

## Prefer Web-standard boundaries

Portable packages avoid Node-only dependencies unless an explicit Node entry point is selected. Workers KV provides an eventually consistent cache, R2 provides structural object storage, and Durable Objects keep strongly coordinated rate-limit state local to the storage primitive without changing application policy.

## Queue consumers and localization

[Cloudflare Queues](/integrations/cloudflare-queues) now has an explicit producer/consumer adapter with per-message acknowledgement, retry and durable quarantine requirements. Worker templates preserve the queue bindings you review; verify them in your own Cloudflare deployment.

The portable [localization core](/docs/packages/foundation/i18n) supports locale policy and catalog fallback without React/Nest dependencies. The application selects the ICU formatter and recipient preferences.

## Packages

- [@playstack/i18n](/docs/packages/foundation/i18n)
- [@playstack/queues](/docs/packages/infrastructure/queues)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/cache](/docs/packages/infrastructure/cache)
- [@playstack/feeds](/docs/packages/content-intelligence/feeds)
- [@playstack/rate-limit-do](/docs/packages/events-operations/rate-limit-do)
- [@playstack/storage](/docs/packages/infrastructure/storage)

## Integrations

Source: https://playstack.dev/integrations.md

- [Auth.js](/integrations/authjs): Bridge Auth.js provider callbacks into Playstack identity and session contracts while retaining application-owned provider configuration.
- [Prisma](/integrations/prisma): Persist Playstack capabilities through explicit application-owned Prisma clients, transactions, and managed schema fragments.
- [Redis](/integrations/redis): Use Redis for distributed caches, rate-limit counters, and BullMQ-backed jobs while keeping application policy portable.
- [Postmark](/integrations/postmark): Deliver rendered Playstack mail or Postmark provider templates through an explicit driver and native client escape hatch.
- [SMTP](/integrations/smtp): Deliver portable rendered email through an application-configured Nodemailer transport.
- [Resend](/integrations/resend): Deliver transactional mail and synchronize consent-aware audience contacts through explicit Resend adapters.
- [Inngest](/integrations/inngest): Dispatch portable Playstack queue jobs through Inngest and serve the generated functions from Next.js, NestJS, or another supported framework.
- [Fathom Analytics](/integrations/fathom): Send consent-aware pageviews and named events to an application-initialized Fathom client.
- [Plausible Analytics](/integrations/plausible): Send pageviews and custom events with non-null properties through an application-initialized Plausible client.
- [Basemarks](/integrations/basemarks): Adapt Basemarks track events with explicit property mapping; supply an application-owned transport for error reporting.
- [Sentry](/integrations/sentry): Report scrubbed Playstack errors through an initialized Sentry client without hiding SDK configuration or instrumentation.
- [Mailgun](/integrations/mailgun): Deliver rendered messages or stored Mailgun templates through an explicit regional provider client.
- [SendGrid](/integrations/sendgrid): Deliver rendered messages and dynamic templates through an isolated SendGrid mail client.
- [Amazon SES](/integrations/amazon-ses): Deliver rendered messages and SES templates through an application-configured AWS SDK client.
- [Cloudflare Queues](/integrations/cloudflare-queues): Typed Worker jobs with per-message acknowledgements, bounded batches, retries and explicit terminal-failure handling.
- [GitHub](/integrations/github): Connect OAuth credentials, GitHub App installation tokens, and verified webhook delivery through explicit Playstack boundaries.
- [Stripe](/integrations/stripe): Create hosted subscription and one-time checkout, then reconcile verified Stripe events into separate billing and payment projections.
- [Argon2](/integrations/argon2): Use Playstack's Node.js authentication entrypoint with application-configured Argon2id password hashing.
- [SimpleWebAuthn](/integrations/simplewebauthn): Verify passkey registration and authentication ceremonies through Playstack's optional Node.js WebAuthn adapter.
- [BullMQ](/integrations/bullmq): Dispatch and process typed Playstack jobs through BullMQ with explicit Redis ownership and native queue access.
- [AT Protocol](/integrations/at-protocol): Use the official AT Protocol OAuth client while keeping discovery, PAR, PKCE, DPoP, nonces, and session storage intact.
- [Cloudflare Workers KV](/integrations/cloudflare-kv): Use an application-supplied Workers KV binding for namespaced cache storage with explicitly eventual capabilities.
- [Cloudflare R2](/integrations/cloudflare-r2): Store portable Playstack objects through a structural R2 binding with explicit public and signed URL capabilities.
- [MiniSearch](/integrations/minisearch): Back Playstack's product-defined search protocol with a compact in-memory full-text index.
- [Amazon S3](/integrations/amazon-s3): Store portable Playstack objects and issue bounded signed URLs through an application-configured AWS SDK client.
- [Zod](/integrations/zod): Reuse Zod schemas across Playstack content, React forms, wire validation, and NestJS request boundaries.
- [Chakra UI](/integrations/chakra-ui): React DOM themes and application compositions on Chakra v3.
- [Queuebert](/integrations/queuebert): Connect existing named BullMQ queues to an application-authorized Queuebert installation.
- [Relaypath](/integrations/relaypath): Optional Relaypath-backed outbound webhook dispatch without making it a required transport.
- [OAuth 2 providers](/integrations/oauth2): Configurable OAuth 2 credential lifecycle for provider APIs, separate from application login.
- [LinkedIn](/integrations/linkedin): A packaged LinkedIn connection adapter with application-owned scopes and product operations.
- [Mastodon](/integrations/mastodon): A packaged Mastodon connection adapter with explicit instance and credential ownership.
- [X](/integrations/x): A packaged X OAuth 2 connection adapter with explicit application permissions.
- [IndexedDB](/integrations/indexeddb): Browser persistence and Web Locks coordination for the local-first engine.
- [Apple Push Notification service](/integrations/apns): An APNs delivery seam with application-owned provider credentials and device selection.
- [Firebase Cloud Messaging](/integrations/fcm): An FCM delivery seam with explicit provider transport and target lifecycle.
- [Web Push](/integrations/web-push): A Web Push delivery seam without requiring an application notification inbox.

## Auth.js

Source: https://playstack.dev/integrations/authjs.md

Bridge Auth.js provider callbacks into Playstack identity and session contracts while retaining application-owned provider configuration.

Preview composition; review its documented limitations before adopting.

## Provider sign-in at the edge

Auth.js owns provider protocol and callback orchestration. The Playstack bridge translates successful provider identity into portable credential and session behavior.

Normalize the Auth.js profile explicitly, then call
`CredentialService.authenticateProfile()`. Playstack applies the same
verified-email linking, reservations, transaction, and identity events used by
its provider-owned authorization-code path. The bridge projects only the narrow
Playstack session view into Auth.js’s encrypted JWT and works with both Next.js
routers.

Provider definitions, secrets, route placement, and Auth.js module augmentation
remain application-owned.

## Packages

- [@playstack/auth-authjs](/docs/packages/identity/auth-authjs)
- [@playstack/auth](/docs/packages/identity/auth)

## Prisma

Source: https://playstack.dev/integrations/prisma.md

Persist Playstack capabilities through explicit application-owned Prisma clients, transactions, and managed schema fragments.

Implemented in source; check the exact release before adopting.

## The application owns the client

Prisma adapters receive an existing client or transaction handle. Playstack does not create hidden global connections or take ownership of application migrations.

## Keep transactions aligned

Capabilities that require atomic state and Tier 1 events expose transaction-kind requirements so persistence and event delivery can commit together.

## Materialize package-owned schema

Packages with managed Prisma fragments publish database-schema artifacts alongside structurally typed persistence adapters. `playstack database sync` copies those fragments into the application's named Prisma target; the application reviews the result, owns migrations, and supplies its existing client or transaction at runtime.

The current commerce and licensing schema surfaces include catalog, payments, inventory, commerce orders, fulfillment, and license issuance. Run `playstack database compatibility` before synchronization when an existing application schema may already satisfy those contracts.

## Preserve supported application versions

Playstack supports multi-file schema artifacts with Prisma `>=6.7.0 <8`. Prisma 6 and Prisma 7 have separate verification coverage, and adoption preserves the application's installed major, generator provider, client output, and migration ownership. A version change is an explicit application operation rather than a side effect of adding a capability.

Compatibility checks inspect the complete datasource-bearing schema folder. An empty or nested target that excludes the configured schema entry fails closed instead of reporting a false compatible result.

## Packages

- [@playstack/accounts](/docs/packages/identity/accounts)
- [@playstack/ai](/docs/packages/content-intelligence/ai)
- [@playstack/api-keys](/docs/packages/identity/api-keys)
- [@playstack/audiences](/docs/packages/communication/audiences)
- [@playstack/audit](/docs/packages/identity/audit)
- [@playstack/auth](/docs/packages/identity/auth)
- [@playstack/billing](/docs/packages/identity/billing)
- [@playstack/catalog](/docs/packages/commerce/catalog)
- [@playstack/commerce](/docs/packages/commerce/commerce)
- [@playstack/connections](/docs/packages/identity/connections)
- [@playstack/delivery-history](/docs/packages/communication/delivery-history)
- [@playstack/devices](/docs/packages/identity/devices)
- [@playstack/entitlements](/docs/packages/identity/entitlements)
- [@playstack/event-store](/docs/packages/events-operations/event-store)
- [@playstack/events-prisma](/docs/packages/events-operations/events-prisma)
- [@playstack/fulfillment](/docs/packages/commerce/fulfillment)
- [@playstack/inventory](/docs/packages/commerce/inventory)
- [@playstack/licensing-issuer](/docs/packages/identity/licensing-issuer)
- [@playstack/mfa](/docs/packages/identity/mfa)
- [@playstack/notifications](/docs/packages/communication/notifications)
- [@playstack/payments](/docs/packages/commerce/payments)
- [@playstack/prisma-runtime](/docs/packages/foundation/prisma-runtime)
- [@playstack/rate-limit-prisma](/docs/packages/events-operations/rate-limit-prisma)
- [@playstack/webhooks](/docs/packages/communication/webhooks)

## Redis

Source: https://playstack.dev/integrations/redis.md

Use Redis for distributed caches, rate-limit counters, and BullMQ-backed jobs while keeping application policy portable.

Implemented in source; check the exact release before adopting.

## Storage, not policy

Redis adapters provide atomic cache leases and tag generations, atomic rate-limit counters, and the connection boundary used by BullMQ. Each adapter exposes its client while namespaces, limits, retry policy, and job behavior remain in the corresponding Playstack package.

Keep queue storage on a `noeviction` Redis instance rather than sharing it with an evictable application cache. Connection ownership, health checks, and shutdown remain application responsibilities.

## Packages

- [@playstack/cache](/docs/packages/infrastructure/cache)
- [@playstack/queues](/docs/packages/infrastructure/queues)
- [@playstack/rate-limit-redis](/docs/packages/events-operations/rate-limit-redis)

## Postmark

Source: https://playstack.dev/integrations/postmark.md

Deliver rendered Playstack mail or Postmark provider templates through an explicit driver and native client escape hatch.

Implemented in source; check the exact release before adopting.

## Rendered mail and provider templates

The Postmark driver supports portable rendered messages and provider-owned template IDs plus exact JSON models. Configure separate clients when message streams need different operational policies.

## Unknown delivery outcomes

The adapter distinguishes provider rejection, unavailability and `MailOutcomeUnknownError`. A lost send response can mean the message was accepted; do not classify every network failure as safe to retry. Configure production preflight and reconcile uncertain sends through application-owned delivery records. Capture tests are not proof of production deliverability.

## Packages

- [@playstack/mail](/docs/packages/infrastructure/mail)

## SMTP

Source: https://playstack.dev/integrations/smtp.md

Deliver portable rendered email through an application-configured Nodemailer transport.

Implemented in source; check the exact release before adopting.

## Bring the transport

The SMTP driver receives an application-configured Nodemailer transport, exposes verify, and disables filesystem and URL attachment loading on the portable path.

## Packages

- [@playstack/mail](/docs/packages/infrastructure/mail)

## Resend

Source: https://playstack.dev/integrations/resend.md

Deliver transactional mail and synchronize consent-aware audience contacts through explicit Resend adapters.

Implemented in source; check the exact release before adopting.

## Rendered mail and provider templates

The Resend adapter delivers portable rendered messages and provider-owned templates through `@playstack/mail/resend`. Template variables are validated against Resend's string-and-number boundary before provider I/O, while the native client remains available for provider-specific capabilities.

## Configure the adapter

```ts
import { createMail } from '@playstack/mail'
import { createResendMailProvider } from '@playstack/mail/resend'

const mail = createMail({
  driver: createResendMailProvider(process.env.RESEND_API_KEY!),
  from: { address: 'hello@example.com', name: 'Example' },
})
```

Rendered messages, attachments, custom headers, and provider templates share the same portable mail contract. Applications remain responsible for queueing, retry idempotency, and delivery-event workflows.

## Synchronize audience contacts

`@playstack/audiences-resend` maps Playstack's subscription, consent, confirmation, suppression, and unsubscribe lifecycle onto Resend Audiences contacts. It can treat Resend as a remote projection while Playstack remains the application authority, or participate in a deliberate remote-authority workflow.

Transactional mail and audience membership remain separate contracts even when both use the same Resend account and client.

## Packages

- [@playstack/mail](/docs/packages/infrastructure/mail)
- [@playstack/audiences-resend](/docs/packages/communication/audiences-resend)

## Inngest

Source: https://playstack.dev/integrations/inngest.md

Dispatch portable Playstack queue jobs through Inngest and serve the generated functions from Next.js, NestJS, or another supported framework.

Implemented in source; check the exact release before adopting.

## Portable jobs, managed execution

`@playstack/queues/inngest` maps typed Playstack jobs onto namespaced Inngest events and generates a serve-ready Inngest function. Payload validation, retry policy, terminal-failure hooks, and trace propagation stay in the portable queue contract; concurrency, throttling, priorities, and workflows remain available through Inngest.

```ts
import { Inngest } from 'inngest'
import { createQueue } from '@playstack/queues'
import {
  createInngestQueueDriver,
  createInngestQueueFunction,
} from '@playstack/queues/inngest'

export const inngest = new Inngest({ id: 'billing' })

const driver = createInngestQueueDriver({
  client: inngest,
  app: 'billing',
  environment: 'production',
  queue: 'emails',
})

export const emails = createQueue('emails', {
  app: 'billing',
  environment: 'production',
  driver,
})

export const emailJobs = createInngestQueueFunction({
  client: inngest,
  app: 'billing',
  environment: 'production',
  queue: 'emails',
  jobs,
  concurrency: 20,
})
```

## Serve from Next.js or NestJS

The generated function works with Inngest's official framework adapters. A Next.js App Router endpoint can export the handlers from `inngest/next`; a NestJS application can mount `inngest/express` on its HTTP adapter. The serving edge changes without changing the job definition or its application dependencies.

Inngest supports delayed jobs, bounded retries, fixed and exponential backoff, repeatable work, priorities, rate limiting, and flows. Custom Playstack `jobId` values are rejected because Inngest's 24-hour event deduplication does not satisfy the portable queue-scoped receipt contract. Keep side effects idempotent at the handler boundary.

See the [`@playstack/queues` guide](/docs/packages/infrastructure/queues) for complete Next.js and NestJS endpoint examples.

## Packages

- [@playstack/queues](/docs/packages/infrastructure/queues)

## Fathom Analytics

Source: https://playstack.dev/integrations/fathom.md

Send consent-aware pageviews and named events to an application-initialized Fathom client.

Implemented in source; check the exact release before adopting.

## Map the supported analytics surface

`@playstack/analytics/fathom` maps portable pageviews and named events onto an initialized Fathom client. A numeric `value` property becomes Fathom's `_value`; other event properties are intentionally not claimed by this adapter.

```ts
import { configureAnalytics } from '@playstack/analytics'
import { fathom } from '@playstack/analytics/fathom'

const analytics = configureAnalytics({
  drivers: [fathom(fathomClient)],
})
```

The adapter is structural and does not bundle or initialize the vendor SDK. Fathom-specific operations remain available through `analytics.driver('fathom').client`.

## Packages

- [@playstack/analytics](/docs/packages/events-operations/analytics)

## Plausible Analytics

Source: https://playstack.dev/integrations/plausible.md

Send pageviews and custom events with non-null properties through an application-initialized Plausible client.

Implemented in source; check the exact release before adopting.

## Preserve portable event properties

`@playstack/analytics/plausible` maps Playstack pageviews to Plausible's `pageview` event and forwards non-null primitive custom properties for page and named-event calls.

```ts
import { configureAnalytics } from '@playstack/analytics'
import { plausible } from '@playstack/analytics/plausible'

const analytics = configureAnalytics({
  drivers: [plausible(plausibleClient)],
})
```

The application initializes the Plausible client and owns durable consent evidence. Provider-only behavior remains accessible through `analytics.driver('plausible').client`.

## Packages

- [@playstack/analytics](/docs/packages/events-operations/analytics)

## Basemarks

Source: https://playstack.dev/integrations/basemarks.md

Adapt Basemarks track events with explicit property mapping; supply an application-owned transport for error reporting.

Implemented in source; check the exact release before adopting.

## Share normalized operational context

The analytics adapter supports the published SDK's `track()` contract with string-valued properties. It does not advertise page, identify or group support. By default, non-string property values reject the entire event; supply an explicit `mapProperties` policy if the application needs conversion. There is no silent stringification or unsupported-method no-op.

```ts
import { basemarks as basemarksAnalytics } from '@playstack/analytics/basemarks'
import { configureAnalytics } from '@playstack/analytics'
import { basemarksErrorBridge } from '@playstack/errors/basemarks'
import { configureErrors } from '@playstack/errors'

const analytics = configureAnalytics({
  drivers: [basemarksAnalytics(basemarksClient)],
})

const errors = configureErrors({
  drivers: [basemarksErrorBridge(applicationErrorTransport)],
  environment: 'production',
  release: 'storefront@2.4.0',
})
```

`applicationErrorTransport` must implement `captureError(error, normalizedEvent)`; it is not the Basemarks web SDK, which does not expose that method. The bridge defaults to no fingerprint/trace guarantee. Only declare capabilities the transport actually implements.

Analytics consent defaults to required. Its gate covers Playstack calls, not the SDK's automatic instrumentation. SDK initialization, consent policy, transport lifecycle and duplicate-report prevention stay application-owned. The adapter is written against the Basemarks track-only SDK and imports cleanly under SSR and browser bundling; verify live telemetry in your own Basemarks account.

## Packages

- [@playstack/analytics](/docs/packages/events-operations/analytics)
- [@playstack/errors](/docs/packages/foundation/errors)

## Sentry

Source: https://playstack.dev/integrations/sentry.md

Report scrubbed Playstack errors through an initialized Sentry client without hiding SDK configuration or instrumentation.

Implemented in source; check the exact release before adopting.

## Initialize Sentry at the application edge

`@playstack/errors/sentry` maps normalized severity, tags, context, opaque user and account IDs, fingerprint hints, and W3C trace metadata onto a structural Sentry client.

```ts
import { configureErrors } from '@playstack/errors'
import { sentry, sanitizeSentryEvent } from '@playstack/errors/sentry'

// Install in every SDK runtime (browser, server and edge), after any host hook:
// Sentry.init({ dsn, beforeSend: (event) => sanitizeSentryEvent(event) })

const errors = configureErrors({
  drivers: [sentry(initializedSentry)],
  environment: 'production',
  release: 'storefront@2.4.0',
})
```

The adapter does not initialize Sentry, resolve a DSN, configure source maps, or install automatic instrumentation. Those provider choices and the exact native client remain visible to the application.

The final `beforeSend` sanitizer also covers SDK-captured exceptions and inherited
metadata, not just calls through the Playstack reporter. It drops sensitive keys,
nested user/request payloads and nonopaque user IDs, and strips query/fragment
data from absolute and relative URL fields. `sendDefaultPii: false` alone is not
that filtering policy. Choose genuinely opaque IDs and review host-specific
data; logs, profiles, replay and attachments need separate policies.

## Packages

- [@playstack/errors](/docs/packages/foundation/errors)

## Mailgun

Source: https://playstack.dev/integrations/mailgun.md

Deliver rendered messages or stored Mailgun templates through an explicit regional provider client.

Implemented in source; check the exact release before adopting.

## Select the provider region explicitly

`@playstack/mail/mailgun` supports rendered messages, attachments, custom headers, and stored templates with exact JSON variables. Its fetch-based client accepts an explicit API base URL for Mailgun's US or EU infrastructure.

```ts
import { createMail } from '@playstack/mail'
import { createMailgunMailProvider } from '@playstack/mail/mailgun'

const mail = createMail({
  driver: createMailgunMailProvider(
    process.env.MAILGUN_API_KEY!,
    'mg.example.com',
    { url: 'https://api.eu.mailgun.net' },
  ),
  from: 'hello@example.com',
})
```

The portable boundary validates envelopes before provider I/O. Mailgun's native client remains accessible for provider-specific behavior.

## Packages

- [@playstack/mail](/docs/packages/infrastructure/mail)

## SendGrid

Source: https://playstack.dev/integrations/sendgrid.md

Deliver rendered messages and dynamic templates through an isolated SendGrid mail client.

Implemented in source; check the exact release before adopting.

## Keep credentials instance-scoped

`@playstack/mail/sendgrid` supports rendered messages, attachments, and dynamic templates with exact JSON data. Each provider factory creates an isolated SendGrid `MailService`, avoiding the SDK's process-wide singleton for API-key configuration.

```ts
import { createMail } from '@playstack/mail'
import { createSendGridMailProvider } from '@playstack/mail/sendgrid'

const mail = createMail({
  driver: createSendGridMailProvider(process.env.SENDGRID_API_KEY!),
  from: 'hello@example.com',
})
```

The native client remains available through `mail.client`; queueing, idempotency, and delivery feedback remain separate application workflows.

## Packages

- [@playstack/mail](/docs/packages/infrastructure/mail)

## Amazon SES

Source: https://playstack.dev/integrations/amazon-ses.md

Deliver rendered messages and SES templates through an application-configured AWS SDK client.

Implemented in source; check the exact release before adopting.

## Use the AWS credential boundary you already own

`@playstack/mail/ses` supports rendered messages, attachments, custom headers, and SES templates. Application template models cross an exact JSON boundary before becoming provider template data.

```ts
import { createMail } from '@playstack/mail'
import { createSesMailProvider } from '@playstack/mail/ses'

const mail = createMail({
  driver: createSesMailProvider(
    { region: 'us-east-1' },
    { configurationSetName: 'transactional' },
  ),
  from: 'hello@example.com',
})
```

AWS credentials use the SDK's standard provider chain, and the exact `SESv2Client` remains available through `mail.client`.

## Packages

- [@playstack/mail](/docs/packages/infrastructure/mail)

## Cloudflare Queues

Source: https://playstack.dev/integrations/cloudflare-queues.md

Typed Worker jobs with per-message acknowledgements, bounded batches, retries and explicit terminal-failure handling.

Preview composition; review its documented limitations before adopting.

The optional `@playstack/queues/cloudflare` entry point adapts structural Worker queue bindings. No BullMQ, Redis, Inngest or Nest dependency is required.

```sh
playstack add queues --adapter=cloudflare --dry-run
```

Use `createCloudflareQueueDriver` for producers and `createCloudflareQueueHandler` for the Worker batch boundary. The handler preserves an explicitly reviewed physical queue name independently of Playstack's logical app/environment/queue namespace. The package publishes Worker wiring and negative-case test templates; review binding mappings, payload cutover, retry budgets and dead-letter configuration before generation.

## Delivery contract

Successful messages are acknowledged individually; a failed sibling can retry without redelivering acknowledged work. Malformed, non-retryable or exhausted messages require a durable, idempotent `onTerminalFailure` callback before acknowledgement. A failed quarantine callback retries instead. Configure provider retry/dead-letter headroom for repeated quarantine failure.

`enqueueBatch` accepts 1–100 validated enqueue requests, with conservative JSON limits of 128,000 UTF-8 bytes per envelope and 250,000 combined. Provider framing may impose a tighter limit. Delays round up to seconds and are bounded to 24 hours. Receipts expose a logical dispatch ID, not a provider message ID; delivery remains at least once. No automatic resend hides an ambiguous producer result.

Custom job IDs, durable progress/events, scheduling, inspection, cancellation, priorities, rate limiting and flows are not supported by this adapter. Unsupported operations fail explicitly. Product idempotency and quarantine retention remain application-owned. Existing raw queue payloads are not automatically recognized as Playstack envelopes.

The adapter is exercised on Node and workerd with a local Miniflare producer and consumer; verify your own Cloudflare deployment's bindings and limits before relying on it in production. See the [Queues guide](/docs/packages/infrastructure/queues).

## Packages

- [@playstack/queues](/docs/packages/infrastructure/queues)

## GitHub

Source: https://playstack.dev/integrations/github.md

Connect OAuth credentials, GitHub App installation tokens, and verified webhook delivery through explicit Playstack boundaries.

Implemented in source; check the exact release before adopting.

## Choose the GitHub identity you need

`@playstack/connections/providers/github` stores refreshable OAuth App credentials for acting on a user's or account's behalf. `@playstack/github-app` creates scoped, short-lived installation tokens for application automation. They solve different identity problems and can be used independently.

```ts
import { githubConnectionProvider } from '@playstack/connections/providers/github'
import { createGitHubApp } from '@playstack/github-app'

const github = githubConnectionProvider({
  clientId: env.GITHUB_CLIENT_ID,
  clientSecret: env.GITHUB_CLIENT_SECRET,
})

const githubApp = createGitHubApp({
  appId: env.GITHUB_APP_ID,
  privateKey: env.GITHUB_APP_PRIVATE_KEY,
  crypto: githubJwtSigner,
  client: githubInstallationClient,
  cache,
  clock,
})
```

## Verify lifecycle events before acting

`githubWebhookProvider()` from `@playstack/webhooks` verifies the exact raw request body and `X-Hub-Signature-256`, then normalizes the delivery ID and event name. The GitHub App service can consume verified installation deletion or suspension events to invalidate cached credentials.

Repository membership, team policy, reconciliation state, and the full Octokit client remain application-owned. The Playstack packages establish credentials and trusted input without pretending that every product has the same access model.

## Packages

- [@playstack/connections](/docs/packages/identity/connections)
- [@playstack/github-app](/docs/packages/identity/github-app)
- [@playstack/nest-github-app](/docs/packages/identity/nest-github-app)
- [@playstack/webhooks](/docs/packages/communication/webhooks)

## Stripe

Source: https://playstack.dev/integrations/stripe.md

Create hosted subscription and one-time checkout, then reconcile verified Stripe events into separate billing and payment projections.

Implemented in source; check the exact release before adopting.

## Keep the provider as billing source of truth

`@playstack/billing/stripe` maps configured Playstack plans to Stripe prices, creates hosted checkout and customer-portal sessions, and normalizes provider subscription state into the portable billing projection.

```ts
import { createBilling, definePlans } from '@playstack/billing'
import Stripe from 'stripe'
import { createStripeBillingClient, stripeBilling } from '@playstack/billing/stripe'

const stripe = createStripeBillingClient(Stripe, {
  apiKey: stripeSecretKey, clock,
  httpClient: Stripe.createNodeHttpClient(),
  configuration: { maxNetworkRetries: 2, timeout: 20_000 },
})
const billing = createBilling({
  provider: stripeBilling(stripe, { instance: 'subscriptions', clock }),
  persistence,
  entitlements,
  entitlementConsistency: 'transactional',
  events,
  clock,
  plans: definePlans({
    pro: {
      prices: {
        monthly: {
          provider: 'stripe',
          instance: 'subscriptions',
          id: 'price_pro_monthly',
        },
      },
      entitlements: [{ entitlement: 'packages.pro', value: true }],
    },
  }),
})
```

Checkout and portal creation belong behind authenticated application routes. Plan names remain application configuration, while product code authorizes stable entitlements rather than Stripe price or subscription identifiers.

## Verify webhooks before reconciliation

`stripeWebhookProvider()` from `@playstack/webhooks` verifies the exact raw body, signature, timestamp tolerance, and provider event ID before the application passes a normalized event to `billing.handleWebhook()`. The billing projection compares both event ID and provider occurrence time, so exact retries and delayed older events cannot replace newer state. Reconciliation is safe to repeat; unverified request bodies never enter billing policy.

Stripe invoice payment and payment failure, cancellation and resumption,
scheduled plan transitions, and provider trial-ending signals become
provider-neutral Playstack events. Current subscription queries, immutable
revision history, and explicit reconciliation support billing settings and
operator tooling without treating the local projection as authoritative.

The injected Stripe client remains available through `billing.provider.client` for provider-specific operations outside the portable subscription surface. Native Checkout requires the dedicated factory above and uses one guarded POST admission per explicit submission. The original SDK instance and its unrelated operations retain their normal configuration; direct native escape-hatch calls are outside that Checkout guarantee. Custom transports must not retry or follow redirects internally.

## Add one-time purchases independently

`@playstack/payments/stripe` uses hosted Checkout in `payment` mode and projects payments, refunds, and disputes without changing subscription state. `@playstack/catalog` stores the provider price reference for each immutable offer, and `@playstack/commerce` snapshots the resolved terms into an order before checkout begins.

```ts
const payments = createPayments({
  providers: [
    stripePayments({ client: storeStripe, instance: 'store' }),
    stripePayments({ client: euStripe, instance: 'eu' }),
  ],
  persistence,
  events,
  ids,
  clock,
})
```

The configured instance is part of every provider identity, so multiple Stripe accounts cannot exchange customer, price, checkout, or webhook IDs accidentally. Verified one-time events flow to `payments.handleWebhook()`; verified subscription events flow to `billing.handleWebhook()`.

## Qualification and idempotency

Billing checkout uses an explicit durable attempt ID before Stripe I/O, preserving immutable request identity and unknown outcomes. Reconcile and retry the same attempt after ambiguous success; do not automatically issue another checkout. Subscription updates, history and transactional entitlement projection share customer locking.

Meter reporting has its own idempotent usage/claim contract. Product prices, allowances, refund/dispute effects and access grants remain application policy. Raw-body signature verification belongs to Webhooks and the host HTTP composition, not a browser return URL.

## Packages

- [@playstack/billing](/docs/packages/identity/billing)
- [@playstack/entitlements](/docs/packages/identity/entitlements)
- [@playstack/catalog](/docs/packages/commerce/catalog)
- [@playstack/payments](/docs/packages/commerce/payments)
- [@playstack/commerce](/docs/packages/commerce/commerce)
- [@playstack/webhooks](/docs/packages/communication/webhooks)

## Argon2

Source: https://playstack.dev/integrations/argon2.md

Use Playstack's Node.js authentication entrypoint with application-configured Argon2id password hashing.

Implemented in source; check the exact release before adopting.

## Keep password hashing on the server

`@playstack/auth/node` supplies a password hasher backed by the optional `argon2` peer dependency. It uses Argon2id and keeps encoded parameters in the stored verifier so future policy changes can trigger rehashing after a successful sign-in.

```ts
import { argon2id } from '@playstack/auth/node'

const passwords = argon2id({
  memoryCostKiB: 19_456,
  timeCost: 2,
  parallelism: 1,
})
```

The Node-only entrypoint stays separate from the Worker-safe authentication core. Applications own password policy, breach checks, rate limits, and the deployment resources required by their selected hash parameters.

## Packages

- [@playstack/auth](/docs/packages/identity/auth)

## SimpleWebAuthn

Source: https://playstack.dev/integrations/simplewebauthn.md

Verify passkey registration and authentication ceremonies through Playstack's optional Node.js WebAuthn adapter.

Implemented in source; check the exact release before adopting.

## Add standards-level ceremony verification

`@playstack/mfa/webauthn` adapts `@simplewebauthn/server` to Playstack's `PasskeyAdapter`. Playstack retains challenge lifetime and single use, credential uniqueness, sign-count persistence, and the application's exact origin and relying-party expectations.

```ts
import { createMfa } from '@playstack/mfa'
import { simpleWebAuthnPasskeys } from '@playstack/mfa/webauthn'

const passkeys = simpleWebAuthnPasskeys({
  userVerification: 'required',
  residentKey: 'preferred',
})

const mfa = createMfa({
  passkeys,
  rpId: 'app.example.com',
  rpName: 'Example',
  origins: ['https://app.example.com'],
  // Persistence, crypto, events, limits, time, and identifiers stay explicit.
})
```

The adapter defaults user verification to required, prefers discoverable credentials, requests no attestation, and supports multiple exact origins. The portable MFA core remains usable with another standards implementation through the same explicit adapter seam.

## Packages

- [@playstack/mfa](/docs/packages/identity/mfa)

## BullMQ

Source: https://playstack.dev/integrations/bullmq.md

Dispatch and process typed Playstack jobs through BullMQ with explicit Redis ownership and native queue access.

Implemented in source; check the exact release before adopting.

## Preserve portable job contracts

`@playstack/queues/bullmq` maps namespaced Playstack jobs, retries, backoff, delays, custom job IDs, trace context, progress, inspection, cancellation, and schedules onto BullMQ queues and workers.

```ts
import { createQueue } from '@playstack/queues'
import { createBullMqQueueDriver, createBullMqWorker } from '@playstack/queues/bullmq'

const connection = { host: '127.0.0.1', port: 6379 }
const driver = createBullMqQueueDriver({
  app: 'billing',
  environment: 'production',
  queue: 'emails',
  connection,
})

export const emails = createQueue('emails', {
  app: 'billing',
  environment: 'production',
  driver,
})
```

Workers receive an explicit job registry and application dependency factory. The portable control plane covers progress subscriptions, bounded job inspection, counts, inactive cancellation, and interval or cron schedules. BullMQ-native priorities, flows, rate limits, and advanced worker settings remain accessible through the exposed client.

`@playstack/nest-queues` adds named queue injection, handler discovery, and optional worker lifecycle management without hiding BullMQ's native producer, worker, or control connections.

BullMQ versions `>=5.16.0 <7` are supported. The CLI validates the declared consumer range instead of treating any installed BullMQ version as compatible.

Use a Redis instance configured with `maxmemory-policy noeviction`; do not share durable queue state with an evictable cache. The application owns connection health, worker deployment, and shutdown.

## Existing queues and strict readiness

BullMQ `>=5.16 <7` is supported; selected monitoring integrations retain their own peer restrictions. `assertBullMqRedisNoEviction(queue.client)` is a fail-closed startup/readiness check, distinct from the non-blocking dispatch observer. It fails if the policy is incompatible or cannot be verified.

The optional [Queuebert bridge](/integrations/queuebert) preserves reviewed native queues and processor statistics. Set a per-queue `expectedPrefix` for an existing physical namespace instead of renaming queues. The bridge does not start workers or authorize administrative HTTP. A job/processor migration still needs drain, cutover and rollback decisions.

## Packages

- [@playstack/queues](/docs/packages/infrastructure/queues)
- [@playstack/nest-queues](/docs/packages/infrastructure/nest-queues)
- [@playstack/nest-event-store](/docs/packages/events-operations/nest-event-store)

## AT Protocol

Source: https://playstack.dev/integrations/at-protocol.md

Use the official AT Protocol OAuth client while keeping discovery, PAR, PKCE, DPoP, nonces, and session storage intact.

Implemented in source; check the exact release before adopting.

## Preserve the protocol

`@playstack/atproto` wraps the official OAuth client rather than flattening AT Protocol into a conventional access-and-refresh-token driver. The official client retains discovery, PAR, PKCE, DPoP keys and nonces, rotating refresh tokens, and its durable state and session stores.

```ts
import { NodeOAuthClient } from '@atproto/oauth-client-node'
import { createAtprotoConnectionClient } from '@playstack/atproto'

const atproto = createAtprotoConnectionClient({
  client: new NodeOAuthClient(configuration),
})
```

Playstack exposes the stable DID as a provider identity for application connection records. Authenticated XRPC continues through the restored session's `fetchHandler`, so bearer tokens and DPoP private keys are not copied into the generic Connections credential model.

Node 20 applications use `@atproto/oauth-client-node` 0.3.x. Node 22 and newer can use the verified 0.3.x through 0.5.x range; the CLI selects the compatible range from the active runtime.

## Packages

- [@playstack/atproto](/docs/packages/identity/atproto)
- [@playstack/connections](/docs/packages/identity/connections)

## Cloudflare Workers KV

Source: https://playstack.dev/integrations/cloudflare-kv.md

Use an application-supplied Workers KV binding for namespaced cache storage with explicitly eventual capabilities.

Implemented in source; check the exact release before adopting.

## Make consistency characteristics visible

`@playstack/cache/cloudflare-kv` implements the portable cache contract over a structural Workers KV binding. KV cannot provide an atomic distributed lock and propagates writes eventually, so the driver declares process-local stampede protection and eventual tag invalidation.

```ts
import { createCache } from '@playstack/cache'
import { CloudflareKvCacheStorage } from '@playstack/cache/cloudflare-kv'

const cache = createCache({
  app: 'billing',
  environment: 'production',
  driver: new CloudflareKvCacheStorage(env.CACHE),
})
```

Logical millisecond expiry travels with the stored value while KV's provider expiration handles physical cleanup. The exact binding remains exposed through the driver client.

## Packages

- [@playstack/cache](/docs/packages/infrastructure/cache)

## Cloudflare R2

Source: https://playstack.dev/integrations/cloudflare-r2.md

Store portable Playstack objects through a structural R2 binding with explicit public and signed URL capabilities.

Implemented in source; check the exact release before adopting.

## Use the native Worker binding

`@playstack/storage/r2` accepts a structural R2 bucket, so importing the adapter does not require ambient Cloudflare types. Object keys, metadata, prefixes, streaming reads, and capability checks remain portable.

```ts
import { createStorage } from '@playstack/storage'
import { R2StorageProvider } from '@playstack/storage/r2'

const storage = createStorage({
  driver: new R2StorageProvider(env.ASSETS, {
    publicBaseUrl: 'https://assets.example.com',
  }),
})
```

The Workers binding handles object operations but does not mint presigned URLs. Applications that require signed access supply a signer backed by R2's S3-compatible credentials. Bucket lifecycle and provider administration remain native operations.

## Packages

- [@playstack/storage](/docs/packages/infrastructure/storage)
- [@playstack/feeds](/docs/packages/content-intelligence/feeds)

## MiniSearch

Source: https://playstack.dev/integrations/minisearch.md

Back Playstack's product-defined search protocol with a compact in-memory full-text index.

Implemented in source; check the exact release before adopting.

## Keep provider choice behind the engine

`@playstack/search-minisearch` maps product records and weighted fields into MiniSearch while preserving Playstack's typed query, filter, result, grouping, cancellation, and error contracts.

The application still defines which fields are searchable, what may be returned, and how authorization or tenancy affects a request. Switching from the built-in lexical engine to MiniSearch does not change the Next.js, NestJS, or HTTP protocol consumed by the UI.

Use the native MiniSearch instance for provider-specific tuning that is not part of the portable contract.

## Packages

- [@playstack/search](/docs/packages/content-intelligence/search)
- [@playstack/search-minisearch](/docs/packages/content-intelligence/search-minisearch)

## Amazon S3

Source: https://playstack.dev/integrations/amazon-s3.md

Store portable Playstack objects and issue bounded signed URLs through an application-configured AWS SDK client.

Implemented in source; check the exact release before adopting.

## Bring the AWS configuration

`@playstack/storage/s3` uses the optional AWS SDK peers for streaming object operations and Signature Version 4 download or upload URLs.

```ts
import { createStorage } from '@playstack/storage'
import { createS3StorageProvider } from '@playstack/storage/s3'

const storage = createStorage({
  driver: createS3StorageProvider(
    { region: 'us-east-1' },
    { bucket: 'product-assets' },
  ),
})
```

Signed operations declare an expiry and, for uploads, any content headers the caller must send exactly. Multipart uploads, bucket lifecycle, and other provider-specific operations remain available through `storage.client` rather than entering the portable contract.

## Packages

- [@playstack/storage](/docs/packages/infrastructure/storage)

## Zod

Source: https://playstack.dev/integrations/zod.md

Reuse Zod schemas across Playstack content, React forms, wire validation, and NestJS request boundaries.

Implemented in source; check the exact release before adopting.

## Share schemas without hiding the validator

`@playstack/content/zod` supplies reusable content fields, while `@playstack/validation` re-exports Zod and adds stable parsing, wire-error, React-form, and NestJS boundaries.

```ts
import { contentBase } from '@playstack/content/zod'
import { parse, z } from '@playstack/validation'

const articleSchema = z.object({
  ...contentBase,
  body: z.string().min(1),
})

const article = parse(articleSchema, input)
```

Applications still own their schemas and may use the native Zod APIs directly. Playstack adds consistent error identity and framework bridges rather than introducing a separate schema language.

## Packages

- [@playstack/content](/docs/packages/content-intelligence/content)
- [@playstack/validation](/docs/packages/foundation/validation)

## Chakra UI

Source: https://playstack.dev/integrations/chakra-ui.md

React DOM themes and application compositions on Chakra v3.

Preview composition; review its documented limitations before adopting.

Use the three-package [UI stack](/features/application-ui) with Chakra 3, Emotion 11 and React/React DOM 18.2 or 19. Neutral and warm presets support application overrides; color-mode and font policy stay in the host. Construct the function-bearing system in a client-owned module for Next.

App UI is an application composition layer, not a documentation renderer, and there is no React Native renderer. Verify browser and assistive-technology behavior in your own product.

## Packages

- [@playstack/chakra](/docs/packages/ui/chakra)
- [@playstack/app-ui](/docs/packages/ui/app-ui)
- [@playstack/admin-ui](/docs/packages/ui/admin-ui)

## Queuebert

Source: https://playstack.dev/integrations/queuebert.md

Connect existing named BullMQ queues to an application-authorized Queuebert installation.

Preview composition; review its documented limitations before adopting.

The optional `@playstack/nest-queues/queuebert` bridge exposes existing native queues and stable logical statistics keys. It does not create another queue, start workers or authorize the admin endpoint.

An explicit per-registration `expectedPrefix` preserves an existing physical Redis namespace. The default is the canonical app/environment prefix. Do not rename a queue prefix to make monitoring work: that selects different jobs. Retain processor/statistics compatibility and Queuebert's own peer ranges and admin guard. See [BullMQ](/integrations/bullmq).

## Packages

- [@playstack/nest-queues](/docs/packages/infrastructure/nest-queues)
- [@playstack/queues](/docs/packages/infrastructure/queues)

## Relaypath

Source: https://playstack.dev/integrations/relaypath.md

Optional Relaypath-backed outbound webhook dispatch without making it a required transport.

Preview composition; review its documented limitations before adopting.

Use `@playstack/webhooks/relaypath` when Relaypath owns remote delivery. The same outbound contract can use `/direct`, or `/durable` with application queues and persistence. Omitting the outbound integration leaves the product event handler unregistered.

Applications choose destinations, authentication, signing policy, payload minimization and retries. Provider acceptance is not proof that Slack, Discord or a downstream consumer processed the message. See [webhooks](/features/webhooks).

## Packages

- [@playstack/webhooks](/docs/packages/communication/webhooks)

## OAuth 2 providers

Source: https://playstack.dev/integrations/oauth2.md

Configurable OAuth 2 credential lifecycle for provider APIs, separate from application login.

Preview composition; review its documented limitations before adopting.

The `@playstack/connections/providers/oauth2` seam supports explicit provider endpoints and normalization, with dedicated GitHub, LinkedIn, Mastodon and X adapters. Applications own approved origins, scopes, consent and business API operations.

This is third-party credential lifecycle, not implicit sign-in or account linking. AT Protocol uses the separate DPoP-aware [AT Protocol adapter](/integrations/at-protocol), not a conventional token-pair substitute.

## Packages

- [@playstack/connections](/docs/packages/identity/connections)

## LinkedIn

Source: https://playstack.dev/integrations/linkedin.md

A packaged LinkedIn connection adapter with application-owned scopes and product operations.

Preview composition; review its documented limitations before adopting.

Compose `@playstack/connections/providers/linkedin` with the Connections encrypted persistence and lifecycle service. Provider credentials, approved permissions, callback routing and all publishing/business operations remain application-owned.

This is not a promise that every LinkedIn product API or permission is enabled. Follow [Connections](/features/connections) and verify the selected application's live provider setup.

## Packages

- [@playstack/connections](/docs/packages/identity/connections)

## Mastodon

Source: https://playstack.dev/integrations/mastodon.md

A packaged Mastodon connection adapter with explicit instance and credential ownership.

Preview composition; review its documented limitations before adopting.

Use `@playstack/connections/providers/mastodon` with reviewed instance configuration and the Connections lifecycle. The application owns allowed instance policy, callback validation, client registration and social operations.

Do not treat a user-provided server URL as approved network access. Provider normalization and encrypted credentials do not replace application SSRF policy or resource authorization.

## Packages

- [@playstack/connections](/docs/packages/identity/connections)

## X

Source: https://playstack.dev/integrations/x.md

A packaged X OAuth 2 connection adapter with explicit application permissions.

Preview composition; review its documented limitations before adopting.

Use `@playstack/connections/providers/x` for credential lifecycle and normalization. Application registration, scopes, consent, callback routing, provider access requirements and posting/reconciliation stay outside Playstack.

The adapter does not supply the product's social publishing workflow or imply support for all X API products.

## Packages

- [@playstack/connections](/docs/packages/identity/connections)

## IndexedDB

Source: https://playstack.dev/integrations/indexeddb.md

Browser persistence and Web Locks coordination for the local-first engine.

Preview composition; review its documented limitations before adopting.

The published IndexedDB adapter supplies the local transaction boundary and browser coordination used by [Local First](/features/local-first). The product defines entities, mutations, conflicts, transport, migration policy and recovery.

A browser adapter is not a native filesystem bridge. Service-worker wakeups are opportunistic; correctness must survive restarts, lost connectivity and missed hints. Tauri and grouped filesystem-change protocols are not supplied by this package.

## Packages

- [@playstack/local-first](/docs/packages/infrastructure/local-first)
- [@playstack/local-first-indexeddb](/docs/packages/infrastructure/local-first-indexeddb)

## Apple Push Notification service

Source: https://playstack.dev/integrations/apns.md

An APNs delivery seam with application-owned provider credentials and device selection.

Preview composition; review its documented limitations before adopting.

Compose `@playstack/delivery/apns` with device targets and an injected provider transport. The application owns credentials, topics/environments, payload policy, provider feedback and platform background behavior.

Data-only wakeups should carry bounded opaque hints, not document bodies or secrets. Revalidate recipients and process permanent invalid-target outcomes. Provider acceptance is not device receipt; an inbox is not required for sync hints.

## Packages

- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/devices](/docs/packages/identity/devices)

## Firebase Cloud Messaging

Source: https://playstack.dev/integrations/fcm.md

An FCM delivery seam with explicit provider transport and target lifecycle.

Preview composition; review its documented limitations before adopting.

Compose `@playstack/delivery/fcm` with an application-owned sender and encrypted device targets. Supply credentials and response normalization; retain product scheduling, recipient revalidation and invalid-token handling.

Delivery acceptance does not prove background execution or message receipt. Data-only hints and visible notifications have different platform policies; keep documents and secrets out of both.

## Packages

- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/devices](/docs/packages/identity/devices)

## Web Push

Source: https://playstack.dev/integrations/web-push.md

A Web Push delivery seam without requiring an application notification inbox.

Preview composition; review its documented limitations before adopting.

Use `@playstack/delivery/webpush` with an injected transport, browser subscription lifecycle and application-owned signing credentials. Devices can retain encrypted targets; the host owns service-worker registration and permission UX.

Handle expired subscriptions, bounded payloads and retries explicitly. A push is a hint, not durable synchronization authority or proof that a browser processed an event.

## Packages

- [@playstack/delivery](/docs/packages/communication/delivery)
- [@playstack/devices](/docs/packages/identity/devices)

## @playstack/audiences

Source: https://playstack.dev/docs/packages/communication/audiences.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/audiences` owns the durable meaning of an audience: lists, subscribers, per-list subscription edges, append-only consent evidence, tags, and versioned segment definitions. It resolves recipients; it never composes or sends a campaign.

## Install

```sh
npm install @playstack/audiences @playstack/delivery @playstack/events
```

## Compose and subscribe

```ts
import { createAudiences } from '@playstack/audiences'

const audiences = createAudiences({
  persistence,
  suppression: delivery,
  tokens,
  normalizer: { normalize: normalizeEmail },
  abuse: signupAbuseGate,
  segments,
  events,
  clock,
  ids,
})

await audiences.createList({
  key: 'release-notes',
  name: 'Release notes',
  topic: 'release-notes',
  authority: 'local',
  doubleOptIn: true,
})

const result = await audiences.subscribe('release-notes', {
  email: form.email,
  source: 'launch-page',
})
```

The trusted server result contains the token required to send a confirmation. Public controllers must return only a uniform accepted state and must never echo confirmation, preference, or unsubscribe tokens.

## Admit consent before issuing credentials

`createAudienceAdmissions()` is the narrower profile for applications that mint
bearer credentials during claimed worker preparation. It requires no token issuer
or segment adapter. Subscription admission returns state and opaque identifiers;
confirmation returns state without issuing preferences or unsubscribe tokens.
The existing `createAudiences()` convenience APIs retain their token results.

The required confirmation-intent sink joins subscriber state, consent evidence
and an identifier-only durable intent on the same physical transaction. It runs
again for every abuse-admitted pending retry, without duplicating consent evidence.
The sink must enforce its durable cooldown, limits and original-key deduplication;
failure rolls back the entire mutation. Acknowledging admission requires a durable
intent for that enrollment, including when reusing an existing intent. No broker,
provider send or independent root transaction belongs in this callback.

Both operations can join a caller transaction. The minimal persistence contract
must assert that the original handle is live; a matching transaction-kind string
alone is insufficient. Propagate failures so the caller rolls back, and expose
results only after commit.

For affected Prisma/adapter combinations, retain the qualified
[Prisma dispatch fence](/docs/packages/foundation/prisma-runtime). Handle checks
and ordinary rollback tests do not qualify delayed SQL after an unfenced rollback;
this profile does not repair that separate driver behavior.

Confirmation verifies the supplied credential and takes native contact/subscription
locks before running an optional current-proof authorization grant. This is where
a canonical application checks or consumes its original enrollment-bound proof
on the same transaction. Native `consentRevision` fences stale work; it is not a
replacement for the application's confirmation-incarnation authority. Removal
and rejoining must not let an older proof authorize the new enrollment.

A supported composition registers a native Delivery attempt with
`contentBinding: 'per-submission'` in the sink, under the host's enrollment and
admission-policy locks. Its immutable key and logical request hash contain opaque
authority references. An Events outbox wakeup can refer to that intent; workers
recover from the durable attempt even if the wakeup is lost. Only claimed worker
preparation rechecks current authority, mints the credential, renders and binds
the complete message. Preserve the original attempt budget and quarantine
uncertain outcomes without reminting.

Keep source and attempt lock ordering consistent. In this recipe, a pending
retry observes its existing attempt without taking that attempt's lock and
retains the current intent key. Only fresh, unexposed keys are registered while
holding source locks. Re-registering an in-flight key there can invert the
worker's attempt/source lock order; qualify competing admission and preparation.

Native Notifications queue/admission methods own root transactions, so they cannot
be called as the audience write's transactional sink. An application using them
hands off after its durable source intent commits. The encrypted confirmation
producer below is a separate supported profile for credentials issued at admission.

## Current state and paged consent evidence

Current subscriptions carry `consentRevision`, not an embedded lifetime history.
Native subscribe/confirm writes state, immutable evidence and transactional
handoffs together. The Prisma artifact stores evidence separately; canonical
application persistence can implement the same public `appendConsent` and
`listConsent` ports without copying its contact authority into another store.
Ordinary state mutations and subscriber listings do not load the history.

Authorized operators can use
`audiences.listConsent(listKey, subscriberId, { limit, cursor })` for 1–100 rows.
The cursor binds the list, subscriber and initial revision ceiling; it is not
authorization. Later appends do not extend an in-progress history. The guarded
Nest management binding exposes the same bounded read. Reauthorize every page,
keep raw evidence private, and treat the host admin detail as a bounded preview.

When explicitly approved for export, `consentProvenance` contains
`{ items, nextCursor? }`, with at most 25 initial evidence rows per subscriber.
Continue its cursor with `AudienceExports.consentPage(...)`; it independently
checks current export authority and records a same-transaction safe audit.
Neither contact pagination nor an initial provenance page implies complete export.

This storage change requires reviewed schema activation. Populated installations
must preserve original consent facts, order and generation bindings through an
explicit offline migration/import; no history is fabricated or automatically
backfilled. The repository host migration refuses populated subscriptions before
destructive DDL. Apply and qualify the reviewed transition before new runtimes
start. Erasure counts evidence against its configured cleanup bound and fails
closed rather than claiming that partial cleanup erased a subscriber.

## Configuration reference

| `AudiencesOptions` property | Required | Purpose                                                                                              |
| --------------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `persistence`               | Yes      | Transactional lists, subscribers, subscriptions, tags, and segment queries.                          |
| `suppression`               | Yes      | Topic suppression writer from `@playstack/delivery`; it must share the persistence transaction kind. |
| `tokens`                    | Yes      | Issues and verifies purpose-bound confirmation, preference, and unsubscribe tokens.                  |
| `normalizer`                | Yes      | Applies the application's email normalization policy.                                                |
| `abuse`                     | Yes      | Runs rate-limit, CAPTCHA, honeypot, or other signup policy before mutation.                          |
| `segments`                  | Yes      | Validates and compiles data-only segment definitions where subscriber data lives.                    |
| `remote`                    | No       | Queue and provider authority for remote-owned lists.                                                 |
| `confirmationDelivery`      | No       | Persists encrypted confirmation admission using the actual consent transaction.                       |
| `operatorRemoval`           | No       | Mandatory transaction-bound action authority when enabling soft subscription removal.                 |
| `events`                    | Yes      | Emits list, consent, subscription, and remote-operation lifecycle events.                            |
| `clock`, `ids`              | Yes      | Application-owned time and identifiers.                                                              |
| token TTL properties        | No       | Override confirmation, preference, and unsubscribe token lifetimes.                                  |
| `onTokenError`              | No       | Observes token failures without exposing them to a public response.                                  |

## Bridge contracts

| Contract                     | Boundary                                                                |
| ---------------------------- | ----------------------------------------------------------------------- |
| `AudiencePersistence`        | Transactional audience storage and database-side subscriber queries.    |
| `AudienceTokenIssuer`        | Purpose-bound signed tokens; exposes its application-owned client.      |
| `AudienceEmailNormalizer`    | Explicit normalization without provider-specific alias assumptions.     |
| `AudienceAbuseGate`          | Pre-mutation public signup policy.                                      |
| `AudienceSegmentCompiler`    | Validates and compiles versioned data-only segment trees.               |
| `AudienceRemoteCommandQueue` | Transactional, endpoint-free subscribe and unsubscribe intent.          |
| `AudienceRemoteAuthority`    | Provider-specific segment validation, mutations, and recipient queries. |
| `AudienceConfirmationDelivery` | Transaction-bound encrypted confirmation admission; never inline provider I/O. |

## Durable confirmation mail

Use `AudienceConfirmationOutbox` from `@playstack/audiences/confirmation` and
`PrismaAudienceConfirmationPersistence` from `@playstack/audiences/confirmation/prisma`
to persist encrypted confirmation intents with consent. Explicitly select the
optional `audiences.confirmations.prisma` artifact and apply your reviewed
migration before enabling it. Compose native CryptoSuite, the same audience token
issuer, a fixed deployment namespace, globally unique IDs and current host
`canDeliver` policy. Cooldown, maximum unexpired intents, token TTL and lease
duration are required server configuration; the existing public abuse gate remains.

Schedule bounded `recover(limit)` and `scrubExpired(limit)` batches. Recovery
hands identifier-only references to native Notifications using stable logical
keys. Durable notification admission and the downstream queue must retain those
keys across consumed jobs and lost acknowledgments. A broker's short deduplication
TTL is insufficient. Recovery is not provider delivery or exactly-once email.

The private mail worker uses `resolve(reference)` to recheck current consent,
authority, recipient and proof bindings before rendering. Its transient email/token
result must never reach browser responses, ordinary jobs, admissions, logs or
events. Native Mail/Delivery suppression and encrypted/uncertain-attempt handling
remain required. No public key-ID resolver or identity/account provisioning is
installed. Nest's post-commit `onSubscriptionResult` is not an atomic mail handoff.

For native Delivery attempt preparation, `resolveInTransaction(reference, tx)`
joins those checks to the original caller handle without a nested transaction.
Its transient result is not a committed send grant. Provider I/O must await the
outer commit; native suppression, encrypted content, immutable request hashes,
compatible locks and the check-to-send race still require qualification.

Expired ciphertext is scrubbed in bounded batches; replay IDs remain as tombstones.
This is not full subscriber erasure. Production Mail composition, erasure policy
and actual application/provider qualification remain separate activation gates.

### Compose the durable handoff

The portable `playstack add audiences --adapter=confirmations --dry-run` recipe
reports these obligations without choosing a provider or starting a scheduler:

1. Select the native audience, confirmation, notification admission/queue,
   Delivery attempt and suppression artifacts that the application uses. Review
   and apply migrations first; configure native Crypto, purpose-bound tokens,
   immutable namespaces, IDs, limits and compatible transaction lock ordering.
2. Join encrypted confirmation admission to the original consent write. Return
   uniform public acceptance only after commit. A post-commit callback alone can
   lose mail handoff on a crash and is not this composition.
3. Recover the original opaque reference through durable Notifications admission
   with the same logical key. `PrismaNotificationQueue` and its optional
   `notifications.queue.prisma` artifact retain independent replay receipts after
   completed jobs are deleted. A bounded queue consumer joins Delivery registration,
   protected content and an outbox on its supplied transaction; no provider I/O,
   `notifications.process()` or nested root transaction belongs in that callback.
4. Resolve proof/consent/recipient authority and suppression using the original
   Delivery preparation handle. Bind the approved template, configured callback
   origin, recipient, proof and expiry to the immutable request hash. Keep secrets
   transient or protected, never in ordinary jobs, events or logs.
5. Only a newly committed Delivery begin grants Mail I/O. Unknown outcomes stay
   quarantined; queue replay is not permission to resend. Schedule bounded
   recovery, proof scrubbing, cleanup and shutdown, and qualify actual driver
   fencing and provider cancellation.

Public GET inspection does not mutate consent. Native POST confirmation is not
signup, session issuance or early-access admission. Welcome mail is a separate
approved confirmed-event notification with its own stable key and current
suppression. Preferences/unsubscribe have separate token purposes; do not use a
marketing welcome's transactional label to evade consent. Invitation mail uses
the distinct native invitation lifecycle.

The packaged README contains the maintained sequence and privacy activation
checklist. Local capture-provider/PostgreSQL fixtures exercise the handoff, not
live sender verification, templates, deployed queues, provider delivery or
cross-store erasure. Optional webhooks must not become a signup/mail prerequisite.

The maintained PostgreSQL confirmation fixture additionally composes native Mail
with a capture provider and native Delivery attempts on Prisma 6.19.3/7.9.1.
Rollback/no-send-before-commit, duplicate refusal, changed recipients, current
policy/consent/expiry and suppression checks, and unknown-outcome quarantine pass.
Use `checkSuppressionInTransaction` with the original preparation handle and a
recomputed immutable request hash. This is local fixture evidence, not live email,
rendered templates, deployed queue/scheduler/driver safety or complete erasure.

Remote-authoritative lists enqueue idempotent commands and resolve current subscriber data when a worker executes them. Mirrored authority is deliberately unsupported.

`@playstack/audiences-resend` implements the remote-authority boundary with Resend Contacts and Segments. Playstack retains consent, confirmation, suppression, and idempotent command state while Resend becomes the explicitly selected contact authority. Removing one subscription removes only that segment membership; fresh explicit consent may clear the contact's global unsubscribe state.

## Early-access invitations

`AudienceInvitations` from `@playstack/audiences/invitations` supplies explicit
confirmed-subscriber batch preview/commit, authenticated recipient inspection and
acceptance, operator status/revoke/reissue, encrypted proof intents and stable
identifier-only recovery. Select/migrate `audiences.invitations.prisma` for the
native `/invitations/prisma` adapter. Consent and proof possession do not create an
identity, account or membership. New recipients first complete native Auth signup;
existing recipients need an authenticated active verified identity.

Operator authority, current recipient/activity, suppression/eligibility,
transaction-bound admission and native Events/Audit ports are mandatory. Native
Prisma uses Read Committed and subscriber-before-subscription locking; qualify
compatible host identity/attempt/suppression order. Private
`resolveInTransaction(reference, tx)` joins send preparation, not a send grant.
Await committed begin before external Mail I/O. Reissue invalidates the old proof;
expiry/revocation/acceptance clear ciphertext while replay tombstones remain.

The package installs no invitation routes, automatic signup, mail provider,
scheduler, product entitlement or complete erasure policy. Batches use explicit
IDs, not an automatic priority/segment policy. The explicit CLI
`audiences --adapter=invitations` plan reports those adoption gates.

## Prisma and testing

`@playstack/audiences/prisma` exports `PrismaAudiencePersistence` and `PrismaAudienceSegmentCompiler`. Built-in segment nodes compile to parameterized SQL and application resolvers return predicates with separately supplied values. The managed `audiences.prisma` artifact remains application-migrated.

`@playstack/audiences/testing` supplies deterministic in-memory persistence, token, topic-suppression, segment, queue, and remote-authority fixtures. They are not production persistence or cryptography.

## Operator reads

`listSubscribers(listKey, { status, tag, segmentKey, cursor, limit })` remains
active-only by default; trusted management callers can explicitly query pending,
active, removed or all records. Tag filters intersect stored segments.
`getSubscriber(listKey, subscriberId)` returns list-bound subscription/consent/tag
detail or `null`, not a raw global subscriber lookup. These advanced reads are
local-authoritative; remote providers reject unsupported management filters.

Every read needs current server-side management authority. A deployment operator
waitlist is not customer account administration. Minimize provenance fields in
browser responses and route the identifier-only query/inspection events through
the host's audit composition. Cursors bind list and filters but are not credentials;
reset pagination when changing filters or adopting the current cursor format.
Native Nest management routes expose these reads under their required guard.
Read-only `AudiencesSection`, `AudienceSubscribersTable` and
`AudienceSubscriberDetail` are available from `@playstack/admin-ui`. The host
supplies authorized safe projections and immutable scope/list/query generations;
the section refuses mismatched page/detail responses and gates extension slots.
Fetch cancellation remains host-owned. Export, removal and all-list erasure have
separate opt-in contracts below; the read-only section installs none of them.

## Operator administration and removal

Use native scoped management reads with Admin UI's `AudiencesSection`, subscriber
table/detail and `AudienceInvitationsSection`; the host supplies routes, safe
projections and trusted operator access. Customer account roles do not implicitly
grant deployment-list authority. UI permission hints never replace server checks.

Optional `operatorRemoval` authorizes
`removeSubscription(listKey, subscriberId, { principal, source })` on the exact
write transaction, including missing-edge/replay calls. `principal` is the trusted
server identity and `source` is server-owned provenance. Local removal retains
consent history, applies topic suppression and emits the native removed event;
remote lists retain native unsubscribe/suppression commands until convergence.
This is soft removal, not subscriber erasure or proof issuance. Rejoining needs
fresh native consent, and old invitation consent bindings cannot revive.

Nest exposes the route only with explicit `operatorRemoval` callbacks:
`POST /audiences/subscribers/:subscriberId/remove?list=...` accepts an empty body.
Keep its management guard, per-action native authority, Origin/CSRF and rate policy.
Admin UI's standalone `AudienceSubscriptionRemoval` confirms the action and waits
for authoritative reload; aborting browser waiting does not roll back a server
commit. Export/download and all-list erasure remain separate contracts, not
direct-row deletion buttons.

## Authorized export and all-list erasure

`AudienceExports` from `@playstack/audiences/export` returns bounded live keyset
JSON pages using native persistence and segment queries, without a second contact
store. Required operator/list/field authorization and safe action recording join
the original physical transaction; audit failure withholds the page. Cursors bind
the deployment, export, list, filters and approved fields, not authentication.
Remote lists refuse. The host owns private no-store downloads, whole-export
time/row/byte/rate budgets, output retention and CSV formula escaping if converting.
These pages are not an immutable snapshot and returned data cannot be recalled.

`AudienceSubscriberLifecycle` from `@playstack/audiences/lifecycle` and
`PrismaAudienceLifecyclePersistence` from `/lifecycle/prisma` supply explicit
**all-list** contact erasure for local-authoritative lists. Review the nullable
email/tombstone migration and apply the exported `/lifecycle/constraints.sql`
resource; Prisma does not create that CHECK automatically. Configure every
installed confirmation/invitation producer namespace, current authority over
every affected list and mandatory same-handle before/after cleanup and Audit hooks.
Remote associations refuse; a partial list selection is not erasure authority.

Native erasure clears contact/user links, consent evidence, tags and selected
producer secrets while retaining opaque tombstones, removed edges and replay/
suppression evidence. It is not account deletion, legal anonymity or automatic
cleanup of survey stores, admissions/queues, protected payloads, exported files,
providers, WAL or backups. External cleanup needs durable recovery because repeated
native erasure does not rerun cleanup hooks. An active sender cannot be recalled.
Qualify complaint/bounce policy and fresh enrollment separately: a new subscriber
ID does not automatically inherit old subject-bound suppressions.

Expose `AudienceExport` or `AudienceSubscriberErasure` from Admin UI only after
their independently authorized workflows are qualified. UI hints, soft removal,
receipt deletion or a local `accepted` result do not establish complete erasure.

## Boundary

Fresh consent removes only the exact list suppression retained by the removed subscription. It never clears channel-wide or unrelated list suppressions. Email equality also never links a subscriber to a user; `verifiedUserId` is accepted only through a trusted application call.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/audiences@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/audiences` | `./dist/index.d.ts` |
| `@playstack/audiences/errors` | `./dist/errors.d.ts` |
| `@playstack/audiences/export` | `./dist/export.d.ts` |
| `@playstack/audiences/lifecycle` | `./dist/lifecycle.d.ts` |
| `@playstack/audiences/lifecycle/prisma` | `./dist/lifecycle-prisma.d.ts` |
| `@playstack/audiences/lifecycle/constraints.sql` | No TypeScript declaration (asset or metadata export). |
| `@playstack/audiences/prisma` | `./dist/prisma.d.ts` |
| `@playstack/audiences/confirmation` | `./dist/confirmation.d.ts` |
| `@playstack/audiences/confirmation/prisma` | `./dist/confirmation-prisma.d.ts` |
| `@playstack/audiences/invitations` | `./dist/invitations.d.ts` |
| `@playstack/audiences/invitations/prisma` | `./dist/invitations-prisma.d.ts` |
| `@playstack/audiences/testing` | `./dist/testing.d.ts` |
| `@playstack/audiences/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/audiences/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Configuration

Source: https://playstack.dev/docs/cli/configuration.md

The CLI reads `playstack.json` from the repository root. Commit this file so local work, CI checks, and managed update branches use the same package selections and ownership boundaries.

## Survey before initialization

```sh
npx playstack init --survey
```

Survey mode recognizes root and nested Nx projects, Next Pages and App Router applications, Nest APIs and workers, Redis and BullMQ usage, datasource-bearing Prisma contexts, generated schema derivatives, package-manager state, installed Playstack packages, and likely artifact destinations. It reports proposals without writing files or installing dependencies.

Use `--json` for one machine-readable object with no human prelude:

```sh
npx playstack init --survey --json
```

Run interactive initialization when the proposals look right:

```sh
npx playstack init
```

Initialization confirms every target before writing. Optional package scripts, a `doctor --check` CI step, and configured Skills installation are each proposed and confirmed independently.

### Exclude generated and unrelated directories

Survey discovery skips generated `.app`, `.app.dSYM`, and `.xcarchive` bundles, Rust `target` directories, `.contentlayer`, `.expo`, and `.turbo` output by default. Add repeatable repository-relative directory exclusions when needed:

```sh
npx playstack init --survey --json --exclude='.prismark/**' --exclude='scratch'
```

Patterns use `/` separators and apply to project-root and technology discovery. They cannot re-include generated output or bypass symlink exclusions. Inspect the survey before accepting proposals; discovery is not runtime qualification.

Utility packages do not require a capability recipe. For example, inspect `playstack info @playstack/crypto`, install the package and its dependencies with your existing package manager, and use its supported `/primitives` entry point directly. The CLI does not replace dependency ownership or application composition.

## Configuration reference

```json
{
  "$schema": "./node_modules/@playstack/cli/playstack.schema.json",
  "databases": {
    "primary": {
      "adapter": "prisma",
      "target": "prisma/schema",
      "packages": {
        "@playstack/accounts": {},
        "@playstack/events-prisma": {}
      }
    }
  },
  "emails": { "target": "src/emails/playstack" },
  "content": { "target": "content/playstack" },
  "i18n": { "target": "src/i18n/playstack" },
  "lint": {
    "target": "eslint.playstack.config.mjs",
    "recommended": true,
    "chakra": {
      "files": ["src/**/*.{ts,tsx}"],
      "tokens": ["fg.default", "bg.canvas", "border.default"]
    }
  },
  "skills": {
    "targets": [".agents/skills"],
    "overrides": {
      "event-design": ".playstack-overrides/event-design"
    }
  },
  "updates": {
    "gitHost": "github",
    "schedule": "0 9 * * 1",
    "requiredCheck": "test",
    "autoMerge": false
  }
}
```

| Property                                                | Type                                            | Purpose                                                                                    |
| ------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `$schema`                                               | `string?`                                       | Points editors to the JSON Schema shipped with the installed CLI.                          |
| `databases.<name>.adapter`                              | `prisma \| drizzle`                             | Selects the schema representation accepted by this named target.                           |
| `databases.<name>.dialect`                              | `postgresql?`                                   | Required for Drizzle and omitted for Prisma.                                               |
| `databases.<name>.target`                               | Relative path                                   | Destination for composed database-schema artifacts.                                        |
| `databases.<name>.packages`                             | Package array or package-to-options map         | Selects emitters and optional package variants.                                            |
| `databases.<name>.packages.<package>.artifacts`         | `string[]?`                                     | Exact synchronized artifact IDs; `[]` selects none, omission uses non-optional defaults.   |
| `databases.<name>.packages.<package>.existingArtifacts` | `string[]?`                                     | Declares artifacts that the existing schema must fully satisfy before they can be skipped. |
| `emails`, `content`, `i18n`                             | `{ target, packages? }`                         | Selects an artifact kind, destination, and optional emitter allowlist.                     |
| `lint`                                                  | `{ target, recommended?, extension?, chakra? }` | Configures the managed flat ESLint fragment and scoped presets.                            |
| `skills.targets`                                        | `string[]`                                      | Selects repository-relative destinations for the Skills corpus.                            |
| `skills.overrides`                                      | `Record<skillId, path>?`                        | Shadows packaged guidance with a user-owned directory.                                     |
| `updates.gitHost`                                       | `github`                                        | Pull-request host used by managed updates.                                                 |
| `updates.schedule`                                      | Cron string                                     | Schedule written by `playstack ci init`.                                                   |
| `updates.requiredCheck`                                 | `string`                                        | Existing workflow job required before managed merge.                                       |
| `updates.autoMerge`                                     | `boolean`                                       | Opt-in policy for eligible managed updates.                                                |

## Path and schema guarantees

Every target and override must be a relative path confined to the repository. Absolute paths and parent traversal fail before the CLI writes anything.

For Prisma, `target` must be the complete datasource-bearing schema folder; the
example assumes its datasource is in `prisma/schema`. Setting a target never
creates a database or applies migrations. Select `scope` only when the installed
package manifest declares that variant: Accounts has no `account` variant.
Email/content/i18n target paths do not make a package an emitter. Only explicitly
allowlist packages that publish that artifact kind; Auth does not publish email
templates. The target-only entries above discover compatible installed emitters
and do not configure transactional delivery or author application templates.

Invalid types and malformed values fail configuration loading. Unknown keys warn during the preview period so a typo is visible without blocking forward-compatible experimentation; the published schema supplies editor completion and validation.

Artifact commands can establish one missing target interactively. See [Artifact synchronization](/docs/cli/artifact-synchronization) for that focused workflow.

If exactly one database is configured, `database sync` selects it automatically. With several databases, interactive use prompts for a target and automation must provide `--database=<name>`.

Database package options can select individual artifacts or ask Playstack to verify that application-owned storage already satisfies them:

```json
{
  "databases": {
    "primary": {
      "adapter": "prisma",
      "target": "prisma/schema",
      "packages": {
        "@playstack/accounts": {
          "existingArtifacts": ["accounts.prisma"]
        },
        "@playstack/auth": {
          "artifacts": ["auth.prisma"]
        }
      }
    }
  }
}
```

Unknown, incompatible, duplicate, or overlapping artifact IDs fail before mutation. `existingArtifacts` is fail-closed: a package artifact is skipped only after the existing schema satisfies its physical storage contract.

## Selecting schema artifacts

Selection applies after the database format and any declared variant:

| `artifacts` setting | Synchronized schemas |
| --- | --- |
| Omitted | Compatible non-optional defaults, except IDs selected as existing. |
| `[]` | None; `existingArtifacts` are still validated independently. |
| `["<manifest-id>"]` | Exactly those compatible IDs; include every schema your configured native adapters require. |

Schemas marked `optional: true` are never selected implicitly. Name them in
`artifacts` to synchronize them, or in `existingArtifacts` to verify an existing
application schema. Notifications' inbox, queue and admission schemas are all
independently optional. For a queue-only host that already owns compatible native
queue tables, use:

```json
{
  "databases": {
    "primary": {
      "adapter": "prisma",
      "target": "prisma/schema",
      "packages": {
        "@playstack/notifications": {
          "artifacts": [],
          "existingArtifacts": ["notifications.queue.prisma"]
        }
      }
    }
  }
}
```

`doctor --check` validates that existing queue contract without proposing an inbox
or admission schema. For a new managed queue schema, use
`artifacts: ["notifications.queue.prisma"]` and omit `existingArtifacts`.
An `inApp` notification definition still requires an inbox adapter and compatible
schema; selection does not disable runtime requirements. These rules require the
updated CLI and Notifications artifacts, not merely an unchanged prerelease label.
Selecting fewer schemas neither deletes old managed files nor qualifies the live
database. Application-owned persistence uses the explicit `unmanagedPackages`
boundary; it is not proof of native-schema satisfaction.

Use named `databases` with an explicit `adapter`. A top-level `prisma` object is unsupported and rejected before artifact writes.

## Foundation

Source: https://playstack.dev/docs/packages/foundation.md

Foundation packages establish the contracts every other Playstack capability can share without pulling a framework, database, or vendor toward the application core.

## Packages

| Package                                                                 | Plan                        | Responsibility                                                           |
| ----------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------ |
| [`@playstack/core`](/docs/packages/foundation/core)                     | <PlanTierDot tier="free" /> | Errors, results, clocks, identifiers, and operation context.             |
| [`@playstack/errors`](/docs/packages/foundation/errors)                 | <PlanTierDot tier="free" /> | Runtime-neutral error reporting and safe fan-out.                        |
| [`@playstack/errors-react`](/docs/packages/foundation/errors-react)     | <PlanTierDot tier="pro" />  | React error reporting context and resettable boundaries.                 |
| [`@playstack/nest-errors`](/docs/packages/foundation/nest-errors)       | <PlanTierDot tier="pro" />  | NestJS error reporter injection and exception filtering.                 |
| [`@playstack/validation`](/docs/packages/foundation/validation)         | <PlanTierDot tier="free" /> | Shared schemas and portable HTTP validation errors.                      |
| [`@playstack/crypto`](/docs/packages/foundation/crypto)                 | <PlanTierDot tier="free" /> | Web Crypto operations with explicit key ownership.                       |
| [`@playstack/prisma-runtime`](/docs/packages/foundation/prisma-runtime) | <PlanTierDot tier="pro" />  | Explicit runtime selection for generated Prisma clients.                 |
| [`@playstack/skills`](/docs/packages/foundation/skills)                 | <PlanTierDot tier="free" /> | Versioned repository guidance for agents composing Playstack packages.   |
| [`@playstack/lint`](/docs/packages/foundation/lint)                     | <PlanTierDot tier="free" /> | Deterministic checks for package and application integration boundaries. |

## Start here

Most applications begin with `@playstack/core`. Add validation or crypto only when a capability needs those contracts, and keep framework bindings at the outside edge of the application. Tooling remains independently adoptable: the [CLI](/docs/cli), Skills corpus, and lint rules do not enter the runtime graph.

## Localization previews

The independently adoptable [i18n core](/docs/packages/foundation/i18n), [React binding](/docs/packages/foundation/i18n-react) and [Nest binding](/docs/packages/foundation/nest-i18n) provide locale policy, typed catalogs and application composition. All three are classified Free under the Laravel localization baseline. The [UI localization guide](/docs/frameworks/ui-localization) covers shared English artifacts and host-owned translations.

## Playstack documentation

Source: https://playstack.dev/docs.md

Playstack is a collection of TypeScript packages for assembling production applications without coupling the application core to a single framework, database, queue, or vendor.

Use it to add capabilities such as authentication, billing, email, queues or file storage to an existing application. Start with one package; you do not need to adopt every package or change frameworks.

> The packages are in preview. APIs can still change before the first stable release, and the documentation will call out migration notes as they settle.

## Choose the smallest package

- **New to Playstack?** [Run the storage quickstart](/docs/getting-started#your-first-working-example) to write and read an object without cloud credentials.
- **Choosing a capability?** [Browse packages](/docs/packages) by what you want to build.
- **Integrating an existing app?** Start with [Next.js](/docs/frameworks/nextjs/getting-started), [React](/docs/frameworks/react/getting-started), [NestJS](/docs/frameworks/nestjs/getting-started) or [Workers](/docs/frameworks/workers/getting-started).
- **Looking up an API?** Read the [reference guide](/docs/packages#reading-the-reference), then open the package you use.
- **Preparing a project for registry access?** Learn about [Free accounts, tokens, and upgrades](/docs/registry-access).

Start with the domain package that owns the capability you need. Add a framework adapter such as a `nest-*`, `*-next`, or `*-react` package only at the edge that needs it. Add a persistence adapter only where state must cross a process boundary.

This keeps the dependency direction clear:

```text
application -> framework adapter -> domain contract -> injected infrastructure
```

## What the preview guarantees

- Domain packages expose typed contracts and accept injected infrastructure.
- Commands that produce events can carry operation context for correlation, causation, actors, scopes, and traces.
- Event producers expose explicit bridge seams instead of requiring a particular broker.
- Stateful packages make the database, cache, queue, or storage client boundary visible.
- Standard errors can cross HTTP boundaries without losing their stable code and details.

## Next step

Read [Getting started](/docs/getting-started) for the installation model and a minimal composition example. Then use [Packages](/docs/packages) to choose the capability layer that fits your application.

## @playstack/audiences-react

Source: https://playstack.dev/docs/packages/communication/audiences-react.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/audiences-react` supplies headless state for subscribe forms and token-scoped preference centers. The package root is an explicit Client Component entry point that can sit beneath a Next.js App Router Server Component boundary.

## Install

```sh
npm install @playstack/audiences-react @playstack/audiences react
```

## Compose a subscribe form

```tsx
'use client'

import {
  createAudiencesCache,
  PlaystackAudiencesProvider,
  useAudienceSubscribe,
} from '@playstack/audiences-react'

const cache = createAudiencesCache()

function Subscribe() {
  const subscription = useAudienceSubscribe({ listKey: 'launch' })

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault()
        void subscription.submit(
          new FormData(event.currentTarget).get('email') as string,
        )
      }}
    >
      <input name="email" type="email" />
      <button disabled={subscription.status === 'loading'}>Subscribe</button>
    </form>
  )
}

export function AudienceBoundary() {
  return (
    <PlaystackAudiencesProvider client={applicationClient} cache={cache}>
      <Subscribe />
    </PlaystackAudiencesProvider>
  )
}
```

## Provider configuration

| Property | Required | Purpose |
| --- | --- | --- |
| `client` | Yes | Application-owned subscribe and preference API client. |
| `cache` | Yes | Explicit synchronous `get`/`set`/`subscribe` boundary. |
| `toSafeError` | No | Maps application errors to approved UI copy. |
| `onError` | No | Sends the original error to caller-owned observability. |

`client.client` preserves access to the underlying HTTP or query client. The package does not install a fetch global, authentication, CAPTCHA, routing, or visual styling.

## Hooks and headless components

| API | Purpose |
| --- | --- |
| `useAudienceSubscribe({ listKey, cacheKey? })` | Submits an address, shares in-flight operations, and reports accepted or confirmation-required state. |
| `useAudiencePreferences({ token, cacheKey })` | Loads and optimistically updates token-scoped preferences. |
| `AudienceSubscribe` | Render-prop equivalent of the subscribe hook. |
| `AudiencePreferenceCenter` | Render-prop equivalent of the preferences hook. |

The preference `cacheKey` must be a stable, non-secret identifier. The signed token is intentionally never used as a cache key. Public subscribe responses containing subscriber IDs or signed tokens are rejected as invalid.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/audiences-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/audiences-react` | `./dist/index.d.ts` |
| `@playstack/audiences-react/errors` | `./dist/errors.d.ts` |
| `@playstack/audiences-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/audiences` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/auth

Source: https://playstack.dev/docs/packages/identity/auth.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/auth` owns framework-independent identities, credentials, and opaque session behavior for Node and Workers. It deliberately does not own accounts, authorization, device identity, or MFA.

## Install

```sh
npm install @playstack/auth @playstack/core @playstack/crypto @playstack/events
```

Install the optional `argon2` peer only in the Node process that hashes passwords.

## Compose sessions and credentials

```ts
import { createAuthAttemptLimiter, createCredentialService, databaseSessions } from '@playstack/auth'
import { argon2id } from '@playstack/auth/node'

const sessions = databaseSessions({
  persistence,
  crypto: applicationCrypto,
  events,
  clock,
  ids,
  deviceValidator: devices,
  requireDevice: true,
  reusePolicy: 'all-user-sessions',
})

const credentials = createCredentialService({
  persistence,
  crypto: applicationCrypto,
  passwordHasher: argon2id(),
  breachChecker,
  limiter: createAuthAttemptLimiter(rateLimiter),
  delivery,
  stepUp: mfa,
  events,
  clock,
  ids,
  dummyPasswordHash: process.env.AUTH_DUMMY_PASSWORD_HASH!,
})
```

The same persistence instance may implement both the session and credential contracts. Transactional event handlers must use the same `transactionKind` so state and Tier 1 events commit atomically.

## Session configuration

| `DatabaseSessionOptions` property | Required | Purpose                                                             |
| --------------------------------- | -------- | ------------------------------------------------------------------- |
| `persistence`                     | Yes      | Transactional session and refresh-token state.                      |
| `crypto`                          | Yes      | Random tokens, SHA-256 digests, and replacement-token encryption.   |
| `events`                          | Yes      | Typed auth event emitter.                                           |
| `clock`, `ids`                    | Yes      | Application-owned time and ID generation.                           |
| `deviceValidator`                 | No       | Rejects inactive device-backed sessions.                            |
| `requireDevice`                   | No       | Requires a device ID on issue; defaults to `false`.                 |
| `accessTtlMs`                     | No       | Access lifetime; defaults to 15 minutes.                            |
| `refreshTtlMs`                    | No       | Refresh lifetime; defaults to 30 days.                              |
| `refreshGraceMs`                  | No       | Parallel-refresh replacement grace; defaults to 30 seconds.         |
| `reusePolicy`                     | No       | Reuse response: `session-family` by default or `all-user-sessions`. |

Refresh tokens rotate under a persistence lock. Reuse after the grace window revokes the token family and session.

`jwtAccessSessions()` signs and verifies the final token response before its
issuance/rotation transaction commits. Recovery returns that exact response and
refuses a consumed successor. The optional `@playstack/auth/jwt/node` codec uses
local rotating HS256, RS256 or ES256 keys with explicit issuer, audience and
clock. Install its optional `jose` peer only for that entry point; use a supported
Node version listed in the package README. Server revalidation remains enabled
by default. An application disabling it accepts expiry-bounded revocation.

## Session security and maintenance

```ts
const active = await sessions.listSessionsForUser(userId)
await sessions.revokeSessionForUser(userId, active[0].id)

const maintenance = createAuthMaintenance({
  persistence,
  clock,
  kinds: ['sessions', 'refreshTokens', 'authTokens'],
  consumedRetentionMs: 7 * 24 * 60 * 60_000,
})

await maintenance.purgeExpired({ limit: 250 })
```

Session activity exposes IDs, creation and last-use timestamps, expiry,
account/client/device references, and bounded IP and user-agent metadata. It
never exposes access or refresh credentials. Maintenance is bounded and
scheduler-neutral; one call removes at most 1,000 expired or retained consumed
records across sessions, refresh records, one-time tokens, authorization codes,
email changes, and staff sessions.

Choose trusted `kinds` for the installed capabilities. Omission selects all six
kinds and therefore requires their tables; unselected kinds are not queried.
Selecting `sessions` also requires `refreshTokens`. A selected missing table is
an error that rolls back the purge, not a reason to silently skip maintenance.
Signup proofs use their separate native cleanup operation. Schedule both where
needed and configure database timeouts: a row-deletion budget is not a scan or
elapsed-time bound.

Consumed refresh tokens remain replay evidence through the family's original
absolute expiry, even after revocation or a shorter consumed-record retention.
Cleanup preserves refreshable sessions after access expiry and removes only
eligible childless parents within the shared row budget. Do not equate expired
access credentials with safely reclaimable session families.

Use `listRenewableSessionsForUser()` when a security page must include sessions
whose access expired but whose renewal authority remains valid. It returns
bounded, scope-bound cursor pages. Individual and all-user revocation can join
the caller's physical transaction and revoke retained refresh descendants too.
`clearExpiredRefreshReplacements({ limit })` separately reclaims expired recovery
ciphertext without deleting replay evidence. Current custom adapters must
implement these ports and update-only session/refresh saves; a stale write must
not recreate a purged credential.

## Dual-inbox email approval

Set `emailChangePolicy: 'dual-inbox'` on `CredentialService` to require approval
from both the current and proposed inbox. The default `'new-inbox'` policy
preserves new-address confirmation and the old-address reversal window.
Pending requests retain their own immutable policy across service reconstruction.

Dual mode requires a `TransactionalAuthDelivery`. Request admission creates two
independent proofs and enqueues both encrypted delivery intents on the same
transaction. Deliveries identify `purpose: 'emailChangeApproval'` and an `inbox`
of `'old'` or `'new'`; immutable outbox keys must distinguish the two sides.
Failure of either enqueue rolls the request back. Delivery providers run only
through the application's claimed worker after commit.

`approveEmailChange(inbox, token, context?, transaction?)` returns a discriminated
`pending` or `completed` result. The first approval leaves the email unchanged.
The second commits the contact change, proof consumption, session/refresh
revocation, completion notice and required evidence together. Replays, expired
or superseded requests, changed current contacts and deleted identities refuse.
Standard confirmation/reversal cannot bypass a dual-policy request, and dual
completion issues no unilateral reversal proof.

The optional `authorizeEmailChange` callback runs on the identity transaction
with safe change metadata, including the approval side and completion stage.
Use it for application-owned dispute and freeze checks. Native requested and
approved events contain IDs/inbox only; the existing changed event represents
the final mutation. Return results to clients after the caller transaction
commits. A passing fixture does not qualify product policy, mail or browser flows.

Native persistence adds the policy, old-inbox proof hash and approval timestamps.
Update custom adapters and schema together. The Playstack host migration refuses
existing pending rows rather than inventing policy/approval evidence; retained
application requests need an explicit reviewed transition. No automatic backfill
is included. The current Playstack API does not enable dual-approval routes and
explicitly refuses that unadopted mail purpose.

## Transaction-bound active identity checks

For a trusted authenticated principal, call `assertActiveUser(persistence,
userId, transaction)` before protected writes on that same physical transaction.
Native Prisma's `findUserActivityForUpdate` locks the identity row through
commit/rollback, rather than performing an unlocked `findUserById` lookup.
The helper rejects root/autocommitting clients; a user ID is not authentication.
Account membership, roles and token scopes still need current authorization.

Cooperating operations must serialize deactivation/restoration/deletion on that
same row. Acquire identity locks before dependent account/membership/credential
locks, sorting IDs when locking multiple identities. Reconcile existing adapter
lock ordering before adoption; putting an unlocked read inside a transaction is
not equivalent. Do not perform provider I/O while holding these locks or reuse
the result outside the transaction.

## Credential configuration

| `CredentialServiceOptions` property | Required | Bridge responsibility                                                         |
| ----------------------------------- | -------- | ----------------------------------------------------------------------------- |
| `persistence`                       | Yes      | Users, identities, one-time credentials, pending email changes, and sessions. |
| `crypto`                            | Yes      | Opaque token generation, hashing, and encryption.                             |
| `passwordHasher`                    | Yes      | Password-grade hash and verify operations.                                    |
| `breachChecker`                     | Yes      | Checks candidate passwords against the application’s breach source.           |
| `limiter`                           | Yes      | Fails closed for identifier-plus-IP auth attempts.                            |
| `delivery`                          | Yes      | Sends verification, reset, magic-link, and email-change notifications.        |
| `stepUp`                            | Yes      | Verifies a recent-auth or MFA proof for sensitive changes.                    |
| `events`, `clock`, `ids`            | Yes      | Typed events and deterministic primitives.                                    |
| `dummyPasswordHash`                 | Yes      | Valid hash verified for unknown users to reduce timing disclosure.            |
| `tokenTtlMs`                        | No       | One-time token lifetime, 15–60 minutes; defaults to 30 minutes.               |
| `proofTtlMs`                        | No       | Per-purpose proof lifetimes, one minute to seven days. |
| `preparationTtlMs`                  | No       | One-shot preparation lifetime; defaults to 60 seconds, bounded to 1–300 seconds. |
| `authorizeCredentialOperation`     | No       | Read-only product policy checked under current identity locks. |
| `passwordChangeStepUp`              | For password change | Purpose/user-bound grant preparation and transaction-local reauthorization/consumption. |

## Prepared credential mutations

`preparePasswordRegistration`, `preparePasswordAuthentication`,
`preparePasswordReset` and `preparePasswordChange` move expensive password work
before write locks. Each returns a one-shot in-process `commit(transaction)`
capability. Commit checks current identity, password/contact/proof binding,
original expiry and policy before joining native writes and required audit.
Prepared reset/change require durable transaction-bound mail admission. Roll back
the enclosing transaction on any error and expose results only after commit;
retry after rollback requires fresh preparation.

Password change verifies the current password and requires a step-up grant whose
original purpose/user binding and expiry remain authoritative. Its
`authorizeInTransaction` must recheck or consume the original proof in the same
physical transaction. A boolean verifier alone does not supply this contract.
Keep onboarding, suspension, Account policy, templates and transport in the host.

## Browser-bound OAuth login

`@playstack/auth/oauth-login` provides immutable, expiring browser-bound state and
S256 PKCE with atomic single-use consumption before provider I/O. For shared
workers, `@playstack/auth/oauth-login-prisma` and the optional
`auth.oauth-login.prisma` artifact supply encrypted scoped persistence and bounded
cleanup. Use a distinct purpose-bound encryption adapter. An uncertain consume
or exchange requires a new attempt.

`@playstack/auth/github` supplies GitHub user login through the optional
Connections peer. It returns a scrubbed profile and trusts only a verified primary
email; provider tokens are not returned. Pass that server-only result to
`authenticateProfile()` with an explicit identity-linking policy. Hosts own secure
binding cookies, trusted callback extraction, initiation CSRF/rates, redirect
destinations and MFA. GitHub App installation tokens are a separate capability.

## Primary bridge contracts

| Contract                 | Key members                                                                                                |
| ------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `AuthPersistence`        | Transaction, session lookup/save/revoke, refresh-family locking and rotation.                              |
| `CredentialPersistence`  | Extends auth persistence with user, identity, one-time token, and email-change records.                    |
| `AuthCrypto`             | `randomToken`, `digestSha256`, `encrypt`, `decryptText`; structurally compatible with `@playstack/crypto`. |
| `PasswordHasher`         | `hash(password)` and `verify(hash, password)`.                                                             |
| `PasswordBreachChecker`  | `isBreached(password)`.                                                                                    |
| `AuthAttemptLimiter`     | `consume(operation, { identifier, ip })`; returns `allowed` and optional `retryAt`.                        |
| `AuthDelivery`           | Token delivery plus password- and email-change notifications.                                              |
| `StepUpVerifier`         | `verify(userId, proof)`; `MfaService` implements it structurally.                                          |
| `DeviceSessionValidator` | `assertActive(userId, deviceId)`.                                                                          |

`CredentialService.authenticateProfile(profile, context?)` is the entry point
for a provider profile already normalized by Auth.js or another framework.
`authenticateProvider(provider, code, context?)` performs the provider exchange
and delegates to the same method, so linking and verified-email policy cannot
drift between paths.

The transactional `auth.user.registered` event lets `@playstack/accounts`
provision an initial account and owner membership in the same database unit of
work. Delivery and initial session issuance remain post-commit application
orchestration.

`registerPassword()` also accepts an explicit transaction context. Use `createUnitOfWork()` from `@playstack/events` when auth, accounts, profiles, and audit records must share one application-owned database transaction. Persistence adapters must declare the same transaction kind and physical handle; mismatches fail closed.

## Website sign-in with IndieAuth

Use the optional server-side IndieAuth client when visitors should sign in with
their own website URL. It verifies the website's authorization server and hands
the confirmed URL to the existing credential/session system; it does not host
an IndieAuth authorization server or request Micropub access tokens.

```ts
import {
  createIndieAuthClient,
  createIndieAuthBrowserBinding,
  createMemoryIndieAuthAttemptStore,
} from '@playstack/auth/indieauth'
import { createNodeIndieAuthTransport } from '@playstack/auth/indieauth/node'

const indieauth = createIndieAuthClient({
  clientId: 'https://your-site.example/indieauth-client',
  redirectUri: 'https://your-site.example/auth/indieauth/callback',
  attempts: createMemoryIndieAuthAttemptStore(), // single process only
  transport: createNodeIndieAuthTransport(),
})

// After rate limiting and CSRF validation on your sign-in POST:
const browserBinding = createIndieAuthBrowserBinding()
const { authorizationUrl } = await indieauth.begin({ me: websiteUrl, browserBinding })
// Set a Secure/HttpOnly/SameSite=Lax/Path=/ host-only binding cookie;
// then redirect the browser to authorizationUrl.

// In the configured callback, read browserBinding from that cookie:
const profile = await indieauth.complete({ callbackUrl, browserBinding })
const user = await credentials.authenticateProfile(profile)
// Continue required MFA/account checks and your existing session flow.
```

Serve `indieauth.clientMetadata` as JSON at `clientId`. Configure the credential
service with `providerIdentityPolicy: { linking: 'explicit', email: 'optional' }`.
Only the confirmed website URL identifies the visitor. Provider email, profile
attributes and tokens are discarded; first-contact email enrollment and identity
linking require separate verified flows. An existing account is never selected
merely because a provider claims its email address.

Attempts use S256 PKCE, browser binding, expiry and atomic single-use consumption.
Multi-process/serverless deployments must supply a shared `IndieAuthAttemptStore`
with atomic binding/expiry checks and consumption; a replayable cookie alone or
get-then-delete storage is insufficient. Missing/duplicate binding cookies must
fail. Keep login/callback responses private and uncached, suppress referrers,
and redact callback query strings and credentials from logs. Uncertain exchanges
require fresh sign-in, never an automatic code retry.

The supported profile uses public HTTPS websites and current server-metadata
discovery with S256 and `iss`. HTTP/private/local endpoints and endpoint-only
discovery are not supported. The Node transport pins public DNS answers and
bounds redirects, headers, bodies and deadlines. Workers require an explicit
equivalent safe transport; ordinary global `fetch` does not establish SSRF safety.

The installed package's README includes the full setup, storage contract, limits
and error policy. Qualify your provider, browser cookies, proxy configuration,
shared storage and session/MFA flow before launch. The adapter itself adds no
managed schema or application routes.

## Bounded binary HTTPS and authoritative DNS

`createNodeBinaryHttpsTransport` from `@playstack/auth/https/node` returns bounded
binary bytes without UTF-8 decoding, while preserving all-address public DNS
admission, connection pinning, TLS verification and redirect refusal. Configure
trusted origins/paths and byte/time limits. Redirect hops are caller-managed and
must repeat validation; image signatures, raster processing and local asset
publication remain application policy.

`createNodeAuthoritativeDnsTransport` from `@playstack/auth/dns/node` supplies
explicit public-address-safe UDP/TCP observations. Install its optional exact
`@dnspect/dns-ts@0.4.0` peer and retain the packaged parser worker asset. The root
Auth entry remains Worker-safe and does not load Node sockets or that parser.
Configure trusted numeric public bootstrap resolvers and required bounds for
time, bytes, records, authority candidates, datagrams and concurrency. Request
inputs cannot choose ports, bootstrap resolvers or source interfaces.

Supported queries include A, AAAA, NS, CNAME, SOA, TXT, DS, **MX, SRV and CAA**.
MX exposes `preference` and lossless `exchange` labels (including null MX).
SRV exposes distinct `priority`, `weight`, `port` and `target`, preserves underscore
owners/root targets and rejects target compression. CAA exposes `flags`, a
case-preserved `tag` and the complete **`Uint8Array` `value`**, including empty and
non-UTF-8 values. Decode/validate issuer syntax in application policy, not by
assuming every value is text. Every record retains owner/class/type/TTL and its
containing answer, authority or additional section. Negative response flags and
empty answers remain observations, not positive readiness. Earlier beta.3 local
snapshots do not include these types; verify the exact adopted artifacts and
immutable release before replacing an adapter.

Literal wildcard questions such as `*.example.com` preserve the complete leftmost
`*` label through encoding, reply correlation and observations, without expansion.
Partial/interior wildcards and wildcard authority hostnames remain invalid.
TXT observations expose ordered **`readonly Uint8Array[]` `chunks`**, preserving
empty chunks, arbitrary binary bytes and UTF-8 sequences split across chunks.
There is no forced decoding, concatenation or replacement. Interpret bytes in
host policy; migrate the earlier ASCII-string projection deliberately. The raw
byte profile uses the same exact parser peer and bounded worker assets, not a
second generic DNS implementation. Verify the new immutable release; existing
beta.3 artifacts cannot acquire these changes without a distinct publication.

The transport pins admitted addresses, correlates replies, bounds parsing in a
worker and handles cancellation/cleanup. It does not automatically follow
referrals/CNAMEs or perform retries. Results keep answers, authority and
additional sections separate: flags, glue and DS bytes are observations, not
ownership or DNSSEC proofs. Delegation/bailiwick policy, network egress and real
API/worker runtime qualification remain host responsibilities.

## Snapshot TXT bytes without decoding

For an application-owned JSON snapshot, convert each byte chunk separately to a
numeric array. This preserves order, empty chunks and arbitrary bytes, including
UTF-8 sequences split across chunks, without choosing a text interpretation:

```ts
import type { DnsRecord } from '@playstack/auth/dns/node'

export function txtChunkSnapshot(
  record: DnsRecord,
): readonly (readonly number[])[] | undefined {
  if (record.kind !== 'TXT') return undefined
  return record.chunks.map((chunk) => Array.from(chunk))
}
```

For example, `[[226], [130, 172], [], [255, 0, 128]]` remains four chunks, not a
replacement-character string. This is a local projection, not a send grant,
authenticated DNS result or new persistence contract. If product policy permits
text, choose its encoding and validation explicitly and account for sequences
spanning chunks; keep the original byte chunks. Consult the [upgrade checklist](/changelog/beta-3-adoption-and-operator-controls#upgrade-checklist)
before replacing older string-based projections.

## Realtime composition

Issue a purpose-bound ticket from authenticated HTTP, then redeem it in the WebSocket hello frame:

```ts
import { createRealtimeTicketService } from '@playstack/auth'

const tickets = createRealtimeTicketService({ sessions, crypto: applicationCrypto })
const ticket = await tickets.issue(accessToken)
const session = await tickets.redeem(ticket)
```

Tickets default to 30 seconds and may be configured from 1–60 seconds. Single-use safety depends on the atomic replay store configured in `@playstack/crypto`. Redemption re-resolves the session, so a newly revoked session cannot open a connection.

## Web and persistence adapters

- `sessionCookie`, `clearSessionCookie`, and `assertCsrf` enforce the v1 cookie/CSRF transport invariants without choosing a framework.
- `@playstack/auth-contracts` publishes the JSON-safe session contract independently so a React or Next client can migrate before the server implementation.
- `parseAuthSessionView` validates the narrow JSON-safe client projection and rejects unknown or credential-shaped fields.
- `@playstack/auth/prisma` provides `prismaAuthPersistence(prisma)` and the managed, non-ejectable Prisma fragment.
- `@playstack/auth/testing` provides in-memory persistence and crypto fakes.

Applications can register a validated `extensions` envelope with `createAuthSessionViewParser()` and `createAuthSessionProjector()`. The same parser must be used by server projection, React transport, account client, and Next helpers so product-specific account summaries or capabilities remain typed end to end. Tokens and provider credentials never belong in this envelope.

## Migrate an established application

`createMigratingPasswordHasher()` verifies an explicitly configured legacy hash such as bcrypt, then rehashes with the current Argon2id policy under a compare-and-swap write. `createLegacySessionExchange()` provides an absolute, bounded window for exchanging an existing JWT or refresh session into ordinary Playstack opaque credentials.

Existing schemas may satisfy managed artifacts through exact model and field mappings. When physical storage differs, omit the artifact and implement application-owned `AuthPersistence` or `CredentialPersistence`; that adapter owns identifier conversion, dual reads, shadow writes, and record migration. The CLI can scaffold typed method stubs and contract-test entry points without declaring a semantically similar schema compatible.

Register `sessions.deviceRevocationHandler()` for `devices.device.revoked` and `sessions.mfaChangeHandler()` for MFA enrollment and disable events when those packages share the same transaction kind.

## Boundary

Auth does not load secrets, choose HTTP routes, own account membership or authorization, identify devices, or decide MFA policy. Add React, Next.js, NestJS, accounts, devices, and MFA packages only at application edges that need them.

## Explicit identity and replay policies

`providerIdentityPolicy: { linking: 'explicit', email: 'optional' }` supports email-less provider users without silently linking a matching email. Trusted server normalization and step-up-backed identity linking remain required. The compatible default is verified-email linking with required email. Safe user/session projections and registration-event email may be null; first-contact email enrollment remains an application-owned verified workflow.

New provider users emit the same transactional registration event as password signup. Required account/audit provisioning must join the actual transaction, not a post-login callback.

`refreshReplayPolicy: 'strict'` rejects every consumed-token replay without returning replacement material. The default bounded-grace policy retains encrypted replacement credentials for a bounded interval. `reusePolicy: 'all-user-sessions'` separately widens revocation scope. Strict mode rejects `refreshGraceMs` and requires shared client refresh coordination.

Authorization-code consumption and session issuance can commit together through required transaction-aware issuance/authorization seams with account context. A committed code cannot be replayed to recover lost credentials. Optional device authorization follows the same one-time boundary. See [Client authentication](/features/client-authentication).

## Optional session lifetime and admission policies

`databaseSessions()` now accepts `idleTtlMs`, `absoluteTtlMs` and `maxActiveSessions`; omitted options preserve existing defaults.

- Idle expiry is inclusive and measured from successful verify, resolve or refresh. Background requests count as activity and verification adds locked activity writes; this is not automatically human-idle detection.
- Absolute lifetime starts at original issuance, survives refresh/account switching and clips token expiry. It cannot exceed `refreshTtlMs`.
- Per-user caps reject new issuance with `playstack/auth/session_limit_exceeded`, never silently evict devices. Refreshable sessions count even after access expiry; caps require an explicit absolute lifetime.

Prisma supplies transaction-scoped creation locks and locked reads. Application persistence must implement `lockSessionCreationForUser` for caps and `findSessionByIdForUpdate` for idle expiry, with real cross-instance correctness. Missing seams fail configuration; no-op locks are not sufficient. All issuers must use the same policy/lock contract.

Map authenticated cap failures to a deliberate host response while retaining enumeration-safe unauthenticated errors. Strict/bounded-grace refresh replay remains independent; cookie transport, SSO, native coordination and deployment policy changes require application acceptance tests.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/auth@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/auth` | `./dist/index.d.ts` |
| `@playstack/auth/errors` | `./dist/errors.d.ts` |
| `@playstack/auth/indieauth` | `./dist/indieauth.d.ts` |
| `@playstack/auth/indieauth/node` | `./dist/indieauth-node.d.ts` |
| `@playstack/auth/https/node` | `./dist/https-node.d.ts` |
| `@playstack/auth/dns/node` | `./dist/dns-node.d.ts` |
| `@playstack/auth/authorization` | `./dist/authorization.d.ts` |
| `@playstack/auth/device` | `./dist/device-authorization.d.ts` |
| `@playstack/auth/device-prisma` | `./dist/device-prisma.d.ts` |
| `@playstack/auth/device-testing` | `./dist/device-testing.d.ts` |
| `@playstack/auth/passwords` | `./dist/passwords.d.ts` |
| `@playstack/auth/jwt/node` | `./dist/jwt-node.d.ts` |
| `@playstack/auth/oauth-login` | `./dist/oauth-login.d.ts` |
| `@playstack/auth/oauth-login-prisma` | `./dist/oauth-login-prisma.d.ts` |
| `@playstack/auth/github` | `./dist/github.d.ts` |
| `@playstack/auth/node` | `./dist/node.d.ts` |
| `@playstack/auth/prisma` | `./dist/prisma.d.ts` |
| `@playstack/auth/testing` | `./dist/testing.d.ts` |
| `@playstack/auth/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/auth/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/connections` | `0.1.0-beta.4` | Optional; only for the entry points that use it. |
| `@dnspect/dns-ts` | `0.4.0` | Optional; only for the entry points that use it. |
| `argon2` | `>=0.44.0 <1` | Optional; only for the entry points that use it. |
| `jose` | `>=6.2.13 <7` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/cache

Source: https://playstack.dev/docs/packages/infrastructure/cache.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/cache` provides a small exact-JSON cache with millisecond TTLs, canonical tag generations, stampede-safe `remember`, explicit capabilities, and native client access.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/cache@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose and use

```ts
import { createCache } from '@playstack/cache'
import { RedisCacheStorage } from '@playstack/cache/redis'

const cache = createCache({
  app: 'billing',
  environment: 'production',
  driver: new RedisCacheStorage(redis),
  lockTtlMs: 30_000,
})

const plan = await cache.remember('plans:account-1', 60_000, () => loadPlan())

const accountCache = cache.tags('accounts', 'region:us')
await accountCache.put('summary', { total: 4 }, 60_000)
await accountCache.flush()
```

The physical namespace is `{app}:{environment}:cache`. Keys allow up to 512 UTF-8 bytes and no control characters. TTLs are positive integer milliseconds.

## Configuration reference

| Property             | Required | Purpose                                               |
| -------------------- | -------- | ----------------------------------------------------- |
| `app`, `environment` | Yes      | Canonical physical namespace.                         |
| `driver`             | Yes      | JSON entry, tag-generation, and optional lock bridge. |
| `lockTtlMs`          | No       | Distributed stampede lease; defaults to 30 seconds.   |

Values cross a versioned exact-JSON boundary. Class instances, undefined, non-finite numbers, sparse arrays, symbols, and cycles fail before the driver. Cached null is retained by `remember`, while `get<T>()` intentionally uses `T | null` for conventional misses.

## Tag and lock behavior

Tags are sorted and deduplicated. Tagged and untagged keys remain separate. `flush()` increments canonical tag generations, making old entries unreachable without scanning; they expire on their original TTL.

Concurrent `remember` calls on one cache instance always share a promise. A driver with atomic locks extends that across processes. Loader errors remain unchanged, are not cached, and release the lease.

Native lock-capable storage fences publication with `putIfLockOwner`: a loader whose lease expired cannot overwrite a successor's cached value. Its original caller can still receive the computed, uncached result; the loader is not automatically rerun. This is not an exactly-once execution guarantee.

## Batches, bounded memory, and observations

`getMany<T>(keys)` returns ordered values or nulls and preserves duplicate read keys. `putMany([{ key, value, ttlMs }])` validates and snapshots the whole batch before I/O and rejects duplicate write keys. Batches must be dense arrays of at most 1,000 entries; empty batches are no-ops. Tagged batches use one generation snapshot. Redis uses a native multi-get or Lua write; other drivers can fall back to single-key operations. A failed write may be partial or unacknowledged: there is no portable batch transaction or automatic retry.

`MemoryCacheStorage` is exported from the package root and can serve bounded process-local caches, not just tests. Its defaults are 1,000 values, 16 MiB of UTF-8 key/value bytes, 1,000 tags, and 1,000 locks. Pass positive integer `maxEntries`, `maxBytes`, `maxTags`, and `maxLocks` options as the second constructor argument (after an optional clock). The byte limit is not a total JavaScript heap bound.

Capacity pressure removes expired entries before least-recently-read values. An oversized value is rejected without evicting other values. `pruneExpired(budget = 100)` performs a bounded, resumable sweep without installing a timer. Live locks are not evicted, and tag capacity exhaustion fails rather than resetting generations and reviving stale values.

`cache.getStats()` returns an immutable storage-instance observation of hits, misses, acknowledged writes, and available eviction/expiration counts. These are not namespace-scoped or global request metrics. Unobservable remote counts and unsupported custom-driver statistics are `null`, not fabricated zeroes.

## Driver capabilities

### Layer memory over Redis

Use `createIoredisCacheClient` from `@playstack/cache/redis` to bind an explicitly
installed ioredis 5 client to `RedisCacheStorage`. The application owns connection
timeouts, offline-queue policy and shutdown; the binding does not replay failed
commands or open a client itself.

`MemoryRedisCacheStorage` from `@playstack/cache/memory-redis` adds bounded
read-through memory to that Redis driver. Configure `memory.maxEntries`,
`memory.maxBytes` and a positive finite `maxL1TtlMs`; ten minutes (`600_000`) is
supported when that staleness is acceptable. Each fill is capped by remaining
Redis TTL, including elapsed command time. Native cold batches fetch values and
remaining TTLs in one Redis command; warm untagged batches need no remote call.
Combined statistics count logical reads and acknowledged writes, not internal
L1 probes/fills twice. Tier-local observations remain available through `client`.

Writes await Redis acknowledgment and invalidate, rather than eagerly warm, L1.
Local mutation epochs fence delayed fills. Tag generations remain remote, but
another process's same-generation overwrite/delete can leave a warm value stale
until its L1 deadline. This is not a distributed coherent cache; use plain Redis
or authoritative storage for immediate revocation and authorization checks.

### Opt into loss-tolerant background writes

`createCacheWriteBehind` from `@playstack/cache/write-behind` is a separate,
non-durable helper for rebuildable **untagged** derived data. Ordinary `put`,
`putMany` and token-fenced `remember` publication remain acknowledged operations.
Configure bounded `maxPendingEntries`, `maxPendingBytes`, a safe `onFailure`
observer and a bounded `drainAndClose` shutdown step. Admission is not persistence;
process exit can lose admitted writes.

Optional `provisionalL1` adds independent entry/byte/TTL budgets. Read through
the returned helper's `get`/`getMany` for immediate local visibility, not the
original cache. Values are unacknowledged JSON snapshots; lifetime starts at
admission and acknowledgment never extends it. Schedule `pruneLocal` if idle
expired values need physical reclamation; runtime suspension prevents a strict
wall-clock erasure guarantee.

Unknown write failure stops admission/dispatch and clears local snapshots; close
or shutdown timeout also clears them. An in-flight remote command can still
commit afterward. Do not mix direct writes/deletes, tag flushes or a replacement
helper with pending or uncertain work. There is no automatic retry/continue mode,
remote sender fence or authorization guarantee. The package README documents
local statistics separately from acknowledged driver statistics.

### Provider comparison

| Driver                | Distributed lock         | Tag consistency |
| --------------------- | ------------------------ | --------------- |
| Bounded memory        | No, process-local        | Immediate       |
| Redis                 | Yes, token-checked lease | Immediate       |
| Cloudflare Workers KV | No, process-local        | Eventual        |

Each driver exposes `client` and capabilities. Redis uses `SET NX PX`, Lua release, atomic generation increments, and native millisecond expiry. Workers KV stores logical millisecond expiry while accepting its 60-second minimum physical cleanup.

## Bind shared Next.js caching

[`@playstack/cache-next`](/docs/packages/infrastructure/cache-next) adapts a configured cache to Next's stable incremental `cacheHandler` contract and the Next 16 Cache Components `cacheHandlers` contract. It preserves structured-clone values and streamed Cache Component entries at the framework boundary rather than weakening the core exact-JSON cache.

The package also supplies a timestamped HMAC revalidation handler for both App Router and Pages Router applications. Route placement, cache policy, authorization, and deployment-specific Redis lifecycle remain application-owned.

## Boundary

Cache is not authoritative application state, and provider consistency differences are not hidden. Storage/malformed-entry failures become retryable `CacheUnavailableError`. The application creates, observes, and closes the native client and chooses loaders that finish within the lock lease.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/cache@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/cache` | `./dist/index.d.ts` |
| `@playstack/cache/errors` | `./dist/errors.d.ts` |
| `@playstack/cache/types` | `./dist/types.d.ts` |
| `@playstack/cache/testing` | `./dist/testing.d.ts` |
| `@playstack/cache/redis` | `./dist/redis.d.ts` |
| `@playstack/cache/memory-redis` | `./dist/memory-redis.d.ts` |
| `@playstack/cache/write-behind` | `./dist/write-behind.d.ts` |
| `@playstack/cache/cloudflare-kv` | `./dist/cloudflare-kv.d.ts` |
| `@playstack/cache/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/catalog

Source: https://playstack.dev/docs/packages/commerce/catalog.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/catalog` owns commercial identity and server-authored terms. Storefront presentation, CMS content, payment execution, inventory, and fulfillment remain separate boundaries.

```ts
import { createCatalog } from '@playstack/catalog'

const catalog = createCatalog({
  persistence,
  events,
  ids,
  clock,
  orderMetadataKeys: ['contentRef'],
})

const quote = await catalog.resolveOffer({
  offerId: 'offer-controller-stand-v1',
  provider: 'stripe',
  providerInstance: 'store',
})
```

Offers are immutable revisions. Activating a revision atomically retires the previous active offer for the variant. Resolution rejects inactive products and offers, and matches both provider and configured instance so price IDs cannot cross merchant accounts.

Use `@playstack/catalog/prisma` for PostgreSQL persistence and `@playstack/catalog/testing` for in-memory contract tests. The application owns administration routes, presentation, migrations, and product-copy policy.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/catalog@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/catalog@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/catalog` | `./dist/index.d.ts` |
| `@playstack/catalog/errors` | `./dist/errors.d.ts` |
| `@playstack/catalog/prisma` | `./dist/prisma.d.ts` |
| `@playstack/catalog/testing` | `./dist/testing.d.ts` |
| `@playstack/catalog/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/chakra

Source: https://playstack.dev/docs/packages/ui/chakra.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

The package composes Chakra's default configuration with Playstack's neutral theme and application overrides. It does not duplicate Chakra's primitive component library.

~~~ts
import { createPlaystackSystem, warmTheme } from '@playstack/chakra'

export const system = createPlaystackSystem(warmTheme)
~~~

Create the system once in a client-owned theme module. Pass it to ChakraProvider or PlaystackUIProvider; do not serialize its functions through a Next Server Component boundary.

Exports include the root, `/system`, `/tokens`, `/themes/default` and `/themes/warm`. Both presets define light/dark foreground, background and border tokens. Color-mode selection, persistence, fonts and product branding remain application-owned; density is a separate app-ui shell setting.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/chakra@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Share a theme across a site and dashboard

Keep your product configuration and composed system in one application-owned theme module, then import that same module from both apps:

```ts
import { defineConfig } from '@chakra-ui/react'
import { createPlaystackSystem } from '@playstack/chakra'

const productTheme = defineConfig({
  theme: {
    tokens: {
      fonts: { heading: { value: 'system-ui, sans-serif' } },
    },
    recipes: {
      heading: { base: { letterSpacing: '-0.02em' } },
    },
  },
})

export const system = createPlaystackSystem(productTheme)
```

Pass `system` to the existing `ChakraProvider` or to `PlaystackUIProvider`; do not nest another theme provider just for shared components. Centralize heading size/line-height recipes, semantic colors, and shared styles there, removing equivalent per-page overrides. Keep intentional page-specific layouts separate. Load product fonts and global CSS in each application's framework entry point and align the initial color mode to avoid a hydration flash. Custom font assets and the Playstack site's internal theme are not bundled as a public product theme.

Peers are Chakra 3 (3.0 through 3.37), Emotion 11 and React/React DOM 18.2 or 19. Use TypeScript bundler resolution with ESNext modules. Verify contrast and assistive-technology behavior in your own product's browsers.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/chakra@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/chakra/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/chakra` | `./dist/index.d.ts` |
| `@playstack/chakra/tokens` | `./dist/tokens.d.ts` |
| `@playstack/chakra/system` | `./dist/system.d.ts` |
| `@playstack/chakra/themes/default` | `./dist/themes/default.d.ts` |
| `@playstack/chakra/themes/warm` | `./dist/themes/warm.d.ts` |
| `@playstack/chakra/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@chakra-ui/react` | `^3.0.0` | Required by the package. |
| `@emotion/react` | `^11.0.0` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |
| `react-dom` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/content

Source: https://playstack.dev/docs/packages/content-intelligence/content.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/content` contains pure, deterministic content transforms, shared schema fields, and array-resolution helpers that work with Content Collections or any other loader.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/content@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose a schema

```ts
import { contentBase } from '@playstack/content/zod'
import { excerpt, normalizeDate, readingTime, slugify } from '@playstack/content'
import { z } from 'zod'

const postSchema = z.object({
  ...contentBase,
  content: z.string(),
  tags: z.array(z.string()).default([]),
})

const post = postSchema.parse(input)
const slug = post.slug ?? slugify(post.title)
const date = normalizeDate(post.date)
const summary = excerpt(post.content, { words: 40 })
const duration = readingTime(post.content, { wordsPerMinute: 220 })
```

`contentBase` defines title, description, flexible input date, published flag, and optional slug. Applications extend it rather than inheriting a loader-specific document type.

## Transform options

| Function | Options | Result |
| --- | --- | --- |
| `slugify` | `separator: '-' \| '_'` | Unicode-normalized, stable lowercase slug. |
| `readingTime` | `wordsPerMinute`, `excludeCodeBlocks` | Word count, fractional minutes, and display text. |
| `extractToc` | `minDepth`, `maxDepth` | Nested heading items. |
| `excerpt` | `words`, `marker` | Word-bounded plain-text excerpt. |
| `normalizeDate` | None | Valid normalized ISO timestamp. |

## Resolve injected collections

```ts
import {
  resolveAdjacent,
  resolveDoc,
  resolveDocs,
  resolvePaths,
  resolveRelated,
} from '@playstack/content'

const post = resolveDoc(allPosts, { slug: 'build-once' })
const published = resolveDocs(allPosts, {
  filter: (entry) => entry.published,
  sort: (a, b) => b.date.localeCompare(a.date),
  limit: 20,
})
const paths = resolvePaths(allPosts, { param: 'slug' })
const adjacent = resolveAdjacent(allPosts, { slug: 'build-once' })
const related = resolveRelated(allPosts, { slug: 'build-once', by: 'tags', limit: 3 })
```

Resolution accepts document arrays and optional nested `fields.slug`/`fields.date` mappings. Filtering, sorting, limits, related-field selection, and route param names stay explicit application inputs.

## Boundary

The package performs no filesystem or network access and depends on no framework, rendering library, or content loader. It does not compile Markdown, render MDX, watch files, or own pagination UI. Consumers inject already-loaded values, making the helpers usable at build time, runtime, and in tests.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/content@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/content` | `./dist/index.d.ts` |
| `@playstack/content/zod` | `./dist/zod.d.ts` |
| `@playstack/content/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `zod` | `^4.4.3` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/core

Source: https://playstack.dev/docs/packages/foundation/core.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/core` is the dependency-free center of the package suite. It contains the contracts that capabilities use to agree on errors, results, time, identifiers, and operation metadata without becoming a general utility library.

## Install

```sh
npm install @playstack/core
```

Keep it on the same fixed version as every other `@playstack/*` dependency.

## Compose it at the application boundary

Create operation metadata where a request, job, or message enters the application. Pass it into domain commands explicitly; it is descriptive context, not authorization evidence.

```ts
import type { OperationContext } from '@playstack/core'

export function operationContextFromRequest(request: Request): OperationContext {
  const correlationId = request.headers.get('x-correlation-id') ?? crypto.randomUUID()
  const traceparent = request.headers.get('traceparent')

  return {
    correlationId,
    actor: { type: 'user', id: authenticatedUserId(request) },
    scope: { type: 'account', id: activeAccountId(request) },
    ...(traceparent ? { trace: { traceparent } } : {}),
  }
}

const context = operationContextFromRequest(request)
await accounts.rename({ accountId, name, context })
```

When one operation causes another, reuse the correlation ID and set the source event or command ID as `causationId`.

## Configuration

Core reads no globals and has no configuration object. Supply implementations at the composition root:

| Contract | Required member | Application responsibility |
| --- | --- | --- |
| `Clock` | `nowMs(): number` | Select real or deterministic time. Use `systemClock()` in production. |
| `IdGenerator` | `next(): string` | Generate IDs in the application’s required format. |
| `OperationContext` | Optional metadata fields | Build it from trusted boundary state and pass it explicitly. |

## Operation context properties

| Property | Type | Meaning |
| --- | --- | --- |
| `correlationId` | `string?` | Groups work that belongs to the same end-to-end operation. |
| `causationId` | `string?` | Identifies the command or event that directly caused this work. |
| `actor` | `ActorReference?` | Opaque `apiKey`, `staff`, `system`, or `user` reference. |
| `scope` | `ScopeReference?` | Opaque `account`, `accountMember`, or `user` reference. |
| `trace` | `TraceContext?` | W3C `traceparent` plus optional `tracestate` and `baggage`. |

Actor and scope references deliberately carry IDs, not hydrated account or user records.

## Errors and results

Define package errors with a stable code, then expose expected outcomes as `Result` when the caller should branch without exception handling.

```ts
import {
  PlaystackError,
  createErrorCode,
  err,
  ok,
  type Result,
} from '@playstack/core'

class CartEmptyError extends PlaystackError {
  readonly code = createErrorCode('cart', 'cart_empty')
  readonly retryable = false
}

function checkout(items: readonly Item[]): Result<Order, CartEmptyError> {
  return items.length === 0 ? err(new CartEmptyError('Cart is empty')) : ok(createOrder(items))
}
```

Error codes use `playstack/<kebab-package>/<snake_case-code>`. Every `PlaystackError` declares whether retrying the same operation may succeed.

## Testing

Import `FakeClock`, `SequenceIds`, and `assertPlaystackError` from `@playstack/core/testing` to make time, identifiers, and error assertions deterministic.

The same entry owns the shared in-memory unit of work that every package's memory persistence adapter joins: `MemoryTransactionHandle`, `runMemoryTransaction`, and `MemoryTransactionQueue`. A transaction opened by one package's memory adapter can be passed to another package's adapter, and both commit or roll back together, exactly as they would on one database transaction. Every memory adapter exposes `state(transaction?)` for its working copy inside a transaction or its committed state without one.

```ts
import { MemoryAccountsPersistence } from '@playstack/accounts/testing'
import { MemoryAuthPersistence } from '@playstack/auth/testing'

const auth = new MemoryAuthPersistence()
const accounts = new MemoryAccountsPersistence()

await auth.transaction(async (transaction) => {
  await auth.saveUser(user, transaction)
  await accounts.saveAccount(account, transaction) // joins the same unit of work
})
```

Memory adapters are single-writer test doubles: each serializes the transactions it opens, and composed flows inherit that ordering from the adapter that opened the transaction.

## Boundary

Core contains no configuration loading, logging, persistence, cryptography, or framework lifecycle. Nearly every stateful package composes with these contracts, but the application remains responsible for creating and supplying them.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/core@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/core` | `./dist/index.d.ts` |
| `@playstack/core/errors` | `./dist/errors.d.ts` |
| `@playstack/core/testing` | `./dist/testing.d.ts` |
| `@playstack/core/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/events

Source: https://playstack.dev/docs/packages/events-operations/events.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/events` is the typed seam through which a capability describes what happened without selecting a framework, broker, or event store.

## Install

```sh
npm install @playstack/core @playstack/events
```

## Define and compose registries

Payload validators use Standard Schema v1, so Zod and other conforming schemas work directly.

```ts
import { createEventBridge, defineEvents } from '@playstack/events'
import { z } from 'zod'

const catalogEvents = defineEvents({
  'catalog.product.published': {
    payload: z.object({ productId: z.string(), slug: z.string() }),
    audit: { tier: 'transactional', fields: ['productId'] },
  },
})

export const eventBridge = createEventBridge({
  registries: [catalogEvents, accountsEvents, authEvents],
  clock,
  ids,
  outbox,
  supportedTransactionKinds: ['prisma'],
  routes: [
    { match: 'catalog.product.published', to: [searchIndexer], retry: 3 },
  ],
  failureRecorder,
})

eventBridge.assertReady()
```

Event names require at least three lowercase dot-separated segments. `defineEventSchema(parse)` adapts a small hand-written parser when a schema library is unnecessary.

## Bridge configuration

| `EventBridgeOptions` property | Required | Purpose |
| --- | --- | --- |
| `registries` | Yes | Package and application registries merged into one typed contract. |
| `clock`, `ids` | Yes | Envelope timestamps and identifiers. |
| `outbox` | For durable routes | Transactional observational delivery bridge. |
| `routes` | No | Maps event names to observational handlers and retry counts. |
| `failureRecorder` | No | Captures isolated observational dispatch failures. |
| `supportedTransactionKinds` | No | Allowlist of transaction handles accepted by internal handlers/outbox. |
| `maximumDepth` | No | Limits recursive caused-event chains. |

The returned bridge exposes the merged `registry`, typed `events` emitter, and `assertReady()` completeness check. It owns no application lifecycle.

## Emit options and envelope properties

```ts
await events.emit(
  'catalog.product.published',
  () => ({ productId, slug }),
  { transaction, context: operationContext },
)
```

| Emit option | Meaning |
| --- | --- |
| `transaction` | Application transaction used by same-kind internal handlers and the outbox. |
| `cause` | Source envelope; inherits correlation, actor, scope, and trace and sets causation. |
| `context` | Root `OperationContext` for a new chain. |
| `bestEffort` | Allows explicitly non-critical observational work to fail without rejecting emission. |

Every immutable `EventEnvelope` carries `id`, `name`, validated JSON payload, `occurredAt`, `correlationId`, and optional causation, actor, scope, and W3C trace properties.

## Handler and transport bridges

| Contract | Responsibility |
| --- | --- |
| `InternalEventHandler` | Declares `transactionKind` and handles an event inside the emitting transaction. |
| `ObservationalEventHandler` | Named handler for isolated, retryable application reactions. |
| `OutboxWriter` | Declares `transactionKind` and enqueues the envelope atomically. |
| `DispatchFailureRecorder` | Records event, handler, error, and attempt count. |

Internal handlers implement package invariants and may fail the originating mutation. Observational handlers belong behind the outbox when they cross process or infrastructure boundaries.

`emit` is a no-op that returns `null` when nothing is registered for the event: no internal handler, no observational handler, and no route. An outbox row is written only when an observational handler or route will consume it, so a host that wants every event of a package in the outbox, for a projection or an external consumer, registers an observational handler for it. Transactional audit handlers bound with `@playstack/audit` write audit rows on the emitting transaction and do not by themselves produce outbox rows.

## Boundary

Events is not an event-sourced aggregate log, replay system, or general broker abstraction. Stateful packages expose their persistence seams and publish integration events rather than importing one another; the application chooses durable transport and consumer lifecycle.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/events@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/events` | `./dist/index.d.ts` |
| `@playstack/events/errors` | `./dist/errors.d.ts` |
| `@playstack/events/testing` | `./dist/testing.d.ts` |
| `@playstack/events/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/events/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Artifact synchronization

Source: https://playstack.dev/docs/cli/artifact-synchronization.md

Playstack packages can publish application-facing files without hiding them behind runtime generation. The CLI verifies those files, resolves their configured destinations, and writes reviewable output into the application repository.

## Supported artifact kinds

| Kind | Command | Typical output |
| --- | --- | --- |
| `database-schema` | `playstack database sync` | Prisma or Drizzle schema artifacts selected by named target, package, and variant. |
| `emails` | `playstack emails init` | Package-owned transactional email templates. |
| `content` | `playstack content init` | Content files intended to be adapted by the application. |
| `i18n` | `playstack i18n init` | Translation resources owned by an installed package. |
| `persistence-adapter` | `playstack adapter scaffold <package>` | Explicit application-owned adapter starter; not a managed database schema. |
| `source` | `playstack eject` | Explicitly ejectable source artifacts, not ordinary managed sync output. |

## Preview before writing

```sh
npx playstack database sync --dry-run
npx playstack database compatibility --database=primary
npx playstack database sync --database=primary --diff
npx playstack database sync --check
npx playstack doctor --check
```

`--dry-run` reports planned changes without writing. `--diff` adds the generated difference and implies a dry run. `--check` is intended for CI: it exits non-zero when managed output is stale and never prompts or writes.

Email, content, and translation commands use the same write flags:

```sh
npx playstack emails init --diff
npx playstack content init --check
npx playstack i18n init
```

## Resolve package artifacts

An emitting package publishes `playstack.artifacts.json`. Each entry declares:

| Property | Meaning |
| --- | --- |
| `id` | Stable artifact identifier within the package. |
| `kind` | `database-schema`, `content`, `emails`, `i18n`, `persistence-adapter`, or `source`. |
| `format` | Required database-schema representation: `prisma` or `drizzle`. |
| `dialect` | Required for Drizzle schema artifacts; currently `postgresql`. |
| `source` / `target` | Package-relative source and default application destination. |
| `variant` | Optional schema or output variant selected by application configuration. |
| `optional` | Database schema omitted from defaults; opt in with `artifacts` or `existingArtifacts`. |
| `ejectable` | Whether ownership may move to the application. |
| `securityCritical` | Prevents ejection and keeps updates package-managed. |
| `hash` | SHA-256 integrity check for the published source. |

When `packages` is omitted from an artifact target, the CLI surveys installed `@playstack/*` dependencies and uses emitters that declare the requested kind. An explicit package list becomes an allowlist and causes missing or invalid manifests to fail instead of being skipped.

For database schemas, the configured target `adapter` must match the artifact `format`. Drizzle targets additionally require matching PostgreSQL dialect metadata, exported schema symbols, resolvable copied imports, and collision-free filenames. The CLI materializes source only: Prisma, Drizzle Kit, or the application remains responsible for client generation, migrations, pushes, and introspection.

An omitted `artifacts` list selects compatible non-optional defaults. An explicit
`artifacts: []` selects no synchronized schemas; it does not bypass validation of
`existingArtifacts`. See [schema selection](/docs/cli/configuration#selecting-schema-artifacts)
for the queue-only Notifications example. Optional features must be selected
deliberately, and every configured native adapter still needs its storage contract.

For an established Prisma application, run the read-only `database compatibility` command before synchronization. It reports incoming logical and physical names, existing declarations, artifact-to-artifact collisions, and field-level satisfaction findings. Continue with [Existing schema adoption](/docs/cli/existing-schema-adoption) when the application already owns relevant tables.

## Configure lazily

An artifact command can establish a missing target interactively without requiring a complete `playstack init` first. The CLI shows the proposed relative path and records it in `playstack.json` before synchronization.

In non-interactive or check mode, a missing target is reported as configuration drift. The CLI never guesses a write destination in CI.

## Managed-file ownership

Managed files remain package-owned. The CLI records their source and content hashes under `.playstack`, then compares those hashes before every write.

- A clean second sync is a no-op.
- An upstream change updates an unmodified managed copy.
- A local edit is reported as unsafe and is not silently overwritten.
- Deselected artifacts and artifacts no longer published upstream are not automatically deleted; their existing files and managed records remain.

### Review obsolete output explicitly

Narrowing `artifacts` to `[]`, omitting a now-optional schema, or upgrading to a
manifest without an older artifact does not prune previous output. This applies
to clean and locally edited files. `doctor --check` checks current selections;
success is not evidence that obsolete files or stored database data were erased.

Before retiring an unused inbox or other schema, review its generated file and
managed provenance in version control, application/client references, runtime
adapters and any existing data. Retire files and tracking deliberately as part of
that reviewed change; a database-table drop is a separate application-owned
migration, not an effect of CLI selection. Never delete replay receipts or other
retained authority merely because a feature is no longer selected. See the
[Notifications retention boundaries](/docs/packages/communication/notifications#native-queue-and-retained-replay-receipts).

Commit the managed output and `.playstack` state. Builds can then consume ordinary repository files without requiring the CLI or package registry at runtime.

If the application needs to own and modify an eligible source artifact, continue with [Ejection and merging](/docs/cli/ejection-merging).

## Getting started

Source: https://playstack.dev/docs/getting-started.md

You can adopt one Playstack package without replacing your framework or adopting the whole stack. All `@playstack/*` packages share one release version, regardless of access tier. Keep them on the same version in your application.

This quickstart and the package pages target beta 3, the release accompanying
this site rollout. Do not copy beta 3-only compositions into an older install.
Check [release availability](/docs/packages#release-availability)
and your approved package source before upgrading the complete graph.
Existing applications can follow the [beta 3 upgrade checklist](/changelog/beta-3-adoption-and-operator-controls#upgrade-checklist)
after verifying the exact published artifacts.

## Requirements

- Node.js 22 or newer for new server projects.
- TypeScript with modern ESM and package exports support.
- A terminal and an existing Node project, or an empty directory where you can run `npm init -y`.

## Install only what you use

First check [preview access](/docs/packages#access-policy). These examples assume your chosen version is available from your approved package source. For account-free public releases, use npm's default registry. If you have private registry access, add these lines to your project's `.npmrc`:

```ini
# .npmrc — the token comes from your environment
@playstack:registry=https://registry.playstack.dev/
//registry.playstack.dev/:_authToken=${PLAYSTACK_REGISTRY_TOKEN}
```

The URL and literal `${PLAYSTACK_REGISTRY_TOKEN}` placeholder can be committed. The real token must stay in your shell environment or CI secret store, never in Git. Set it using your approved secret manager before installing.

Run the install command separately in your terminal:

```sh
npm install --save-exact @playstack/storage@0.1.0-beta.4
```

The scope setting sends **all** `@playstack/*` requests to that registry, including Free packages. npm does not automatically fall back to public npm for a missing package. Confirm the selected release and its dependencies are available there.

## Your first working example

Save this as `quickstart.mjs`. It writes and reads a text file using an in-memory provider, so you do not need a database, cloud account or storage credentials. The same API can later use a real storage provider.

```js
import { createStorage } from '@playstack/storage'
import { MemoryStorageProvider } from '@playstack/storage/testing'

const storage = createStorage({
  driver: new MemoryStorageProvider(),
  prefix: 'quickstart',
})

await storage.put('hello.txt', 'Hello, Playstack!', {
  contentType: 'text/plain',
})

const object = await storage.get('hello.txt')
if (!object) throw new Error('Expected the object we just saved')

console.log(await new Response(object.body).text())
console.log(await storage.exists('hello.txt'))
```

Run it:

```sh
node quickstart.mjs
```

Expected output:

```text
Hello, Playstack!
true
```

You have now used a Playstack capability without adopting a framework or another service. `createStorage` owns the common API; the provider owns where bytes live. The memory provider is for learning and tests only: its contents disappear when the process exits. For durable storage, follow the [Storage guide](/docs/packages/infrastructure/storage) and choose a filesystem, S3 or Workers R2 provider.

## Choose your next step

- **Find another capability:** browse [Packages](/docs/packages) by the problem you want to solve.
- **Use an existing framework:** follow the [Next.js](/docs/frameworks/nextjs/getting-started), [React](/docs/frameworks/react/getting-started), [NestJS](/docs/frameworks/nestjs/getting-started) or [Workers](/docs/frameworks/workers/getting-started) integration guide. These assume an existing app and call out the services you must supply.
- **Look up an API:** open a package page for its examples and options, then use the installed TypeScript declarations for exact signatures. [Reading the package reference](/docs/packages#reading-the-reference) explains the distinction.

Add framework adapters only where they help. A Next application can use storage directly on the server; a Nest application can optionally register it through a module.

If Megamono owns your workspace structure, follow [Using Playstack with Megamono](/docs/megamono) for the handoff between app and database scaffolding, capability installation, schema compatibility, and application-owned migrations.

## When you add commands and events

You do not need operation context for the example above. When a capability produces events, context helps connect them to the request or job that caused them.

Create context once at the outside boundary and pass it into commands that produce events:

```ts
import type { OperationContext } from '@playstack/core'

export function contextFromRequest(requestId: string): OperationContext {
  return {
    correlationId: requestId,
    trace: { traceId: requestId },
  }
}
```

When one event causes another command, pass the source envelope as the cause. Playstack adapters preserve that relationship when they bridge domain events into the application event system.

## Moving from the example to an application

Create infrastructure clients in one application setup module (often called the **composition root**) and pass them into the packages that need them. That gives your application control over credentials, shutdown, access checks and tests without changing the domain API. Before production, read the chosen provider's limits and test failures against your real infrastructure.

## Follow package progress

Use the [status inventory](/status) for current package and adapter coverage, the [changelog](/changelog) for release notes, and [pricing](/pricing) for package access.

## Getting started with NestJS

Source: https://playstack.dev/docs/frameworks/nestjs/getting-started.md

This guide builds a protected account-scoped route. It deliberately keeps the configured domain services outside NestJS so they can also be used by jobs, scripts, tests, or another application edge.

## 1. Install capability and binding pairs

**Before you start:** use an existing NestJS app with configured [Auth sessions](/docs/packages/identity/auth), [Accounts](/docs/packages/identity/accounts) and [Entitlements](/docs/packages/identity/entitlements). The `sessions`, `accounts` and `entitlements` names below refer to those application-owned instances. These are composition examples, not a complete identity backend or database migration.

New to Playstack? Run the [storage quickstart](/docs/getting-started#your-first-working-example) first. Confirm [package access](/docs/packages#access-policy); the Accounts pair requires paid access, while a first capability does not have to use Accounts.

```sh
npm install \
  @playstack/auth @playstack/nest-auth \
  @playstack/accounts @playstack/nest-accounts \
  @playstack/entitlements @playstack/nest-entitlements
```

Keep every Playstack dependency on the same fixed version. Install only the pairs used by the first route; events, audit, queues, billing, and analytics can be composed later without changing these boundaries.

## 2. Create portable services

In an application composition module, configure `sessions`, `accounts`, and `entitlements` with application-owned persistence and infrastructure. These values are normal TypeScript services, not Nest-specific singletons.

Register their bindings at the application root:

```ts
import { Module } from '@nestjs/common'
import { PlaystackAuthModule } from '@playstack/nest-auth'
import { PlaystackAccountsModule } from '@playstack/nest-accounts'
import { PlaystackEntitlementsModule } from '@playstack/nest-entitlements'
import type { AccountRequest } from '@playstack/nest-accounts'

@Module({
  imports: [
    PlaystackAuthModule.forRoot({
      sessions,
      allowedOrigins: ['https://app.example.com'],
    }),
    PlaystackAccountsModule.forRoot({ accounts }),
    PlaystackEntitlementsModule.forRoot({
      entitlements,
      resolveSubject: (request) => {
        const account = (request as AccountRequest).playstackAccount

        if (!account) {
          throw new Error('Account context must be resolved first')
        }

        return { type: 'account', id: account.account.id }
      },
    }),
  ],
})
export class ApplicationModule {}
```

Use each package's `forRootAsync` form when construction depends on other Nest providers. The resulting factory should still return explicit configured services rather than hiding environment reads throughout feature modules.

## 3. Apply guards in trust order

```ts
import { Controller, Get, UseGuards } from '@nestjs/common'
import { PlaystackAuthGuard } from '@playstack/nest-auth'
import { PlaystackAccountGuard } from '@playstack/nest-accounts'
import { PlaystackEntitlementsGuard, RequireEntitlements } from '@playstack/nest-entitlements'

@Controller('/accounts/:accountId/packages')
export class PackagesController {
  @Get('/pro')
  @UseGuards(
    PlaystackAuthGuard,
    PlaystackAccountGuard,
    PlaystackEntitlementsGuard,
  )
  @RequireEntitlements('packages.pro')
  getProPackage(@CurrentAccount() account: AccountResolution) {
    return this.packages.forAccount(account.account.id)
  }
}
```

`PlaystackAccountGuard` revalidates membership rather than trusting the route parameter or stale session metadata. The entitlement subject resolver reads the context established by earlier guards; it does not infer an account from the URL.

For unsafe cookie-authenticated requests, run `PlaystackCsrfGuard` before `PlaystackAuthGuard`, as the `cookie` composition does, so a forged request is refused before any session is read; routes that set or clear session cookies add `PlaystackCookieCsrfGuard`, which is never bearer-exempt. Bearer-authenticated requests do not use ambient browser credentials and follow the auth adapter's fixed CSRF behavior.

The application owns the composition. Hand-place guards as above, or name a composition once and place `PlaystackGuardStack` wherever it belongs: `guards: 'cookie'` or `'bearer'` selects a default per transport profile, and an ordered list mixes package guards, other bindings and your own guards, for example `[PlaystackOriginGuard, TenantGuard, PlaystackAuthGuard, PlaystackAccountGuard]`. `globalGuards: true` installs the composed stack globally, with `@Public()` opting routes out of the session check. No package installs a guard on its own.

## 4. Add operational boundaries explicitly

- Register [`@playstack/nest-errors`](/docs/packages/foundation/nest-errors) with an existing reporter and choose where its exception filter applies.
- Register [`@playstack/nest-events`](/docs/packages/events-operations/nest-events) when domain events need outbox pumping or queued observation.
- Add [`@playstack/nest-rate-limit`](/docs/packages/events-operations/nest-rate-limit) only after trusted caller and client-IP resolution are available.
- Bind queued consumers in the worker process that owns them rather than starting every consumer inside the HTTP application.

## Verify the boundary

- No Playstack module is silently global.
- Authentication precedes account, entitlement, role, and rate-limit checks.
- Every account-scoped request revalidates current membership.
- Database transactions remain application-visible.
- Decorators declare metadata but domain services perform mutations and emit events.
- Worker consumers and shutdown behavior are wired by the application.

## Getting started with Next.js

Source: https://playstack.dev/docs/frameworks/nextjs/getting-started.md

This path composes Playstack authentication around an existing Next.js application. Auth.js can continue to own provider handshakes and cookies while Playstack owns normalized identities, account linking policy, opaque sessions, and the safe session projection exposed to application code.

## 1. Install one fixed package version

**Before you start:** this is an integration guide for an existing Next.js app, not a complete sign-up server. For a runnable first program, try the [storage quickstart](/docs/getting-started#your-first-working-example). Confirm [package access](/docs/packages#access-policy) before installing.

The composition examples below require these application-owned pieces:

- `auth`, including the example's `authenticateProvider` wrapper: your configured identity service and provider-login policy. Start with [Auth](/docs/packages/identity/auth) and the [Auth.js bridge](/docs/packages/identity/auth-authjs).
- `activeAccountFor` and `accounts.ensurePersonalAccount`: your account-selection and onboarding functions, not exports supplied by the bridge. See [Accounts](/docs/packages/identity/accounts).
- `readCsrfCookie`, session resolvers and HTTP routes: your cookie/session transport. See [Auth React](/docs/packages/identity/auth-react) and [Auth Next](/docs/packages/identity/auth-next).
- `Dashboard`, `feedDefinition` and `streamPublishedPosts`: your UI and product content, needed only for the examples that use them.

Keep server services out of browser bundles. Auth.js is one optional composition, not a prerequisite for using Playstack in Next.js.

Install the portable capability and only the edge bindings you need:

```sh
npm install @playstack/auth @playstack/auth-authjs @playstack/auth-react next-auth
```

Add `@playstack/auth-next` when either router needs server-side session handoff or protection. Add `@playstack/feeds` and `@playstack/feeds-next` only when the application publishes feeds or sitemaps.

## 2. Build the server service first

Create the `@playstack/auth` service in a server-only composition module. The application supplies persistence, time, identifiers, secrets, and event delivery. Export the configured service rather than reconstructing it in individual route files.

Provider profile normalization is application policy. Connect Auth.js only after that policy is explicit:

```ts
import { createAuthJsBridge } from '@playstack/auth-authjs'

export const authJsBridge = createAuthJsBridge({
  normalizeProfile: ({ provider, providerAccountId, profile }) => ({
    providerId: provider,
    providerUserId: providerAccountId,
    email: profile.email,
    emailVerified: profile.email_verified === true,
    name: profile.name,
    avatarUrl: profile.avatar_url,
    raw: profile,
  }),
  authenticate: (profile) => auth.authenticateProvider(profile),
  resolveAccount: (userId) => activeAccountFor(userId),
  onAuthenticated: (user) => accounts.ensurePersonalAccount(user.id),
})
```

Attach `authJsBridge.callbacks` to the application's Auth.js configuration. Do not enable dangerous email account linking: the portable auth service owns linking decisions, and the application must state whether a provider email is verified.

## 3. Hand only the safe session to React

Connect the application routes expected by the React transport, then place one provider near the application root:

```tsx
import { PlaystackAuthProvider, createFetchAuthTransport } from '@playstack/auth-react'

const transport = createFetchAuthTransport({
  headers: () => ({ 'x-csrf-token': readCsrfCookie() }),
})

export function ApplicationProviders({ children, session }) {
  return (
    <PlaystackAuthProvider transport={transport} initialSession={session}>
      {children}
    </PlaystackAuthProvider>
  )
}
```

This is the same-origin cookie transport: the Next routes and the page share an origin, so the CSRF cookie is readable and the session cookie is ambient. A Next app talking to an API on another origin uses `createBearerAuthTransport` instead, without a CSRF header (see the auth-react package page). The initial value is the narrow Playstack session view, never the Auth.js provider token or raw profile. Pass `null` when the server has established that the request is unauthenticated; omit the property only when the client should load the session itself.

Client guards improve navigation but do not protect data. Route handlers, server actions, API operations, and server-rendered pages must validate the session and account scope again at their trusted boundary.

## 4. Use the router-specific adapter where it fits

Pages Router applications can protect SSR handlers with [`@playstack/auth-next/pages`](/docs/packages/identity/auth-next):

```ts
import { createAuthNextHelpers } from '@playstack/auth-next/pages'

const { requireSession } = createAuthNextHelpers({
  resolveSession: ({ req }) => resolveSafeSessionView(req),
})

export const getServerSideProps = requireSession(
  { redirectTo: '/login' },
  async ({ session }) => ({ props: { userId: session.user.id } }),
)
```

App Router applications use the dedicated structural helper while retaining Next's redirect and request behavior at the application edge:

```tsx
import { redirect } from 'next/navigation'

import { createAppRouterAuthHelpers } from '@playstack/auth-next/app'

const auth = createAppRouterAuthHelpers({
  resolveSession: () => resolveSessionViewFromCookies(),
})

export default async function DashboardPage() {
  const session = await auth.requireSession({
    redirectTo: '/login',
    onUnauthenticated: (destination) => redirect(destination ?? '/login'),
  })

  return <Dashboard userId={session.user.id} />
}
```

The same helper wraps route handlers and passes the original request and route context through unchanged. Middleware, React Server Component cache policy, and server-action authorization remain explicit application decisions.

## 5. Add route capabilities independently

For example, a small feed can be exposed from an App Router route without introducing a Next.js runtime dependency into the feed domain:

```ts
import { createInlineFeedRoute } from '@playstack/feeds-next'

export const GET = createInlineFeedRoute({
  format: 'rss',
  definition: feedDefinition,
  entries: () => streamPublishedPosts(),
  content: 'full',
  cacheControl: 'public, max-age=60',
})
```

See [`@playstack/feeds-next`](/docs/packages/content-intelligence/feeds-next) for generated-document storage, dynamic paths, and failure behavior.

## Verify the boundary

Before adding another package, confirm that:

- provider tokens and raw profiles remain server-only;
- all Playstack packages use one version;
- server operations validate session and account scope;
- client providers receive only safe serializable projections;
- framework, provider, and database clients are constructed by the application;
- each additional adapter corresponds to a lifecycle boundary the application actually uses.

## Optional application UI

[Chakra](/docs/packages/ui/chakra), [App UI](/docs/packages/ui/app-ui) and [Admin UI](/docs/packages/ui/admin-ui) add themes, controlled application compositions and the initial scoped members section. These are independent of auth/backend adoption. The host owns routes, data, mutations and authorization; Next Client Components hold callbacks and the Chakra system. See [Application interfaces](/features/application-ui).

## Getting started with React

Source: https://playstack.dev/docs/frameworks/react/getting-started.md

Start with one client capability and one operational boundary. This guide supplies session state to the component tree and reports render failures without coupling the application to a particular auth server, router, or monitoring vendor.

## 1. Install the core and binding pairs

**Before you start:** use an existing React app and a working authentication API. This guide wires that API to React; it does not create a backend. For a complete first program without a server, try the [storage quickstart](/docs/getting-started#your-first-working-example). Confirm [package access](/docs/packages#access-policy) before installing.

The composition example assumes `errors` is a configured [Errors service](/docs/packages/foundation/errors), `ErrorScreen` is your recovery UI, and `initialSession` is the safe session value from your server. `readCsrfCookie` is your cookie-reading helper for the same-origin option, not a Playstack export. Choose one transport using the [Auth React guide](/docs/packages/identity/auth-react); the example API hostname is a placeholder for your own service.

```sh
npm install @playstack/auth @playstack/auth-react @playstack/errors @playstack/errors-react react
```

Keep all `@playstack/*` dependencies on the same fixed version. The React packages are bindings, so their portable core peers remain explicit application dependencies.

## 2. Configure application-owned edges

Create the error reporter with the drivers your application already uses. Create the auth transport that matches where the API lives. When the API is served from the same origin as the page, the cookie transport carries the session cookie and echoes the CSRF cookie your routes issue; when the API is a different origin, the bearer transport keeps a short-lived access token in memory and needs no CSRF header, because the API checks `Origin` and `Sec-Fetch-Site` instead:

```tsx
import {
  PlaystackAuthProvider,
  createBearerAuthTransport,
  createFetchAuthTransport,
} from '@playstack/auth-react'
import { ErrorsBoundary, ErrorsProvider } from '@playstack/errors-react'

// Same origin: cookies plus the synchronizer header your routes require.
const sameOriginTransport = createFetchAuthTransport({
  headers: () => ({ 'x-csrf-token': readCsrfCookie() }),
})

// Split origin: HTTPS (or loopback in development), no CSRF header.
const transport = createBearerAuthTransport({
  baseUrl: 'https://api.example.com',
})

export function ApplicationRoot({ children, initialSession }) {
  return (
    <ErrorsProvider errors={errors}>
      <ErrorsBoundary
        fallback={({ error, reset }) => (
          <ErrorScreen error={error} onRetry={reset} />
        )}
      >
        <PlaystackAuthProvider
          transport={transport}
          initialSession={initialSession}
        >
          {children}
        </PlaystackAuthProvider>
      </ErrorsBoundary>
    </ErrorsProvider>
  )
}
```

Either transport includes cookies but never reads an HTTP-only cookie. The application supplies the synchronizer header only for the same-origin cookie transport and may replace the endpoint paths or fetch implementation on both. After an account switch with the bearer transport, call `refresh` from `useAuth()` so the transport adopts the rotated refresh cookie.

## 3. Read state without hiding loading

```tsx
import { RequireSession, useAuth, useSession } from '@playstack/auth-react'

function AccountArea() {
  const { status, user, error } = useSession()
  const { signOut } = useAuth()

  if (status === 'loading') return <LoadingAccount />
  if (error) return <SessionProblem error={error} />

  return (
    <RequireSession fallback={<SignInPrompt />}>
      <p>Signed in as {user?.name}</p>
      <button onClick={() => signOut()}>Sign out</button>
    </RequireSession>
  )
}
```

Treat `RequireSession` as navigation UX. The API serving the protected data must independently verify the session and authorization context.

## 4. Add providers as capabilities appear

- Add [`@playstack/accounts-react`](/docs/packages/identity/accounts-react) when users can switch account context. The server still validates membership on each scoped request.
- Add [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react) after the core analytics instance has restored durable consent.
- Use `useErrors()` for event-handler and effect failures so they pass through the same scrubbed reporter as render errors.

Keep provider configuration in one application composition module. Components should consume the narrow hooks, while infrastructure clients and secrets stay outside the React tree.

## Optional application UI

[Chakra](/docs/packages/ui/chakra), [App UI](/docs/packages/ui/app-ui) and [Admin UI](/docs/packages/ui/admin-ui) add themes, controlled application compositions and the initial scoped members section. These are independent of auth/backend adoption. The host owns routes, data, mutations and authorization; Next Client Components hold callbacks and the Chakra system. See [Application interfaces](/features/application-ui).

## Getting started with Workers

Source: https://playstack.dev/docs/frameworks/workers/getting-started.md

This guide demonstrates two edge-safe boundaries: a reusable cryptography service configured from Worker bindings, and a streaming RSS response returned directly from `fetch`.

## 1. Install portable packages

**Before you start:** use an existing Worker project with its generated `Env` type. These composition examples assume you supply encryption/signing key bindings, a content binding, `feedDefinition` and `streamPublishedEntries`. See [Crypto](/docs/packages/foundation/crypto) for key configuration and [Feeds](/docs/packages/content-intelligence/feeds) for content shapes. They do not provision infrastructure or secrets. For a complete local first program, use the [storage quickstart](/docs/getting-started#your-first-working-example), then return here for edge deployment.

```sh
npm install @playstack/core @playstack/crypto @playstack/feeds
```

These packages use Web platform primitives. Keep them on the same fixed Playstack version, confirm [package access](/docs/packages#access-policy), and run the relevant Worker tests for your selected version. Portable APIs alone do not establish deployment compatibility.

## 2. Configure Web Crypto explicitly

Convert environment bindings into package configuration in the application entry. Do not let the package read ambient environment variables:

```ts
import { systemClock } from '@playstack/core'
import { createCrypto } from '@playstack/crypto'

function cryptoFor(env: Env) {
  return createCrypto({
    crypto: globalThis.crypto,
    clock: systemClock(),
    encryptionKeys: JSON.parse(env.PLAYSTACK_ENCRYPTION_KEYS),
    signingKeys: JSON.parse(env.PLAYSTACK_SIGNING_KEYS),
  })
}
```

Each keyset must contain exactly one active key. Retain inactive keys while existing ciphertext or signed tokens may still reference them. Supply an atomic replay store before verifying single-use tokens.

If configuration is deployment-stable, parse it once at module scope or cache the configured service by binding identity. Request-specific actors and correlation metadata belong in operation context, not global service state.

## 3. Return a native streaming response

`@playstack/feeds` can stream directly from a Worker without a Next.js or Node.js adapter:

```ts
import { feedResponse } from '@playstack/feeds'

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url)

    if (url.pathname !== '/feed.xml') {
      return new Response('Not found', { status: 404 })
    }

    return feedResponse(
      'rss',
      feedDefinition,
      streamPublishedEntries(env.CONTENT),
      { content: 'full', cacheControl: 'public, max-age=60' },
    )
  },
}
```

The returned value is a standard `Response`, and the body is a standard `ReadableStream`. For large generated feeds, use the structural R2 storage adapter and publish a committed generation instead of rebuilding the document on every request.

## 4. Add edge persistence by contract

- Use [`@playstack/rate-limit-do`](/docs/packages/events-operations/rate-limit-do) when rate-limit state needs Durable Object atomicity.
- Use the R2 feed storage implementation from [`@playstack/feeds`](/docs/packages/content-intelligence/feeds) for generated documents.
- Provide application adapters for authentication, accounts, audit, and events rather than importing Node-specific Prisma or NestJS bindings.
- Queue observational events after the domain mutation has reached its own durable boundary.

## Verify the bundle

- Run the package's Worker compatibility target before deployment.
- Inspect the bundle for accidental Node built-ins and framework adapters.
- Keep raw subject identifiers out of rate-limit storage keys.
- Verify HMACs and webhooks against the exact request bytes.
- Treat `waitUntil` as delivery scheduling, not as a substitute for transactional durability.

## Identity and access

Source: https://playstack.dev/docs/packages/identity.md

Identity packages separate who a user is, which account they are acting within, which device or credential is involved, and what the application is allowed to do next.

## Authentication and accounts

| Package                                                               | Plan                        | Responsibility                                       |
| --------------------------------------------------------------------- | --------------------------- | ---------------------------------------------------- |
| [`@playstack/auth`](/docs/packages/identity/auth)                     | <PlanTierDot tier="free" /> | Identity and opaque session primitives.              |
| [`@playstack/auth-contracts`](/docs/packages/identity/auth-contracts) | <PlanTierDot tier="free" /> | Client-safe session projection and parsing.          |
| [`@playstack/auth-react`](/docs/packages/identity/auth-react)         | <PlanTierDot tier="pro" />  | React session state and client auth actions.         |
| [`@playstack/auth-next`](/docs/packages/identity/auth-next)           | <PlanTierDot tier="pro" />  | Pages and App Router session handoff and protection. |
| [`@playstack/auth-authjs`](/docs/packages/identity/auth-authjs)       | <PlanTierDot tier="pro" />  | Auth.js callback and safe-session bridge.            |
| [`@playstack/nest-auth`](/docs/packages/identity/nest-auth)           | <PlanTierDot tier="pro" />  | NestJS guards, decorators, and CSRF transport.       |
| [`@playstack/accounts`](/docs/packages/identity/accounts)             | <PlanTierDot tier="free" /> | Tenancy, membership, invitations, and account scope. |
| [`@playstack/accounts-react`](/docs/packages/identity/accounts-react) | <PlanTierDot tier="pro" />  | React account-list and active-account state.         |
| [`@playstack/nest-accounts`](/docs/packages/identity/nest-accounts)   | <PlanTierDot tier="pro" />  | NestJS account scope and role guards.                |
| [`@playstack/client-auth`](/docs/packages/identity/client-auth)       | <PlanTierDot tier="free" /> | PKCE authentication for public native clients.       |

## Trust and credentials

| Package                                                                 | Plan                        | Responsibility                                           |
| ----------------------------------------------------------------------- | --------------------------- | -------------------------------------------------------- |
| [`@playstack/devices`](/docs/packages/identity/devices)                 | <PlanTierDot tier="free" /> | Registered devices, push targets, trust, and revocation. |
| [`@playstack/mfa`](/docs/packages/identity/mfa)                         | <PlanTierDot tier="free" /> | Passkeys, TOTP, recovery, and recent-auth proofs.        |
| [`@playstack/api-keys`](/docs/packages/identity/api-keys)               | <PlanTierDot tier="free" /> | Customer API-key issuance, scopes, and revocation.       |
| [`@playstack/nest-api-keys`](/docs/packages/identity/nest-api-keys)     | <PlanTierDot tier="pro" />  | NestJS API-key guards and rate-limit declarations.       |
| [`@playstack/connections`](/docs/packages/identity/connections)         | <PlanTierDot tier="free" /> | Encrypted third-party API credentials and refresh.       |
| [`@playstack/atproto`](/docs/packages/identity/atproto)                 | <PlanTierDot tier="pro" />  | DPoP-aware AT Protocol OAuth lifecycle.                  |
| [`@playstack/extension`](/docs/packages/identity/extension)             | <PlanTierDot tier="free" /> | MV3-safe extension auth, messaging, and storage.         |
| [`@playstack/extension-react`](/docs/packages/identity/extension-react) | <PlanTierDot tier="pro" />  | Reactive extension session and permission state.         |
| [`@playstack/audit`](/docs/packages/identity/audit)                     | <PlanTierDot tier="free" /> | Tamper-evident per-scope audit chains.                   |
| [`@playstack/nest-audit`](/docs/packages/identity/nest-audit)           | <PlanTierDot tier="pro" />  | Explicit NestJS route audit declarations.                |

## Billing and platform access

| Package                                                                     | Plan                        | Responsibility                                                  |
| --------------------------------------------------------------------------- | --------------------------- | --------------------------------------------------------------- |
| [`@playstack/billing`](/docs/packages/identity/billing)                     | <PlanTierDot tier="free" /> | Provider-neutral subscription projection and checkout boundary. |
| [`@playstack/nest-billing`](/docs/packages/identity/nest-billing)           | <PlanTierDot tier="pro" />  | NestJS account-aware billing composition.                       |
| [`@playstack/entitlements`](/docs/packages/identity/entitlements)           | <PlanTierDot tier="free" /> | Source-projected capabilities and numeric limits.               |
| [`@playstack/nest-entitlements`](/docs/packages/identity/nest-entitlements) | <PlanTierDot tier="pro" />  | NestJS entitlement guards and subject resolution.               |
| [`@playstack/github-app`](/docs/packages/identity/github-app)               | <PlanTierDot tier="free" /> | Scoped GitHub App installation-token lifecycle.                 |
| [`@playstack/nest-github-app`](/docs/packages/identity/nest-github-app)     | <PlanTierDot tier="pro" />  | NestJS binding for the GitHub App service.                      |

## Licensing

| Package                                                                   | Plan                        | Responsibility                                                    |
| ------------------------------------------------------------------------- | --------------------------- | ----------------------------------------------------------------- |
| [`@playstack/licensing`](/docs/packages/identity/licensing)               | <PlanTierDot tier="free" /> | Portable signed-license parsing and offline verification.         |
| [`@playstack/licensing-issuer`](/docs/packages/identity/licensing-issuer) | <PlanTierDot tier="free" /> | Server-side issuance, activation, refresh, seats, and revocation. |
| [`@playstack/nest-licensing`](/docs/packages/identity/nest-licensing)     | <PlanTierDot tier="pro" />  | NestJS client and administration composition for license issuers. |

## Keep the boundaries separate

Authentication proves identity. Accounts establish tenancy and membership. API keys authenticate external callers. Connections hold credentials for acting against another provider. Audit records what happened. Keeping those jobs separate prevents one package from becoming the authorization policy for an entire application.

## Next.js

Source: https://playstack.dev/docs/frameworks/nextjs.md

Playstack treats Next.js as an application edge. Portable packages own authentication, accounts, validation, events, feeds, and other product behavior; Next.js decides where those capabilities enter through server rendering, route handlers, React providers, and application lifecycle.

[Start with the Next.js guide](/docs/frameworks/nextjs/getting-started) for a minimal authentication composition, then add only the capability adapters your application uses.

## Available package layers

| Need                        | Portable package                                                     | Next.js or React edge                                                                                                                                    |
| --------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authentication and sessions | [`@playstack/auth`](/docs/packages/identity/auth)                    | `@playstack/auth-authjs`, [`@playstack/auth-next`](/docs/packages/identity/auth-next), and [`@playstack/auth-react`](/docs/packages/identity/auth-react) |
| Account selection           | [`@playstack/accounts`](/docs/packages/identity/accounts)            | [`@playstack/accounts-react`](/docs/packages/identity/accounts-react)                                                                                    |
| Error reporting             | [`@playstack/errors`](/docs/packages/foundation/errors)              | [`@playstack/errors-react`](/docs/packages/foundation/errors-react)                                                                                      |
| Analytics and consent       | [`@playstack/analytics`](/docs/packages/events-operations/analytics) | [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react)                                                                         |
| Audience signup and preferences | [`@playstack/audiences`](/docs/packages/communication/audiences) | [`@playstack/audiences-react`](/docs/packages/communication/audiences-react)                                                                              |
| In-app notification inbox   | [`@playstack/notifications`](/docs/packages/communication/notifications) | [`@playstack/notifications-react`](/docs/packages/communication/notifications-react)                                                                  |
| Feeds and sitemaps          | [`@playstack/feeds`](/docs/packages/content-intelligence/feeds)      | [`@playstack/feeds-next`](/docs/packages/content-intelligence/feeds-next)                                                                                |
| Content transforms          | [`@playstack/content`](/docs/packages/content-intelligence/content)  | [`@playstack/content-next`](/docs/packages/content-intelligence/content-next) for explicit Pages and App Router document lookup.                          |
| Shared validation           | [`@playstack/validation`](/docs/packages/foundation/validation)      | Use the same schemas in forms, server actions, and route handlers.                                                                                       |

All `@playstack/*` packages in an application should use the same fixed version.

## App Router and Pages Router

The adapters describe their actual router boundary rather than claiming universal Next.js support:

- `@playstack/auth-next/pages` provides Pages Router `getServerSideProps` session handoff and protection.
- `@playstack/auth-next/app` validates optional or required sessions in Server Components and route handlers while accepting application-owned redirect or response behavior.
- `@playstack/content-next/pages` supplies static paths, static props, and server-side props; `@playstack/content-next/app` supplies static params and document lookup for direct or promised route params.
- `@playstack/analytics-react/next-pages` observes completed Pages Router navigation; `@playstack/analytics-react/next-app` observes committed pathname and search-parameter changes.
- `@playstack/feeds-next` produces App Router-compatible `GET` and `HEAD` handlers using Web `Request`, `Response`, and streams.
- `@playstack/auth-authjs` bridges Auth.js callbacks to the portable authentication service and is not tied to one router.
- Audience and notification React providers work beneath either router and call application-owned routes; they do not expose signed audience tokens or establish inbox authorization.
- React providers work beneath either router. Server-side authorization remains mandatory regardless of client navigation guards.

## A common composition

```text
Auth.js callbacks ----> @playstack/auth-authjs ----> @playstack/auth
Next server boundary -> @playstack/auth-next -----> safe session view
React tree ----------> @playstack/auth-react -----> application HTTP routes
Next content route --> @playstack/content-next ---> @playstack/content
Route handlers ------> @playstack/feeds-next -----> @playstack/feeds
```

Keep provider SDKs, cookies, environment loading, and database clients in the application composition root. Playstack adapters accept those boundaries or structural callbacks; they do not create hidden global clients.

## Continue by capability

After the initial composition, use the [package catalog](/docs/packages) for complete configuration, persistence, event, and bridge contracts. A package page is canonical when a framework guide and an API detail appear to overlap.

## Optional application UI

[Chakra](/docs/packages/ui/chakra), [App UI](/docs/packages/ui/app-ui) and [Admin UI](/docs/packages/ui/admin-ui) add themes, controlled application compositions and the initial scoped members section. These are independent of auth/backend adoption. The host owns routes, data, mutations and authorization; Next Client Components hold callbacks and the Chakra system. See [Application interfaces](/features/application-ui).

## @playstack/app-ui

Source: https://playstack.dev/docs/packages/ui/app-ui.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

App UI sits above [Chakra themes](/docs/packages/ui/chakra) and below [Admin UI](/docs/packages/ui/admin-ui). It has no mandatory Next, auth, accounts, database, router, table-engine or drag-and-drop dependency.

| Composition                                | Current behavior                                                                                                                                 |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| AppShell / Sidebar / Navbar                | Sidebar or top navigation, mobile inline disclosure, host routes and active links.                                                               |
| PageHeader / PageSection / ContentState    | Heading/actions, stacked or column settings, loading/empty/error/denied presentation.                                                            |
| AccountSwitcher / AccountPicker / UserMenu | Minimal native selection or searchable dialog with avatars/initials, badges, descriptions and create/manage actions; no token/session authority. |
| DataTable / collection controls            | Semantic rows, sort intent, eligible-page selection, search, filters, pagination and bulk actions.                                               |
| ColumnSettings / table views               | Controlled visibility, numeric widths and logical pinning. Pure preference helpers at `@playstack/app-ui/table-view`.                            |
| CommandPalette                             | Local or abortable async sources, keyboard selection, scoped cancellation and safe failures.                                                     |
| KanbanBoard                                | Controlled columns/cards and explicit destination controls; serialized moves, scope checks and cancellation.                                     |
| SplitView / ResizableSplitView             | Static panes or controlled pointer/keyboard sizing, RTL behavior, bounds and narrow-container stacking.                                          |

```tsx
'use client'
import { useState, type ReactNode } from 'react'
import { ResizableSplitView } from '@playstack/app-ui'

export function MasterDetail({ list, detail }: { list: ReactNode; detail: ReactNode }) {
  const [size, setSize] = useState(60)
  return <ResizableSplitView primaryLabel="Items" secondaryLabel="Details" primary={list} secondary={detail} value={size} onValueChange={setSize} />
}
```

Compose under PlaystackUIProvider or a compatible ChakraProvider. Interactive callbacks belong in Client Components; flat layout exports can accept serializable server content. Keep query state, routes, persistent preferences, mutation idempotency and server authorization in the host. Abort stops obsolete UI work, not a server transaction.

The table is not a full ARIA grid. Pointer Kanban reordering, virtualized/editable grids, tours, a collapsed desktop rail and modal mobile navigation remain open. Resizing preserves mounted panes; account/authority changes require deliberate host state reset.

The components render under Next and React 18 and 19, including server-side rendering. Pointer geometry, touch, screen-reader behavior and your product's authorization are yours to test in the real application.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/app-ui@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Account selection and localization

AccountPicker accepts host-controlled `pending` and safe `error` state, skips disabled choices in keyboard navigation and restores trigger focus on close. The host supplies authorized accounts and performs switching; the component never grants membership or changes server authority.

Package-owned visible and accessible copy now consumes `@playstack/i18n-react`, including account search, tables, commands, boards and splitter instructions. `appUICatalog` at `@playstack/app-ui/messages` is data-only; `./locales/en.json` and the `app-ui.messages.en` artifact expose editable English source. Explicit component overrides win, and no-provider usage retains English defaults. Names, actions and application data remain host-owned. See [UI localization](/docs/frameworks/ui-localization).

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/app-ui@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/app-ui/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/app-ui` | `./dist/index.d.ts` |
| `@playstack/app-ui/table-view` | `./dist/table-view.d.ts` |
| `@playstack/app-ui/package.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/app-ui/messages` | `./dist/messages.d.ts` |
| `@playstack/app-ui/locales/en.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/app-ui/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@chakra-ui/react` | `^3.0.0` | Required by the package. |
| `@emotion/react` | `^11.0.0` | Required by the package. |
| `@playstack/chakra` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |
| `react-dom` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |
| `@playstack/i18n-react` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/auth-contracts

Source: https://playstack.dev/docs/packages/identity/auth-contracts.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/auth-contracts` contains only the safe session projection, parser, and transport-facing types shared by Playstack clients. It has no persistence, crypto, framework, or server-auth dependency.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/auth-contracts@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Adopt the client independently

```ts
import { createAuthSessionViewParser } from '@playstack/auth-contracts'

export const parseSession = createAuthSessionViewParser((value) => sessionExtensionsSchema.parse(value))
```

The default parser rejects unknown fields and credentials. An application can expose additional JSON-safe session data only through an explicit extension parser.

Use this package when an existing backend continues to own authentication while `@playstack/auth-react` or `@playstack/auth-next` adopts Playstack's client state and transport conventions.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/auth-contracts@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/auth-contracts` | `./dist/index.d.ts` |
| `@playstack/auth-contracts/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/auth-react

Source: https://playstack.dev/docs/packages/identity/auth-react.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/auth-react` provides headless session state, client actions, revalidation, and cross-tab synchronization around a server-owned `@playstack/auth` session.

## Install and compose

```sh
npm install @playstack/auth @playstack/auth-react react
```

```tsx
import {
  PlaystackAuthProvider,
  createFetchAuthTransport,
} from '@playstack/auth-react'

const transport = createFetchAuthTransport({
  baseUrl: 'https://api.example.com',
  headers: () => ({ 'x-csrf-token': readCsrfCookie() }),
})

<PlaystackAuthProvider transport={transport} initialSession={initialSession}>
  <App />
</PlaystackAuthProvider>
```

Omit `initialSession` to fetch on mount, pass `null` when SSR proved the request unauthenticated, or pass a validated session to render authenticated immediately.

## Provider properties

| Property | Default | Purpose |
| --- | --- | --- |
| `transport` | Required | Server communication bridge. |
| `initialSession` | `undefined` | Initial safe projection; controls the first loading state. |
| `revalidateOnFocus` | `true` | Refresh state when the window regains focus. |
| `refreshBeforeMs` | `60000` | Refresh this long before session expiry. |
| `sync` | Browser sync | Custom `AuthSync`, default cross-tab sync, or `false`. |
| `onError` | None | Isolated error observer. |

`useSession()` returns status, session, user, error, login progression, and `revalidate`. `useAuth()` returns `signIn`, `completeSignIn`, `cancelSignIn`, `signOut`, `refresh`, and `replaceSession`. `refresh` re-establishes the session through the transport's refresh path and applies the result; call it after the server rotated the credential, such as an account switch with the bearer transport. Every replacement is validated against `AuthSessionView`.

## Pending login and MFA

`signIn` returns either a validated session or
`{ status: 'mfa-required', challengeId, expiresAt, methods }`. Available methods
are `totp`, `passkey` and `recoveryCode`. `useSession().login` distinguishes `idle`,
`signing-in`, `pending` and `submitting`; the latter two carry the current challenge.
Pending login has no session/user and remains unauthenticated. It never opens
`RequireSession` or broadcasts session authority.

Configure `completeSignInEndpoint` explicitly on either HTTP transport. Sign-in
and completion accept exactly `{ pendingLogin: AuthPendingLogin }` or the existing
completed session envelope. Mixed session/credential fields refuse. Call
`completeSignIn(displayedChallengeId, input)` to submit the current challenge;
only a validated final session establishes identity. The host supplies proof
input and owns passkey ceremonies, routes and UI.

Wrong/expired challenge IDs and overlapping completions refuse before submission.
`cancelSignIn()` retires local progression, and stale completions cannot restore
it. Background session reads/refresh do not bypass a pending challenge. Qualified
retryable failures permit explicit retry; uncertain/nonretryable failures retire
the challenge without an automatic resend. Metadata stays in instance memory.
The server owns challenge binding, expiration, limits, consumption, MFA policy
and atomic session issuance; it must not issue authenticated credentials from a
pending response. Local cancellation does not revoke server authority
or undo cookies; actual browser/server integration still needs qualification.

Playstack's local TLS Chromium fixture exercises mounted pending/completion flows
with both cookie and bearer transports through workspace-built public exports.
Its separate loopback origins are cross-origin and same-site. That evidence does
not qualify product challenge issuance, live passkeys, cross-site cookie behavior
or adopter database/server integration. Maintainers run it with
`pnpm run test:auth:browser`; selective CI and the local packages lane include it.

## Transport bridge

```ts
interface AuthTransport {
  client: unknown
  getSession(): Promise<AuthSessionView | null>
  refresh(): Promise<AuthSessionView | null>
  signIn(input?: unknown): Promise<AuthSignInResult>
  completeSignIn?(challengeId: string, input?: unknown): Promise<AuthSignInResult>
  cancelSignIn?(): void
  signOut(): Promise<void>
}
```

The included fetch transport defaults to `GET /api/auth/session` and `POST /api/auth/refresh`, `/api/auth/sign-in`, and `/api/auth/sign-out`, always including cookies. Configure `fetch`, `baseUrl`, any endpoint path, and a sync/async headers callback. A session 401 receives exactly one refresh attempt.

## Split-origin browser applications

Use the bearer profile when the browser application and API are hosted on
different origins:

```ts
import { createBearerAuthTransport } from '@playstack/auth-react'

const transport = createBearerAuthTransport({
  baseUrl: 'https://api.example.com',
})
```

The API keeps the rotating refresh credential in a secure HTTP-only cookie and
returns exactly `{ session, accessToken, accessExpiresAt }` from sign-in and
refresh. The short-lived opaque access token stays in transport memory and is
sent as a bearer credential. A reload bootstraps a new access token through the
refresh cookie; responses that expose a refresh token or unknown credential
fields fail closed.

A cross-origin page cannot read a CSRF cookie set on the API origin, so there
is no synchronizer header in this profile. The API protects its cookie-bearing
routes with `Origin` and `Sec-Fetch-Site` checks (`PlaystackOriginGuard` in
`@playstack/nest-auth`) and enables CORS with credentials for the
application's origins only; the application still owns those allowlists, the
routes, and the cookie attributes. Those checks apply to browser requests
only: a client that sends no `Origin`, no Fetch Metadata and no cookie has
no ambient credential, so a CLI, a mobile app or a server can reach the same
routes with its bearer token, and the API may hand such a client the whole
token pair in the body instead of a cookie. `baseUrl` must be
HTTPS except for loopback hosts, which browsers treat as secure contexts, so
local development can point at `http://127.0.0.1:<port>` without a TLS proxy.

An account switch that issues a new session on the server also rotates the
refresh cookie. Pass `refresh` from `useAuth()` as the accounts provider's
`onSessionChange` so the transport adopts the cookie and obtains the access
token for the new session:

```tsx
const { session } = useSession()
const { refresh } = useAuth()

<PlaystackAccountsProvider client={accounts} session={session} onSessionChange={refresh}>
  {children}
</PlaystackAccountsProvider>
```

## Sync and token-storage bridges

`AuthSync` exposes `publish`, `subscribe`, and `close` plus its native `client`. The browser implementation uses `BroadcastChannel` with a storage-event fallback.

`TokenStorage` exposes `get`, `set`, and `clear` plus its native `client` for non-cookie applications. `createExpoTokenStorage(SecureStore, { key? })` stores only opaque tokens and numeric expiries and rejects expanded or malformed records.

## Guards and boundary

`RequireSession` accepts `loadingFallback`, unauthenticated `fallback`, and children. It is navigation UX only—not a server security or account authorization boundary. React and all Playstack peers remain application-controlled at the same fixed version.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/auth-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/auth-react` | `./dist/index.d.ts` |
| `@playstack/auth-react/errors` | `./dist/errors.d.ts` |
| `@playstack/auth-react/expo` | `./dist/expo.d.ts` |
| `@playstack/auth-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/auth-contracts` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/cache-next

Source: https://playstack.dev/docs/packages/infrastructure/cache-next.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/cache-next` maps an application-owned `@playstack/cache` driver onto Next.js caching without importing framework internals.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/cache-next@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Choose the Next.js cache boundary

- `createNextIncrementalCacheHandler()` supports the stable singular `cacheHandler` used by ISR, fetch caching, and Pages/App Router output from Next 14.1 onward.
- `createNextCacheComponentHandler()` supports Next 16 `cacheHandlers`, streamed Cache Component values, and distributed soft-tag invalidation.
- `createNextRevalidationHandler()` verifies a timestamped HMAC before invoking injected path and tag revalidation functions in either router.

Incremental entries use Node structured-clone serialization so buffers, typed arrays, and maps are not forced through JSON. Applications own the handler file, cache policy, secrets, and deployment topology.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/cache-next@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/cache-next` | `./dist/index.d.ts` |
| `@playstack/cache-next/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/cache` | `0.1.0-beta.4` | Required by the package. |
| `next` | `>=14.1.0 <17` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/content-next

Source: https://playstack.dev/docs/packages/content-intelligence/content-next.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/content-next` connects application-loaded documents to explicit Pages and App Router boundaries. It imports no Next.js runtime and works with Content Collections, a CMS, or any other source that produces document values.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/content-next@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## App Router

```tsx
import { notFound } from 'next/navigation'

import { documentFor, staticParamsFor } from '@playstack/content-next/app'

export const generateStaticParams = () => staticParamsFor(allPosts, { param: 'slug' })

export default async function PostPage({ params }) {
  const post = await documentFor(allPosts, { params, param: 'slug' })
  if (!post) notFound()
  return <Post document={post} />
}
```

`documentFor()` accepts direct or promised params, returns `null` for absent, catch-all, or unknown slugs, and supports the portable package's field mappings. Next.js redirects, draft mode, cache policy, and `notFound()` remain application-owned.

## Pages Router

```ts
import { staticPathsFor, staticPropsFor } from '@playstack/content-next/pages'

export const getStaticPaths = () => staticPathsFor(allPosts, { param: 'slug' })

export const getStaticProps = staticPropsFor(allPosts, {
  param: 'slug',
})
```

`serverSidePropsFor()` provides request-time resolution. Missing documents become `notFound` by default or an explicit non-permanent redirect. A `select(document, context)` callback can project custom serializable props.

## Configuration and boundary

| Surface              | Responsibility                                                               |
| -------------------- | ---------------------------------------------------------------------------- |
| `documentFor`        | Resolve one App Router document from direct or promised params.              |
| `staticParamsFor`    | Produce `generateStaticParams` records.                                      |
| `staticPathsFor`     | Produce Pages Router static paths.                                           |
| `staticPropsFor`     | Resolve static props with missing-document behavior and optional projection. |
| `serverSidePropsFor` | Resolve request-time props using the same content mapping.                   |

The package root exposes uniquely named helpers from both routers, but router-specific entrypoints are recommended so the application boundary remains visible. Content loading, authorization, rendering, and framework cache semantics stay outside the adapter.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/content-next@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/content-next` | `./dist/index.d.ts` |
| `@playstack/content-next/app` | `./dist/app.d.ts` |
| `@playstack/content-next/pages` | `./dist/pages.d.ts` |
| `@playstack/content-next/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/content` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/errors

Source: https://playstack.dev/docs/packages/foundation/errors.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/errors` reports operational failures without coupling application code to one monitoring vendor. One normalized, scrubbed event fans out to isolated structural drivers.

## Install

```sh
npm install @playstack/core @playstack/errors
```

## Compose the reporter

Initialize vendor SDKs in the application, adapt their clients, and construct one reporter for the lifecycle you control.

```ts
import { configureErrors } from '@playstack/errors'
import { sentry, sanitizeSentryEvent } from '@playstack/errors/sentry'

// In each application-owned Sentry initializer, including framework capture:
// beforeSend: (event) => sanitizeSentryEvent(event)
// If another beforeSend transforms events, sanitize its result LAST.

export const errors = configureErrors({
  drivers: [sentry(initializedSentry)],
  environment: process.env.DEPLOYMENT_ENVIRONMENT!,
  release: process.env.RELEASE_ID!,
  trace: activeTraceProvider,
  onError: ({ stage, driverId, error }) => {
    process.stderr.write(`Error reporting failure at ${stage}/${driverId}: ${String(error)}\n`)
  },
})

errors.captureError(cause, {
  severity: 'error',
  tags: { feature: 'checkout' },
  context: { attempt: 2 },
  userId: user.id,
  accountId: account.id,
  fingerprint: ['checkout-submission'],
})
```

`captureError` returns immediately and does not throw. Synchronous and asynchronous driver failures are sent to `onError` without stopping healthy drivers.

## Reporter configuration

| `ConfigureErrorsOptions` property | Type                     | Required | Purpose                                                                     |
| --------------------------------- | ------------------------ | -------- | --------------------------------------------------------------------------- |
| `drivers`                         | `readonly ErrorDriver[]` | Yes      | One or more uniquely named delivery bridges.                                |
| `environment`                     | `string`                 | Yes      | Deployment environment attached to every event; maximum 64 characters.      |
| `release`                         | `string`                 | Yes      | Release identifier attached to every event; maximum 200 characters.         |
| `trace`                           | `ErrorTraceProvider`     | No       | Supplies current W3C trace context when capture does not set it explicitly. |
| `onError`                         | `(failure) => void`      | No       | Isolated observer for input, trace-provider, or driver failures.            |

The reporter is instance-scoped. Create separate instances when SSR requests, tests, or applications in the same process require different configuration.

## Capture properties

| `ErrorCaptureOptions` property | Type                                         | Purpose                                                        |
| ------------------------------ | -------------------------------------------- | -------------------------------------------------------------- |
| `severity`                     | `debug \| info \| warning \| error \| fatal` | Delivery severity; defaults to `error`.                        |
| `tags`                         | Primitive record                             | Indexed, low-cardinality attributes.                           |
| `context`                      | Primitive record                             | Non-indexed diagnostic attributes.                             |
| `userId`                       | `string`                                     | Opaque user attribution.                                       |
| `accountId`                    | `string`                                     | Opaque account attribution.                                    |
| `fingerprint`                  | `readonly string[]`                          | Portable grouping hint; support depends on the driver.         |
| `trace`                        | `ErrorTraceContext \| null`                  | Explicit W3C trace context, or `null` to suppress correlation. |

Tags and context allow at most 30 shallow primitive values. Private keys are dropped, while emails, bearer tokens, JWTs, credentials, and private assignments are redacted from strings, error messages, stacks, causes, trace state, and baggage.

The same key policy covers camelCase credentials such as `accessToken` and
`clientSecret`, including nested final Sentry metadata. Sensitive property names
are dropped rather than renamed; property names can leak data just like values.
Keep host-specific privacy filtering for data the generic policy cannot identify,
and configure non-error telemetry separately.

The Sentry filter also removes password/email aliases (`passwd`, `userPasswd`,
`eMail`), nonopaque-looking user IDs, and query/fragment secrets in relative
URL/URI-suffix fields after case/separator normalization, regardless of prefix
(`redirect_url`, `oauth_callback_url`, `destination_uri`, etc.). String values in
nested arrays inherit that field policy too. Existing navigation/source keys such
as `from`, `to` and `filename` retain their treatment. Sensitive-key removal wins
over preserving paths; ordinary non-suffix diagnostics remain intact.
Nested user/request containers receive
the same restrictions as top-level ones: no profiles, bodies or headers. Safe
IDs, request methods/URLs and stack diagnostics remain. Identifier syntax cannot
prove anonymity; the host must supply truly opaque IDs and its own privacy policy.

## Driver bridge

Implement this structural contract to connect another reporting backend:

```ts
interface ErrorDriver<TClient = unknown> {
  id: string
  client: TClient
  capabilities: {
    fingerprint: boolean
    trace: false | 'native' | 'tag'
  }
  capture(event: CapturedErrorEvent): void | Promise<void>
}
```

The `id` must be unique kebab case. `client` preserves access to vendor-specific behavior through `errors.driver(id).client`. Capabilities describe what the adapter actually guarantees; they do not emulate unsupported vendor features.

The package includes a structural Sentry adapter and `basemarksErrorBridge` at `@playstack/errors/basemarks`. The latter requires an application-owned `captureError(error, normalizedEvent)` transport; the published Basemarks web SDK does not implement that method. Neither bridge initializes an SDK. See [Basemarks integration boundaries](/integrations/basemarks).

## Sentry identity and final-event privacy

The adapter sends sanitized error data while retaining the original Error only
as an identity hint for Sentry's duplicate suppression. Repeated native/direct/
framework captures can therefore share the SDK's original-object suppression;
distinct Error objects remain distinct. Do not log or serialize that hint.

Install `sanitizeSentryEvent` as the last step of `beforeSend` in every SDK
runtime. Reporter scrubbing happens before inherited SDK scope and integrations
are merged, so it is not a substitute for this final hook. Preserve `null` from
an existing filter; sanitize its resulting event, not the earlier input.

The bounded sanitizer removes sensitive metadata, user fields other than opaque
ID, request bodies/headers, URL query/fragment data and frame locals/source
context while preserving diagnostic identity and trace/stack positions. It is
not universal secret detection: application-specific sensitive values still need
host policy. Logs, transactions, replay, profiles and attachments require separate
configuration. Keep one application-owned SDK and capture lifecycle.

## Testing

`@playstack/errors/testing` exports capture and no-op drivers plus a driver contract suite. Assert against normalized events without sending telemetry.

## Boundary

The package does not own logging, tracing or span creation, breadcrumbs, alerting, storage, replay, profiling, SDK initialization, or automatic instrumentation. Use [`@playstack/errors-react`](/docs/packages/foundation/errors-react) and [`@playstack/nest-errors`](/docs/packages/foundation/nest-errors) only at their respective framework edges.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/errors@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/errors` | `./dist/index.d.ts` |
| `@playstack/errors/errors` | `./dist/errors.d.ts` |
| `@playstack/errors/types` | `./dist/types.d.ts` |
| `@playstack/errors/validation` | `./dist/validation.d.ts` |
| `@playstack/errors/testing` | `./dist/testing.d.ts` |
| `@playstack/errors/sentry` | `./dist/sentry.d.ts` |
| `@playstack/errors/basemarks` | `./dist/basemarks.d.ts` |
| `@playstack/errors/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/errors/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/events-prisma

Source: https://playstack.dev/docs/packages/events-operations/events-prisma.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/events-prisma` persists observational envelopes in the same Postgres transaction as application state, then drains them through an application-owned publisher.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/events-prisma@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose

```ts
import { createEventBridge } from '@playstack/events'
import {
  PrismaEventOutboxWriter,
  PrismaOutboxPump,
} from '@playstack/events-prisma'

const outbox = new PrismaEventOutboxWriter()
const bridge = createEventBridge({ registries, clock, ids, outbox, routes })

const pump = new PrismaOutboxPump({
  client: prisma,
  publisher: queuePublisher,
  clock,
})

await pump.runOnce()
```

The writer’s transaction kind is `prisma`; package persistence must pass the same transaction handle to `events.emit`.

## Pump configuration

| `PrismaOutboxPumpOptions` property | Required | Purpose |
| --- | --- | --- |
| `client` | Yes | Structural root Prisma client; exposed as `pump.client`. |
| `publisher` | Yes | Publishes an envelope with a stable `jobId`. |
| `clock` | Yes | Claim, lease, retry, delivery, and cleanup time. |
| `batchSize` | No | Maximum rows per run; defaults to 100. |
| `leaseMs` | No | Renewable claim lease; defaults to 60 seconds. |
| `retryDelayMs` | No | Positive delay function by attempt; defaults to bounded exponential backoff. |

`runOnce()` returns claimed, delivered, and failed counts. `cleanup(retentionMs?)` removes delivered transport rows after 24 hours by default.

## Publisher bridge

```ts
interface OutboxPublisher {
  publish(event: EventEnvelope, options: { jobId: string }): Promise<void>
}
```

The publisher must use the outbox ID as the transport idempotency key. A process crash after publish but before `deliveredAt` can republish the same row; stable job identity makes the handoff safely at-least-once.

Claims use one parameterized `UPDATE … FOR UPDATE SKIP LOCKED … RETURNING` query, allowing concurrent pumps. Failed rows release their lease and receive bounded backoff. UTC normalization makes claim and recovery independent of database session timezone.

## Boundary

The adapter owns outbox persistence, claims, leases, and transport-row cleanup. It does not select a broker, start a poller, create or disconnect Prisma, define consumer policy, or provide a replayable domain log. The application owns migrations and pump lifecycle.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/events-prisma@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/events-prisma` | `./dist/index.d.ts` |
| `@playstack/events-prisma/testing` | `./dist/testing.d.ts` |
| `@playstack/events-prisma/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/events-prisma/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/feeds

Source: https://playstack.dev/docs/packages/content-intelligence/feeds.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/feeds` streams RSS, Atom, JSON Feed, sitemaps, and generate-to-storage documents from application-supplied async iterables in Node and Workers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/feeds@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and generate feeds

```ts
import { generateFeedDocuments } from '@playstack/feeds/feeds'

for await (const document of generateFeedDocuments(
  {
    id: 'https://example.com/',
    title: 'Example',
    description: 'Example posts',
    homePageUrl: 'https://example.com/',
    updatedAt: contentIndexUpdatedAt,
    urls: {
      rss: 'https://example.com/feed.rss.xml',
      atom: 'https://example.com/feed.atom.xml',
      json: 'https://example.com/feed.json',
    },
  },
  () => streamPublishedPosts(),
  { content: 'full', formats: ['rss', 'atom', 'json'] },
)) {
  await write(document)
}
```

The entry source is a factory returning `AsyncIterable<FeedEntry>` and is called once per requested format. Stable entry IDs remain RSS GUID, Atom ID, and JSON Feed ID even when URLs change. `full` and `excerpt` modes fail when their required body is absent.

## Feed properties

Definitions require stable ID, title, description, homepage URL, explicit updated time, and a URL per enabled format. Optional language, authors, icon, and favicon are shared. Entries require ID, URL, title, and published time, with optional updated time, full/excerpt HTML or text, authors, tags, and attachments.

`FeedExtension` adds structured RSS namespaces and channel/entry XML elements; raw XML injection is not accepted.

`FeedEntry.summary` is independent plain text: it becomes RSS description, Atom text summary, and JSON Feed summary even when full content is selected. The generator does not infer a summary by stripping HTML. The `rssContent` option defaults to `'both'` (description and encoded body); `'description'` omits the encoded body and `'encoded'` omits the description.

Entry `tags` serialize as native RSS categories as well as Atom/JSON categories or tags. `FeedDefinition.rights` supplies standard Atom text rights, and `iconUrl` supplies the Atom icon. Use these fields instead of extension overrides: extensions reject attempts to replace standard fields.

## Stream sitemaps

```ts
import { generateSitemapDocuments } from '@playstack/feeds/sitemaps'

const documents = generateSitemapDocuments(streamPublicUrls(), {
  baseUrl: 'https://example.com/',
  maxUrlsPerShard: 50_000,
  gzip: true,
})
```

`SitemapSetOptions` also configures index path, shard prefix, robots path/directives, and uncompressed-byte ceiling. Defaults enforce 50,000 URLs and 50 MiB per deterministic gzip shard, then emit an index and `robots.txt`. Entries support optional last-modified time, alternates, images, videos, and news metadata.

## Generate atomically to storage

```ts
import { R2FeedStorage, generateToStorage } from '@playstack/feeds/storage'

const storage = new R2FeedStorage(env.FEEDS_BUCKET, env.FEEDS_PUBLIC_URL)
await generateToStorage({
  storage,
  namespace: 'marketing',
  documents,
  clock,
  ids,
  cacheControl: 'public, max-age=300',
  onEvent: (event) => operations.record(event),
})
```

Objects write below a versioned generation key. `manifest.json` changes only after every object succeeds, so failed regeneration leaves the prior generation serving.

## Storage and queue bridges

| Contract | Members |
| --- | --- |
| `FeedStorage` | Native `client`, `put`, `get`, `delete`, and public `url`. |
| `FeedGenerationQueue` | Native `client` plus `dispatch(job, { jobId })` returning queued/duplicate. |
| `WebSubPublisher` | Native `client` plus `publish({ hubUrl, topicUrl })`. |

`serveGeneratedDocument` supports `auto`, `proxy`, or `redirect`; auto proxies up to 5 MiB and redirects larger objects. `FeedRegenerator` uses deterministic `feeds:<namespace>:<definitionId>` jobs to coalesce content-change, scheduled, and manual triggers.

## Boundary

The package produces public documents but does not parse feeds, syndicate content, query a filesystem, choose routes, or install scheduler/queue infrastructure. Storage completion events are observational and cannot fail generation; the committed manifest is the durable handoff point.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/feeds@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/feeds` | `./dist/index.d.ts` |
| `@playstack/feeds/feeds` | `./dist/feeds.d.ts` |
| `@playstack/feeds/sitemaps` | `./dist/sitemaps.d.ts` |
| `@playstack/feeds/storage` | `./dist/storage.d.ts` |
| `@playstack/feeds/errors` | `./dist/errors.d.ts` |
| `@playstack/feeds/testing` | `./dist/testing.d.ts` |
| `@playstack/feeds/package.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/feeds/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-audiences

Source: https://playstack.dev/docs/packages/communication/nest-audiences.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-audiences` binds the portable audience service to guarded public consent routes, list-management routes, and a validated remote-command consumer. It does not choose sessions, CAPTCHA, rate limiting, queues, or page rendering.

## Install

```sh
npm install @playstack/nest-audiences @playstack/audiences @nestjs/common
```

## Configure

```ts
PlaystackAudiencesModule.forRoot({
  audiences,
  publicGuard: appAudienceAbuseGuard,
  managementGuard: accountAuthorizationGuard,
  subscribeSource: ({ listKey }) => `public:${listKey}`,
  onSubscriptionResult: ({ result }) => confirmationQueue.enqueue(result),
  context: (request) => request.operationContext,
  ip: (request) => request.ip,
  userAgent: (request) => request.headers['user-agent'],
})
```

Use `forRootAsync({ imports, inject, useFactory })` when the composition root resolves the service through Nest dependency injection. The module is non-global.

## Configuration reference

| `NestAudiencesOptions` property | Required | Purpose |
| --- | --- | --- |
| `audiences` | Yes | The configured core facade. |
| `publicGuard` | Yes | Application abuse policy for public consent routes. |
| `managementGuard` | Yes | Application account and role policy for management routes. |
| `subscribeSource` | Yes | Produces append-only consent-source evidence. |
| `onSubscriptionResult` | No | Receives trusted server results so the application can queue confirmation messages. |
| `context`, `ip`, `userAgent` | No | Extract operation and consent evidence from the application request shape. |

Public mutation responses never include subscriber IDs or signed tokens. GET confirmation and unsubscribe routes inspect token state only; POST routes perform mutations.

## Remote worker

```ts
await remoteConsumer.handle(job.data)
```

Call `PlaystackAudienceRemoteCommandConsumer.handle()` from the queue worker selected by the application. It validates the untrusted data-only envelope before delegating to `audiences.processRemote()`.

Inject `AUDIENCES_SERVICE` for the configured facade or `NEST_AUDIENCES_OPTIONS` for the binding options. These are escape hatches, not alternative ownership boundaries.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-audiences@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-audiences` | `./dist/index.d.ts` |
| `@playstack/nest-audiences/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/audiences` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/payments

Source: https://playstack.dev/docs/packages/commerce/payments.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/payments` handles one-time payment state without owning subscriptions, orders, inventory, fulfillment, raw webhook verification, or card collection.

```ts
import { createPayments } from '@playstack/payments'
import { stripePayments } from '@playstack/payments/stripe'

const payments = createPayments({
  providers: [
    stripePayments({ client: storeStripe, instance: 'store' }),
    stripePayments({ client: euStripe, instance: 'eu' }),
  ],
  persistence,
  events,
  ids,
  clock,
})
```

Each provider and configured instance is a persisted identity, making multiple Stripe accounts safe to compose. Pass only verified, queued events from `@playstack/webhooks` to `handleWebhook()`. The native escape hatch is `payments.provider(ref).client`.

Use `@playstack/payments/prisma` for PostgreSQL projections and atomic refund reservations, `@playstack/payments/stripe` for hosted Stripe Checkout in `payment` mode, and `@playstack/payments/testing` for application contract tests.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/payments@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/payments@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/payments` | `./dist/index.d.ts` |
| `@playstack/payments/errors` | `./dist/errors.d.ts` |
| `@playstack/payments/prisma` | `./dist/prisma.d.ts` |
| `@playstack/payments/stripe` | `./dist/stripe.d.ts` |
| `@playstack/payments/testing` | `./dist/testing.d.ts` |
| `@playstack/payments/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/storage

Source: https://playstack.dev/docs/packages/infrastructure/storage.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/storage` provides one runtime-neutral object boundary across Cloudflare R2, S3-compatible services, local filesystem, and in-memory testing providers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/storage@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose and store objects

For a complete example without cloud credentials, start with the [storage quickstart](/docs/getting-started#your-first-working-example).

**Workers composition example:** the snippet below assumes `env.ASSETS` is your configured R2 bucket binding, `pdfBytes` contains the document, and `signer` is your `StorageUrlSigner`. Omit `signer` if you do not need signed URLs. In Node, choose the S3 or filesystem entry point instead of a Workers binding.

```ts
import { createStorage } from '@playstack/storage'
import { R2StorageProvider } from '@playstack/storage/r2'

const storage = createStorage({
  driver: new R2StorageProvider(env.ASSETS, {
    publicBaseUrl: 'https://assets.example.com',
    signer,
  }),
  prefix: 'billing',
})

const stored = await storage.put('invoices/invoice-1.pdf', pdfBytes, {
  contentType: 'application/pdf',
  cacheControl: 'private, max-age=0',
  metadata: { account_id: 'account-1' },
})
```

Bodies may be strings, bytes, ArrayBuffer, Blob, or Web readable streams. Reads always return a Web stream. Prefix and logical keys are canonical relative paths; dot/empty segments, backslashes, controls, and paths over 1,024 UTF-8 bytes are rejected.

## Put and URL properties

### Common method results

| Call | Result | Missing object / important behavior |
| --- | --- | --- |
| `put(key, body, options?)` | `Promise<StorageObjectMetadata>` | Creates or replaces an object; use write preconditions when replacement is not allowed. |
| `get(key, options?)` | `Promise<StorageObject \| null>` | Returns `null` when absent; consume or cancel the returned body stream. |
| `head(key, options?)` | `Promise<StorageObjectMetadata \| null>` | Metadata only, without downloading the body. |
| `exists(key, options?)` | `Promise<boolean>` | Returns whether the object is present. |
| `delete(key, options?)` | `Promise<void>` | Deletes one key, not an application record or tenant. |
| `list(options?)` | `Promise<StorageListPage>` | A bounded page, not an unbounded list of the bucket. |
| `verify(key, expected, options?)` | `Promise<StorageObjectMetadata \| null>` | Returns `null` when absent; throws on an integrity mismatch. |
| `url(key)` | `string` | Requires public-URL capability; does not grant authorization. |
| `signedUrl(key, options)` | `Promise<string>` | Requires signing capability; treat the result as a secret capability. |

For all overloads and option fields, open the `StorageDriver` and `StoragePutOptions` declarations through your editor. The entry-point table below identifies their installed files.

| Surface                | Properties                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------------------ |
| `CreateStorageOptions` | Required provider plus optional canonical prefix.                                                |
| Put options            | Content type/encoding, cache control, disposition, length, and string metadata.                  |
| Signed URL             | `get` or `put`, expiry from 1 second through 7 days, optional disposition, and PUT content type. |
| Capabilities           | Independent public URL, signed GET, and signed PUT booleans.                                     |

Calling an unsupported URL operation throws `StorageCapabilityError`. When a signed PUT includes content type or disposition, the uploader must send the exact signed header.

## Strict S3 direct uploads

Use `signedUpload` when direct upload admission requires an exact byte length,
whole-body SHA-256 and server-side encryption. The result includes a PUT URL and
all required signed headers. The checksum is canonical **base64**, unlike the
hexadecimal checksum used by body-reading `verify`.

```ts
const expected = {
  contentLength: uploadBytes.byteLength,
  checksumSha256: approvedBase64Sha256,
  encryption: { algorithm: 'AES256' as const },
  contentType: 'application/pdf',
}
const request = await storage.signedUpload('incoming/report.pdf', {
  ...expected,
  expiresInSeconds: 300,
})
// Send request.method, request.url, request.headers and the exact approved bytes.
// After the uploader completes, admit only a verified object:
const uploaded = await storage.verifyUpload('incoming/report.pdf', expected)
```

This partial example assumes an S3 provider, approved bytes and a trusted expected
checksum. Use a client that transmits the exact required wire headers. Browser
Fetch controls Content-Length itself; arbitrary browser streams are not covered.
SSE-KMS uses `{ algorithm: 'aws:kms', keyId: fullKmsKeyArn }`, with a full key ARN
rather than an alias or implicit default. Optional type/disposition headers must
also match exactly. Signed URLs and their headers are secret capabilities.

`headUpload` returns checksum and encryption metadata without downloading bytes.
`verifyUpload` requires exact length, matching encryption/key and a full-object
SHA-256. Missing, composite or unknown checksum metadata refuses verification;
there is no ETag or download fallback. Both return `null` for an absent object.
Providers without these optional operations throw `StorageCapabilityError`.
Ordinary `head`, `signedUrl` and body-reading `verify` remain separate operations.

Presigned PUTs can be reused until expiry; credentials or policy can end them
earlier. Verification observes the current object and does not bind a version or
prevent replacement afterward. Your application owns upload authorization, key
isolation, size policy and final admission. Checksum-enabled KMS HEAD can need
additional KMS permissions. Local MinIO qualification is separate from live
AWS/KMS, browser and multipart qualification. See AWS's
[presigned URL guidance](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
and [HEAD permission requirements](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html).

## Error handling

Errors are exported from `@playstack/storage/errors`. A missing read is `null`, not an exception. The main error codes are:

| Error | Code | What to do |
| --- | --- | --- |
| `StorageConfigurationError` | `playstack/storage/invalid_configuration` | Correct the provider configuration. |
| `StorageInputError` | `playstack/storage/invalid_input` | Correct the key, options or body. |
| `StorageCapabilityError` | `playstack/storage/unsupported_capability` | Choose a provider that supports the operation. |
| `StoragePreconditionError` | `playstack/storage/precondition_failed` | Reconcile the object state; do not overwrite blindly. |
| `StorageAbortedError` | `playstack/storage/operation_aborted` | Treat the operation as cancelled. |
| `StorageIntegrityError` | `playstack/storage/integrity_failed` | Reject the bytes and investigate the mismatch. |
| `StorageUnavailableError` | `playstack/storage/unavailable` | Consider a bounded retry only when repeating the operation is safe. |

The first six are non-retryable. `StorageUnavailableError` is retryable, but that flag does not establish whether a write took effect. Your application owns write idempotency and recovery. Do not expose provider exception details or signed URLs to end users.

## Advanced portable operations

```ts
const page = await storage.list({ prefix: 'fonts', limit: 100 })
await storage.copy('fonts/source.woff2', 'fonts/archive/source.woff2')
await storage.deletePrefix({ prefix: 'temporary', maxObjects: 1_000 })

const chunk = await storage.getRange('packages/font.tgz', {
  start: 0,
  end: 1_048_575,
})

await storage.verify('packages/font.tgz', {
  size: expectedSize,
  etag: expectedEtag,
  sha256: expectedSha256,
})
```

Listing and prefix deletion are cursor-based and bounded. Range reads retain the complete object size. Verification can use size and ETag without downloading the body or SHA-256 when complete-body integrity is required. Operations accept an `AbortSignal` only when the selected provider declares that capability.

S3, R2, and memory providers support atomic create-only and compare-and-swap writes through `ifNoneMatch` and `ifMatch`. S3 also exposes portable multipart create, upload-part, complete, and abort operations. Unsupported capabilities fail explicitly before I/O.

## Provider bridge

```ts
interface StorageProvider<TClient> {
  client: TClient
  capabilities: StorageCapabilities
  put(key, body, options): Promise<StorageObjectMetadata>
  get(key): Promise<StorageObject | null>
  head(key): Promise<StorageObjectMetadata | null>
  delete(key): Promise<void>
  url(key): string
  signedUrl(key, options): Promise<string>
}
```

The driver wrapper adds logical prefixing, validation, normalized errors, `exists`, and stable metadata while exposing the exact native client.

## Provider-specific composition

- R2 accepts a structural bucket, optional public base URL, and optional external `StorageUrlSigner` for S3-compatible presigned URLs.
- S3 accepts AWS client configuration plus bucket/public URL options and uses optional AWS SDK peers.
- Filesystem is a Node development/test adapter for host-controlled, stable body and metadata trees. It accepts public URL/signing options, a byte ceiling, listing budgets, clock, and a separate metadata root.
- Memory storage and the shared provider contract suite live under `@playstack/storage/testing`.
- `@playstack/nest-storage` registers one or more named stores with stable injection tokens while preserving native clients and application lifecycle ownership.

### Filesystem capacity and ownership

The filesystem adapter rejects observed symlinks in object paths, physically
overlapping roots and changed root identities. It reads through the same verified
regular-file handle rather than reopening a pathname. This requires supported
no-follow/nonblocking file-open flags and host-protected roots and ancestors;
it is not a sandbox against hostile concurrent parent replacement or hardlink
injection.

`maxBytes` defaults to **16 MiB** for PUT, complete GET and the returned range span.
A bounded small range may read a larger source. Sidecar JSON is separately capped
at 1 MiB. Growth or a short read fails instead of returning a silently truncated
successful body. Consume or cancel each returned stream to release its handle.

Listing retains at most `limit + 1` sorted candidates and rescans a bounded tree
per page. `maxListEntries` defaults to **10,000** visited entries, including
nonmatches and directories; `maxListDepth` defaults to **64** nested directories.
Exhaustion throws without returning a partial page or cursor. This is a local
development capacity guard, not indexed production pagination.

New directories/files use private permissions without changing existing modes.
Body and metadata publication remain two separate renames, without atomic
generation or equal-size metadata coherence. Conditional reads/writes and
AbortSignal cancellation remain unsupported. Use a suitable production provider
when those guarantees or an untrusted filesystem boundary are required.

## Boundary

Storage does not decide tenant organization, media processing, access policy, lifecycle rules, or bucket administration. The application owns clients, credentials, shutdown, and domain-to-key mapping; use `storage.client` for provider-specific operations outside the portable surface.

## Bounded integrity verification

SHA-256 verification now defaults to a **16 MiB actual-byte ceiling**. Set `verify(key, expected, { maxBytes, signal })` deliberately for larger objects. HEAD metadata permits early rejection but is not trusted instead of counting downloaded bytes; truncation, overflow and cancellation are checked. Changed/abandoned streams are cancelled. The portable hasher buffers up to the bound, so it is not constant-memory.

Node services can inject `storageSha256Node` from `@playstack/storage/node` through `createStorage({ driver, sha256: storageSha256Node })` for incremental hashing under the same byte/abort contract. Use the S3 adapter for MinIO and R2's S3 endpoint in Node; the structural R2 binding is for Workers. Verification proves the downloaded snapshot, not immutability of future reads. Retain product key layouts, authorization and deployment-specific integrity tests.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/storage@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/storage` | `./dist/index.d.ts` |
| `@playstack/storage/errors` | `./dist/errors.d.ts` |
| `@playstack/storage/types` | `./dist/types.d.ts` |
| `@playstack/storage/testing` | `./dist/testing.d.ts` |
| `@playstack/storage/r2` | `./dist/r2.d.ts` |
| `@playstack/storage/s3` | `./dist/s3.d.ts` |
| `@playstack/storage/filesystem` | `./dist/filesystem.d.ts` |
| `@playstack/storage/node` | `./dist/node.d.ts` |
| `@playstack/storage/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/storage/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@aws-sdk/client-s3` | `^3.1116.0` | Optional; only for the entry points that use it. |
| `@aws-sdk/s3-request-presigner` | `^3.1116.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Commerce

Source: https://playstack.dev/docs/packages/commerce.md

Commerce packages keep product terms, payment state, stock promises, order state, and fulfillment progress separate while providing an orchestration layer that composes them into one-off purchase flows.

## Portable domains

| Package | Plan | Responsibility |
| --- | --- | --- |
| [`@playstack/catalog`](/docs/packages/commerce/catalog) | <PlanTierDot tier="free" /> | Products, variants, immutable offers, and provider-aware quotes. |
| [`@playstack/payments`](/docs/packages/commerce/payments) | <PlanTierDot tier="free" /> | One-off checkout, capture, refund, dispute, and reconciliation projections. |
| [`@playstack/inventory`](/docs/packages/commerce/inventory) | <PlanTierDot tier="free" /> | Locations, balances, reservations, and immutable stock adjustments. |
| [`@playstack/fulfillment`](/docs/packages/commerce/fulfillment) | <PlanTierDot tier="free" /> | Encrypted fulfillment requests and provider-neutral delivery progress. |
| [`@playstack/commerce`](/docs/packages/commerce/commerce) | <PlanTierDot tier="free" /> | Order snapshots and transactional orchestration across the other domains. |

## NestJS boundaries

| Package | Plan | Responsibility |
| --- | --- | --- |
| [`@playstack/nest-catalog`](/docs/packages/commerce/nest-catalog) | <PlanTierDot tier="pro" /> | Authorized catalog administration. |
| [`@playstack/nest-payments`](/docs/packages/commerce/nest-payments) | <PlanTierDot tier="pro" /> | Refund, reconciliation, and verified payment-event services. |
| [`@playstack/nest-inventory`](/docs/packages/commerce/nest-inventory) | <PlanTierDot tier="pro" /> | Inventory administration and reservation-expiry workers. |
| [`@playstack/nest-fulfillment`](/docs/packages/commerce/nest-fulfillment) | <PlanTierDot tier="pro" /> | Fulfillment administration and queue-neutral workers. |
| [`@playstack/nest-commerce`](/docs/packages/commerce/nest-commerce) | <PlanTierDot tier="pro" /> | Checkout, order-access, and payment-projection services. |

Subscriptions remain in [`@playstack/billing`](/docs/packages/identity/billing). Product applications can use subscriptions and one-time commerce together without forcing either payment model into the other.

## Existing schema adoption

Source: https://playstack.dev/docs/cli/existing-schema-adoption.md

Playstack can determine whether an existing Prisma schema physically satisfies a selected package artifact without connecting to a database or claiming semantic equivalence.

## Run the read-only preflight

```sh
npx playstack database compatibility --database=primary
npx playstack database compatibility --database=primary --json
```

The report compares incoming artifacts with application `.prisma` files and other selected package artifacts. It checks:

- logical model and enum declarations;
- physical `@@map`, `@map`, constraint, and relation identities;
- scalar field names, types, optionality, defaults, and native database annotations;
- primary, unique, and index guarantees; and
- unmanaged files already occupying a managed artifact destination.

The result is deterministic and field-specific so an application migration or adapter can be reviewed before `database sync` writes anything.

## Declare exact satisfaction

Use `existingArtifacts` only when the existing storage must satisfy the package's full physical contract:

```json
{
  "databases": {
    "primary": {
      "adapter": "prisma",
      "target": "prisma/playstack",
      "packages": {
        "@playstack/accounts": {
          "existingArtifacts": ["accounts.prisma"]
        }
      }
    }
  }
}
```

Adoption is fail-closed. The CLI skips `accounts.prisma` only when model or table identity, fields, mappings, relations, constraints, enums, and safe extra-column rules all pass. A near match remains a reported incompatibility.

Mapped names are supported when Prisma annotations resolve to the artifact's exact storage identities. Provider-native types are checked separately: compatible PostgreSQL defaults can be normalized, materially different types remain incompatible, and annotations are reported as unverified when the datasource provider cannot be determined.

### Partial native schema adoption

`existingArtifacts` and synchronized `artifacts` are separate, disjoint selections.
Use `artifacts: []` when no package files should be copied, while keeping existing
IDs under exact compatibility validation. Omission instead selects compatible
non-optional defaults. Optional schemas are never added implicitly.

For Notifications, existing native queue tables do not imply an inbox or logical
admission store. Follow the [queue-only configuration](/docs/cli/configuration#selecting-schema-artifacts)
and select other schemas only when their native adapters are configured. A missing
existing queue table still fails `doctor --check` even with `artifacts: []`.
This proves schema-file compatibility, not applied migrations or runtime authority.

## Use an application adapter for semantic equivalence

An existing `AccountInvitation` table is not automatically the same contract as a packaged `AccountInvite` model merely because the records have similar meaning. If the physical schema differs:

1. omit that package artifact from the database target's explicit artifact allowlist;
2. implement the package persistence interface in the application composition root; and
3. translate identifiers, reads, writes, coexistence, and migration behavior there.

That application-owned adapter may support rolling reads, shadow writes, or record migration. The CLI deliberately does not invent those semantics or claim that the packaged Prisma adapter can operate against a different schema.

## Keep migrations application-owned

After a compatible sync or a reviewed schema change, use Prisma, Drizzle Kit, or the repository's chosen migration tooling. Playstack does not generate migration histories, run schema push, introspect a live database, or apply migrations.

## NestJS

Source: https://playstack.dev/docs/frameworks/nestjs.md

Playstack NestJS packages adapt portable capabilities to dependency injection, request metadata, guards, controllers, workers, and application lifecycle. They do not move domain policy into decorators or silently register global behavior.

[Start with the NestJS guide](/docs/frameworks/nestjs/getting-started) to compose authentication, account scope, and entitlements in their required request order.

## Available NestJS bindings

| Area | NestJS binding | Portable capability |
| --- | --- | --- |
| Errors | [`@playstack/nest-errors`](/docs/packages/foundation/nest-errors) | [`@playstack/errors`](/docs/packages/foundation/errors) |
| Authentication | [`@playstack/nest-auth`](/docs/packages/identity/nest-auth) | [`@playstack/auth`](/docs/packages/identity/auth) |
| Account scope | [`@playstack/nest-accounts`](/docs/packages/identity/nest-accounts) | [`@playstack/accounts`](/docs/packages/identity/accounts) |
| API keys | [`@playstack/nest-api-keys`](/docs/packages/identity/nest-api-keys) | [`@playstack/api-keys`](/docs/packages/identity/api-keys) |
| Audit declarations | [`@playstack/nest-audit`](/docs/packages/identity/nest-audit) | [`@playstack/audit`](/docs/packages/identity/audit) |
| Entitlements | [`@playstack/nest-entitlements`](/docs/packages/identity/nest-entitlements) | [`@playstack/entitlements`](/docs/packages/identity/entitlements) |
| Audience consent and lists | [`@playstack/nest-audiences`](/docs/packages/communication/nest-audiences) | [`@playstack/audiences`](/docs/packages/communication/audiences) |
| Notification workers | [`@playstack/nest-notifications`](/docs/packages/communication/nest-notifications) | [`@playstack/notifications`](/docs/packages/communication/notifications) |
| Delivery and unsubscribe | [`@playstack/nest-delivery`](/docs/packages/communication/nest-delivery) | [`@playstack/delivery`](/docs/packages/communication/delivery) |
| Domain events | [`@playstack/nest-events`](/docs/packages/events-operations/nest-events) | [`@playstack/events`](/docs/packages/events-operations/events) |
| Event sourcing | [`@playstack/nest-event-store`](/docs/packages/events-operations/nest-event-store) | [`@playstack/event-store`](/docs/packages/events-operations/event-store) |
| Rate limiting | [`@playstack/nest-rate-limit`](/docs/packages/events-operations/nest-rate-limit) | [`@playstack/rate-limit`](/docs/packages/events-operations/rate-limit) |
| Analytics delivery | [`@playstack/nest-analytics`](/docs/packages/events-operations/nest-analytics) | [`@playstack/analytics`](/docs/packages/events-operations/analytics) |
| Feed regeneration | [`@playstack/nest-feeds`](/docs/packages/content-intelligence/nest-feeds) | [`@playstack/feeds`](/docs/packages/content-intelligence/feeds) |
| Billing | [`@playstack/nest-billing`](/docs/packages/identity/nest-billing) | [`@playstack/billing`](/docs/packages/identity/billing) |
| GitHub repository access | [`@playstack/nest-github-app`](/docs/packages/identity/nest-github-app) | [`@playstack/github-app`](/docs/packages/identity/github-app) |
| Licensing | [`@playstack/nest-licensing`](/docs/packages/identity/nest-licensing) | [`@playstack/licensing-issuer`](/docs/packages/identity/licensing-issuer) |
| Catalog | [`@playstack/nest-catalog`](/docs/packages/commerce/nest-catalog) | [`@playstack/catalog`](/docs/packages/commerce/catalog) |
| One-time payments | [`@playstack/nest-payments`](/docs/packages/commerce/nest-payments) | [`@playstack/payments`](/docs/packages/commerce/payments) |
| Inventory | [`@playstack/nest-inventory`](/docs/packages/commerce/nest-inventory) | [`@playstack/inventory`](/docs/packages/commerce/inventory) |
| Orders and checkout | [`@playstack/nest-commerce`](/docs/packages/commerce/nest-commerce) | [`@playstack/commerce`](/docs/packages/commerce/commerce) |
| Fulfillment | [`@playstack/nest-fulfillment`](/docs/packages/commerce/nest-fulfillment) | [`@playstack/fulfillment`](/docs/packages/commerce/fulfillment) |

## Request-boundary order

Guard order is product behavior and remains visible in controllers or application policy:

```text
authentication -> CSRF when cookies are ambient -> account scope
               -> entitlement or role policy -> rate limit -> handler
```

Authentication establishes who is calling. Account resolution revalidates which tenant they may act within. Entitlements and roles evaluate against that trusted context. Rate-limit subjects should be derived only after trustworthy identity and proxy information exist.

## Module ownership

Every `forRoot` or `forRootAsync` call receives an application-configured core service or factory. Applications own database clients, queues, provider SDKs, configuration loading, global guard registration, and shutdown order. Playstack modules export their runners and consumers so worker bindings stay explicit.

## Events and transactions

Decorators such as `@Emits` describe metadata; they cannot invent a transaction. Domain operations emit through the core event bridge inside the application transaction, and Nest lifecycle bindings pump or queue observational delivery afterward.

## Playstack CLI

Source: https://playstack.dev/docs/cli.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/cli` installs explicit capability recipes, synchronizes package-owned artifacts, detects drift, and operates configured application controls without hiding generated files or application ownership.

## CLI guides

| Guide                                                          | Use it to                                                                                                            |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [Configuration](/docs/cli/configuration)                       | Understand `playstack.json`, project survey, paths, and package selection.                                           |
| [Artifact synchronization](/docs/cli/artifact-synchronization) | Preview, write, and verify package-owned database-schema, email, content, and translation artifacts.                 |
| [Existing schema adoption](/docs/cli/existing-schema-adoption) | Compare a mature Prisma schema, declare satisfied artifacts, and choose an application adapter when storage differs. |
| [Integration planning](/docs/cli/integration-planning)         | Inspect package-authored routes, environment, framework, health, test, and ejectable wiring requirements.            |
| [Using Playstack with Megamono](/docs/megamono)                | Let Megamono own repository placement while Playstack owns capability package and artifact correctness.              |
| [Ejection and merging](/docs/cli/ejection-merging)             | Transfer ownership of an ejectable artifact and reconcile later upstream changes.                                    |
| [Lint and Skills](/docs/cli/lint-skills)                       | Install deterministic checks and repository-local agent guidance.                                                    |
| [Updates and CI](/docs/cli/updates-ci)                         | Update fixed package versions and generate the scheduled review workflow.                                            |

Each guide documents one ownership boundary. Start here for the complete command surface, then follow the focused guide when you are configuring that workflow.

## Install as tooling

```sh
npm install --save-dev @playstack/cli
```

Keep every installed `@playstack/*` package on the same fixed version. Commit `playstack.json`, managed output, and `.playstack` state so builds do not depend on running a generator first.

Inspect the executable and compatibility contracts independently:

```sh
playstack --version
playstack version
playstack version --json
```

`--version` and `-V` print the installed CLI package version. `version` also reports the supported `playstack.json` schema, artifact-manifest schema, Node range, source revision, source dirty state, and content-derived build ID; JSON output is stable for other tools and distinguishes republished prerelease builds.

## Initialize and synchronize

```sh
npx playstack init --survey
npx playstack init
npx playstack database compatibility --database=primary
npx playstack database sync --dry-run
npx playstack database sync
npx playstack lint init --diff
npx playstack skills sync --diff
npx playstack doctor --check
```

`init --survey` reports recognized project markers, declared packages, and proposed destinations without writing. Interactive `init` confirms targets and writes configuration only; it does not install packages or run a sync.

### Workspace discovery and source ownership

Survey combines dependency declarations from the root and included workspace
manifests. It does not prove installation or runtime compatibility. Ordinary
artifact discovery and `doctor` remain root-local: install artifact-owning
packages beside the configuration that owns their output, or run from an owning
workspace with its own configuration and relative targets. Nested runtime
dependencies alone do not make artifacts root-resolvable under strict pnpm.

`playstack add <capability> --local-packages=yalc --dry-run` resolves the full
local Playstack closure before mutation. Existing archive overrides such as
`file:vendor/*.tgz` are deliberate source conflicts, not equivalent local Yalc
references. Review and explicitly change only the selected conflicting override
before rerunning the dry-run; the CLI does not silently replace repository pins.
Preserve unrelated policy, commit the reconciled lockfile and qualify exact
replacement bytes with a frozen install. Local Yalc publication is development
distribution, not evidence of a registry release or fresh-builder availability.

## Configuration

```json
{
  "$schema": "./node_modules/@playstack/cli/playstack.schema.json",
  "databases": {
    "primary": {
      "adapter": "prisma",
      "target": "prisma/playstack",
      "packages": {
        "@playstack/accounts": { "scope": "account" },
        "@playstack/events-prisma": {}
      }
    }
  },
  "emails": {
    "target": "src/emails/playstack",
    "packages": ["@playstack/auth"]
  },
  "content": { "target": "content/playstack" },
  "i18n": { "target": "src/i18n/playstack" },
  "lint": {
    "target": "eslint.playstack.config.mjs",
    "recommended": true,
    "chakra": {
      "files": ["src/**/*.{ts,tsx}"],
      "tokens": ["fg.default", "bg.canvas", "border.default"]
    }
  },
  "skills": {
    "targets": [".agents/skills"],
    "overrides": {
      "event-design": ".playstack-overrides/event-design"
    }
  },
  "updates": {
    "gitHost": "github",
    "schedule": "0 9 * * 1",
    "requiredCheck": "test",
    "autoMerge": false
  }
}
```

| Property                    | Type                                            | Purpose                                                                    |
| --------------------------- | ----------------------------------------------- | -------------------------------------------------------------------------- |
| `$schema`                   | `string?`                                       | Points editors to the published JSON Schema.                               |
| `databases.<name>.adapter`  | `prisma \| drizzle`                             | Selects the schema representation accepted by this named target.           |
| `databases.<name>.dialect`  | `postgresql?`                                   | Required for Drizzle targets and omitted for Prisma targets.               |
| `databases.<name>.target`   | Relative path                                   | Destination for composed database-schema artifacts.                        |
| `databases.<name>.packages` | Package array or package-to-options map         | Selects emitters and optional package variants.                            |
| `emails`, `content`, `i18n` | `{ target, packages? }`                         | Selects an artifact kind, destination, and optional emitter allowlist.     |
| `lint`                      | `{ target, recommended?, extension?, chakra? }` | Configures the managed flat ESLint fragment and explicitly scoped presets. |
| `skills.targets`            | `string[]`                                      | Selects repository-relative destinations for the Skills corpus.            |
| `skills.overrides`          | `Record<skillId, path>?`                        | Shadows a packaged skill with a user-owned repository directory.           |
| `updates.gitHost`           | `github`                                        | Pull-request host used by managed updates.                                 |
| `updates.schedule`          | Cron string                                     | Schedule written by `playstack ci init`.                                   |
| `updates.requiredCheck`     | `string`                                        | Existing workflow job required before managed merge.                       |
| `updates.autoMerge`         | `boolean`                                       | Opt-in policy for managed minor and patch updates.                         |

Every target must remain inside the project. Unknown keys warn; invalid types and unsafe paths fail before writing.

## Artifact bridge

Packages that emit application files publish `playstack.artifacts.json`. Each artifact declares:

| Property            | Meaning                                                         |
| ------------------- | --------------------------------------------------------------- |
| `id`                | Stable identifier within the package.                           |
| `kind`              | `database-schema`, `content`, `emails`, `i18n`, or `source`.    |
| `format`            | Required database-schema representation: `prisma` or `drizzle`. |
| `dialect`           | Required for Drizzle schema artifacts; currently `postgresql`.  |
| `source` / `target` | Package-relative source and default application destination.    |
| `variant`           | Optional selectable schema or output variant.                   |
| `ejectable`         | Whether ownership may move to the application.                  |
| `securityCritical`  | Prevents ejection and requires package-managed updates.         |
| `hash`              | SHA-256 integrity check for the published source.               |

Managed files reject local drift. Ejected files record their original base under `.playstack/base` and use `git merge-file` for later three-way updates; conflicts do not overwrite the application file.

## Lint and Skills

The CLI can adopt the separately installed `@playstack/lint` and `@playstack/skills` packages without making either one a CLI runtime dependency.

```sh
npm install --save-dev @playstack/lint @playstack/skills
npx playstack lint init --diff
npx playstack lint init
npx playstack skills sync --diff
npx playstack skills sync
```

`lint init` emits a managed flat ESLint fragment. The recommended preset starts as warnings so a repository can ratchet adoption deliberately. Extension and Chakra rules require explicit file globs; Chakra may additionally restrict the semantic tokens available at that boundary.

`skills sync` installs the declared Markdown corpus into each configured target and records its state in `.playstack/skills.json`. Package content is hash-verified, local edits are reported as unsafe, and an explicit override can shadow one stable skill ID without changing its managed source. See the [Skills overview](/skills) and [`@playstack/skills` reference](/docs/packages/foundation/skills) for the full corpus.

## Complete command surface

| Command                                      | Purpose                                                                                                |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `--version`, `-V`                            | Print the installed executable package version.                                                        |
| `version [--json]`                           | Report CLI, configuration-schema, artifact-schema, and Node compatibility versions.                    |
| `help [command]`, `[command] --help`         | Explain command usage, arguments, flags, defaults, and examples without requiring a project.           |
| `init [--survey]`                            | Inspect a repository, then confirm and write configuration and optional maintenance hooks.             |
| `add <capability>`                           | Install a versioned package recipe and optional web or API bindings.                                   |
| `info [topic]`                               | Explain capabilities, packages, and operations without requiring repository configuration.             |
| `database compatibility [--database=<name>]` | Compare selected Prisma artifacts with an existing schema without writing or connecting to a database. |
| `database sync [--database=<name>]`          | Materialize schema artifacts into a selected named target.                                             |
| `emails init`, `content init`, `i18n init`   | Initialize the selected package-owned artifact kind.                                                   |
| `lint init`                                  | Emit or verify the managed Playstack ESLint fragment.                                                  |
| `skills sync`                                | Install or verify repository-local agent guidance.                                                     |
| `event-store replay\|status\|cancel`         | Operate projection replay through an authenticated, application-owned gateway.                         |
| `adapter scaffold <package>`                 | Materialize an application-owned persistence adapter with typed method stubs and contract-test seams.  |
| `eject`, `merge`                             | Transfer ownership of an ejectable artifact and reconcile later upstream changes.                      |
| `doctor`                                     | Check managed artifacts, lint, Skills, and ejected upstream state together.                            |
| `update [--pr]`                              | Update fixed Playstack versions and optionally partition changes into review branches.                 |
| `ci init`                                    | Emit the scheduled update workflow from committed policy.                                              |
| `feedback [message]`                         | Send a bug report, idea, or question to the Playstack API; `--last` attaches the last failed command.  |

## Capability recipes

`playstack add` currently knows 24 recipes: `accounts`, `analytics`, `audiences`, `audit`, `auth`, `auth-client`, `billing`, `cache`, `commerce`, `connections`, `content`, `delivery`, `devices`, `errors`, `events`, `licensing`, `local-first`, `mail`, `notifications`, `queues`, `rate-limit`, `search`, `storage`, and `validation`. Use `playstack add --list --json` for the versioned catalog and `playstack info <capability>` to inspect prerequisites, portable packages, optional web and API surfaces, supported database adapters, and provider choices before installation.

```sh
npx playstack add commerce --api=apps/api --database=primary --yes
npx playstack add notifications --web=apps/web --api=apps/api --database=primary --yes
npx playstack add search --web=apps/web --api=apps/api --adapter=minisearch --yes
npx playstack info commerce --json
```

`auth-client` installs the session contract and client-side web surfaces without credentials, persistence, Argon2, Auth.js, or a Nest server. Other provider-bearing recipes select adapters explicitly; no provider is installed merely because it was current when the package was authored.

The CLI installs package closures and records explicit targets; it does not directly generate controllers, routes, modules, migrations, or application composition roots. `add --dry-run --json` includes integration manifests and ejectable templates for Megamono or another repository generator to materialize as application-owned wiring. Third-party dependencies are installed through your repository's package manager.

## Feedback

`playstack feedback "message"` sends a bug report, idea, or question to the Playstack API. Nothing is sent without an explicit command: after a failed command the CLI prints a hint suggesting `playstack feedback --last`, which attaches the redacted failure record (command, exit code, message, CLI and Node versions) to the report. Set `PLAYSTACK_FEEDBACK_HINT=0` to silence the hint.

```sh
npx playstack feedback "The add command hung on pnpm install" --category=bug
npx playstack feedback --last
npx playstack feedback "Support Bun" --category=idea --dry-run
npx playstack feedback --resend
```

`--category` is one of `bug`, `idea`, `question`, or `other`; `--contact` adds an e-mail address for follow-up; `--dry-run` prints the report instead of sending it. Reports go to `https://api.playstack.dev/v1/feedback` unless `PLAYSTACK_FEEDBACK_URL` or `--endpoint=<url>` overrides it. If delivery fails the report is saved locally and `playstack feedback --resend` retries it later. The intake is defined by [`@playstack/feedback`](/docs/packages/events-operations/feedback) and served by [`@playstack/nest-feedback`](/docs/packages/events-operations/nest-feedback).

## Updates and CI

```sh
npx playstack update --dry-run
npx playstack update
npx playstack update --pr
npx playstack ci init
```

`update` is the explicit network and package-manager boundary. It updates only installed `@playstack/*` dependencies, synchronizes managed output, and attempts safe ejected-file merges. `--pr` additionally requires a clean tracked worktree and delegates review creation to the configured host adapter.

## Boundary

Artifact operations, information, and initialization are offline. `add` and `update` are explicit package-manager network boundaries; event-store commands are the explicit application-control network boundary. The CLI does not scaffold applications, run database migrations or schema push, connect to a database, or hide generated files.

Use named `databases` and `playstack database sync`. Superseded pre-release configuration and command aliases are not supported.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/cli@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/cli` | `./dist/index.d.ts` |
| `@playstack/cli/playstack.schema.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/cli/playstack.integration.schema.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/cli/playstack.integrations.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/cli/playstack.catalog.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/cli/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/admin-ui

Source: https://playstack.dev/docs/packages/ui/admin-ui.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

Available exports include `AdminShell`, `MembersSection`, standalone `MembersTable`,
`AudiencesSection`, `AudienceSubscribersTable`, `AudienceSubscriberDetail`,
`AudienceInvitationsSection`, optional `AudienceSubscriptionRemoval`,
`AudienceExport`, `AudienceSubscriberErasure` and
`WebhookIntegrationsSection`. These are
host-composed sections, not a complete admin app or global staff authorization service.

AdminShell accepts explicit loading, denied, error or ready access, trusted navigation entries, a host link renderer and slots for branding, notices and account/user controls. Non-ready states do not mount private content. Server authorization still runs before returning private data.

MembersSection accepts token-free member views, including nullable email, allowed-role hints and removal hints. A ready response must match its account/authority scope key. Role/removal callbacks serialize, receive cancellation signals and ignore stale completion; the server must revalidate current authority. Member removal includes confirmation and safe failure handling.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/admin-ui@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Prebuilt and custom Next routes

Use an application-owned client shell in a Next layout. Put MembersSection on a members route and ordinary App UI/product components on custom routes. Next Link, active-route matching, data clients and the composition root remain in that application; the package does not install a second router or discover plugins.

The reference composition combines a members route, a read-only members view, custom publishing routes and localized routes under one shell, on Chakra 3 with React 18 or 19 and server-side rendering.

Global staff access, impersonation, audit browsing, billing panels and other prebuilt sections are not shipped yet; the host Next application owns those routes and the services behind them.

## Audience operator controls

Compose list navigation, paginated status/tag/segment filters and safe consent/
provenance detail from host-controlled read projections. Survey data stays in host
extension slots. Scope/query/generation identity prevents late responses from
replacing the current view; loading, denied and error access never mounts private
subtrees. The host owns Pages Router/App Router integration, request-local SSR
clients and independently authorized backend reads/actions.

`AudienceInvitationsSection` displays confirmed-subscriber batch preview, explicit
commit, status and revoke/reissue controls. Callbacks receive bounded immutable
selection/context and cancellation; returned projections must match the exact
scope and request. A fingerprint/confirmation is not authority: native invitation
mutation must recheck current recipient, consent and admission policy. No proof,
email, bearer credential, HTTP route or new audience service is created in the UI.

`AudienceSubscriptionRemoval` is a separate optional soft-removal control, with
Cancel-focused confirmation, safe errors and authoritative reload. Abort means
stop waiting, not rollback; remote unsubscribe may still await provider convergence.
The host must enable native transaction-bound operator removal and trusted server
principal/source separately. These controls do not supply export download
infrastructure or automatically activate erasure/settings. Localized Chakra v3
controls retain keyboard/focus and stale-response checks; live app composition
still requires its own authorization/browser qualification.

### Explicit export and erasure controls

`AudienceExport` confirms a private export admission using exact scope, list,
query, selected fields and current server permission. The host supplies the
authorized native `AudienceExports.page` route and private transfer. Accepted
means admitted, not downloaded; pagination, total-work budgets, file retention
and CSV conversion remain host-owned. No contact rows or download URLs are
retained in the control.

`AudienceSubscriberErasure` reviews the complete all-list manifest and an
accepted server policy reference. It additionally requires explicit qualified
linked-cleanup, suppression, provider and re-enrollment hints before confirmation.
The server reauthorizes current all-list authority and maps its own trusted policy
to native lifecycle erasure. Matching accepted action **and** authoritative
erased inspection are required to settle a new action; neither implies complete
provider/backup erasure. Local cancellation cannot undo a commit or recall data.

Both are separately opt-in, localized, Cancel-focused controls. Scope/access/
generation changes cancel local waiting and ignore stale responses. No browser
projection grants authority, selects cleanup policy or replaces the package's
server lifecycle contracts.

### Package controls versus the Playstack admin app

Playstack's own admin app currently composes optional **read-only** early-access
and newsletter views through API-owned `ADMIN_EARLY_ACCESS_LIST_KEY` and
`ADMIN_NEWSLETTER_LIST_KEY`. `ADMIN_PRODUCT_UPDATES_CONSENT_ENABLED` enables a
separate read-only view of existing product-update preferences; it does not
convert them into audience subscriptions. All are disabled by default and retain
independent operator/session authorization.

Those flags do not create lists, accept public subscriptions, send invitations,
export/remove/erase contacts or enable webhook settings. Deploy the reviewed
schema and create the exact native lists through a qualified host composition
before configuring keys. Consuming applications choose their own controls and
guards; the package does not install these environment variables or a hosted
administration service.

## Masked webhook integration controls

`WebhookIntegrationsSection` composes current masked Slack/Discord settings with
explicit create/rotate/routing/enable/disable/revoke/test callbacks. The host
supplies approved event options and per-action permission hints; server-side
authorization and revision checks remain mandatory. Ready responses bind exact
scope, list, query and generation; malformed/substituted projections fail closed.

Destination URLs are write-only form input, cleared before callback and on cancel,
revision/scope/access changes and unmount. Never include URLs/ciphertext in masked
rows, read responses or diagnostics. Clearing cannot protect against browser
compromise. Mutations require Cancel-focused confirmation, exact acknowledgment
and authoritative reload; abort means stop waiting, not rollback. Test requests
contain no destination, payload or profile override and mean admission only, not
provider delivery. Compose the explicit native settings/Nest/outbox contracts
and qualify the deployed operator/provider flow separately.

## Shared localization

Shell states, navigation labels, member controls and portal confirmation/error copy use the shared `@playstack/i18n-react` provider. Compose `adminUICatalog` from `@playstack/admin-ui/messages` with `appUICatalog` from `@playstack/app-ui/messages`; both namespaces are required.

`./locales/en.json` and `admin-ui.messages.en` provide English source artifacts. Existing message overrides retain precedence and English works without a provider. Account/member names, roles and custom navigation remain host-owned. See [UI localization](/docs/frameworks/ui-localization); translations never change authorization or mutation ordering.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/admin-ui@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/admin-ui` | `./dist/index.d.ts` |
| `@playstack/admin-ui/package.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/admin-ui/messages` | `./dist/messages.d.ts` |
| `@playstack/admin-ui/locales/en.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/admin-ui/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/admin-ui/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@chakra-ui/react` | `^3.0.0` | Required by the package. |
| `@emotion/react` | `^11.0.0` | Required by the package. |
| `@playstack/chakra` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |
| `react-dom` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |
| `@playstack/app-ui` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/i18n-react` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/audiences-resend

Source: https://playstack.dev/docs/packages/communication/audiences-resend.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/audiences-resend` maps Playstack subscription commands and recipient pages to Resend Contacts and Segments. Consent, confirmation, suppression, and idempotent command state remain in `@playstack/audiences`.

```ts
import { resendAudienceAuthority } from '@playstack/audiences-resend'

const authority = resendAudienceAuthority({
  client: resend,
  resolveSegmentId: ({ list, segment }) => segmentIds[segment?.key ?? list.key],
})
```

The adapter accepts a structural Resend client and does not read environment variables. Removing a subscription removes only its segment membership; it does not silently delete the contact or globally unsubscribe it.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/audiences-resend@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/audiences-resend@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/audiences-resend` | `./dist/index.d.ts` |
| `@playstack/audiences-resend/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/audiences` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/auth-next

Source: https://playstack.dev/docs/packages/identity/auth-next.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/auth-next` provides explicit Pages and App Router session handoff and protection over an application-owned `@playstack/auth` service. Both bindings are structural and import no Next.js runtime.

## Install and compose

```sh
npm install @playstack/auth @playstack/auth-next
```

```ts
import { createAuthNextHelpers } from '@playstack/auth-next/pages'

export const { requireSession, withSession } = createAuthNextHelpers({
  async resolveSession(context) {
    return resolveSafeSessionView(context.req)
  },
})
```

The resolver receives the original structural Pages Router context and may use the application request/response escape hatches. Its result is validated with auth’s exact `AuthSessionView` parser before crossing the SSR boundary.

## Optional and protected pages

```ts
export const getServerSideProps = withSession(async ({ session }) => ({
  props: { title: session ? 'Dashboard' : 'Welcome' },
}))

export const getServerSideProps = requireSession(
  { redirectTo: '/login' },
  async ({ session }) => ({ props: { userId: session.user.id } }),
)
```

`withSession` passes `AuthSessionView | null` to the handler and injects the same value into successful props. `requireSession` provides a non-null session or a non-permanent same-origin redirect. Redirect and not-found results pass through unchanged.

## Configuration and contracts

| Surface                    | Properties                                                |
| -------------------------- | --------------------------------------------------------- |
| `AuthNextOptions`          | `resolveSession(context): Promise<unknown>`.              |
| `RequireSessionOptions`    | `redirectTo`, restricted to a safe same-origin path.      |
| Injected optional context  | Original context plus `session: AuthSessionView \| null`. |
| Injected protected context | Original context plus `session: AuthSessionView`.         |

Handler props may not define their own `session` key. Absolute, protocol-relative, backslash, and header-injection redirect targets are rejected. The ISO-string-only session projection can initialize `PlaystackAuthProvider` without an unauthenticated flash.

## App Router server boundaries

```tsx
import { redirect } from 'next/navigation'

import { createAppRouterAuthHelpers } from '@playstack/auth-next/app'

const auth = createAppRouterAuthHelpers({
  resolveSession: () => resolveSessionViewFromCookies(),
})

export default async function AccountPage() {
  const session = await auth.requireSession({
    redirectTo: '/login',
    onUnauthenticated: (destination) => redirect(destination ?? '/login'),
  })

  return <Account userId={session.user.id} />
}
```

`getSession()` returns a validated optional session. `requireSession()` returns an authenticated session or invokes the supplied unauthenticated policy. `withSession()` and `withRequiredSession()` wrap route handlers while preserving their original request and route-context arguments, including promised dynamic params.

| Surface                                 | Properties                                                                               |
| --------------------------------------- | ---------------------------------------------------------------------------------------- |
| `AppRouterAuthOptions`                  | `resolveSession(...args): unknown \| Promise<unknown>`.                                  |
| `AppRouterRequireSessionOptions`        | Optional same-origin `redirectTo` and an application-owned `onUnauthenticated` callback. |
| `withSession(handler)`                  | Adds `AuthSessionView \| null` before the original handler arguments.                    |
| `withRequiredSession(options, handler)` | Adds `AuthSessionView` or returns the application's unauthenticated response.            |

Without an unauthenticated policy, required access throws the stable `playstack/auth-next/unauthenticated` error. The application continues to own cookies, redirects, middleware, cache behavior, and authorization within server actions and protected operations.

## Boundary

The adapter authenticates nothing by itself and does not replace authorization inside server operations. Its router-specific entrypoints preserve the distinct lifecycle of each router while the application owns request resolution and framework behavior.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/auth-next@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/auth-next` | `./dist/index.d.ts` |
| `@playstack/auth-next/app` | `./dist/app.d.ts` |
| `@playstack/auth-next/pages` | `./dist/pages.d.ts` |
| `@playstack/auth-next/errors` | `./dist/errors.d.ts` |
| `@playstack/auth-next/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/auth-contracts` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/errors-react

Source: https://playstack.dev/docs/packages/foundation/errors-react.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/errors-react` connects an application-owned `@playstack/errors` reporter to React component trees.

## Install

```sh
npm install @playstack/core @playstack/errors @playstack/errors-react react
```

## Compose it at the React root

```tsx
import { ErrorsBoundary, ErrorsProvider } from '@playstack/errors-react'

export function ApplicationRoot() {
  return (
    <ErrorsProvider errors={errors}>
      <ErrorsBoundary
        fallback={({ error, reset }) => (
          <ErrorScreen error={error} onRetry={reset} />
        )}
        resetKey={router.location.key}
        development={import.meta.env.DEV}
        captureOptions={{ tags: { boundary: 'application-root' } }}
        onReset={() => queryClient.clear()}
      >
        <App />
      </ErrorsBoundary>
    </ErrorsProvider>
  )
}
```

`useErrors()` returns the exact reporter supplied to the closest provider, so event handlers and effects use the same capture policy as render boundaries.

## Provider properties

| Property | Type | Required | Purpose |
| --- | --- | --- | --- |
| `errors` | `ErrorReporter` | Yes | Existing reporter created by `configureErrors`. |
| `children` | `ReactNode` | No | The subtree that can call `useErrors` or render a boundary. |

## Boundary properties

| Property | Type | Required | Purpose |
| --- | --- | --- | --- |
| `fallback` | `ReactNode \| ({ error, reset }) => ReactNode` | Yes | Application-owned recovery UI. |
| `captureOptions` | `ErrorCaptureOptions` | No | Severity, tags, context, identity, fingerprint, or trace metadata. |
| `development` | `boolean` | No | Rethrow after capture so a development overlay or parent boundary sees the error. |
| `resetKey` | `unknown` | No | Reset the boundary when its value changes according to `Object.is`. |
| `onError` | `(error, info) => void` | No | Isolated diagnostic observer after capture. |
| `onReset` | `() => void` | No | Isolated observer invoked when the boundary resets. |
| `children` | `ReactNode` | No | Protected component subtree. |

Observer failures cannot replace the original render error or strand the fallback. A manual `reset()` and a `resetKey` change use the same recovery path.

## Bridge boundary

This adapter has no separate persistence or transport bridge. Its only outside-in dependency is the `ErrorReporter` passed to `ErrorsProvider`; the fallback and reset behavior remain application components and callbacks.

The package does not configure drivers, initialize vendor SDKs, read ambient environment variables, or import a router. React 18 or 19 and all `@playstack/*` peers stay application-owned at the same fixed version.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/errors-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/errors-react` | `./dist/index.d.ts` |
| `@playstack/errors-react/errors` | `./dist/errors.d.ts` |
| `@playstack/errors-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/errors` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/feeds-next

Source: https://playstack.dev/docs/packages/content-intelligence/feeds-next.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/feeds-next` exposes generated or inline feed documents through Next.js App Router-compatible handlers using only Web `Request`, `Response`, and stream APIs.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/feeds-next@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Serve pre-generated documents

```ts
import { createGeneratedDocumentRoute } from '@playstack/feeds-next'

const route = createGeneratedDocumentRoute({
  storage: feedsStorage,
  namespace: 'marketing',
  path: 'sitemap.xml',
  mode: 'auto',
  onError: (error) => errors.captureError(error),
})

export { route as GET, route as HEAD }
```

| Generated route property | Purpose |
| --- | --- |
| `storage`, `namespace`, `path` | Selects a committed generated document. |
| `mode` | `auto`, `proxy`, or `redirect`. |
| `redirectAboveBytes` | Overrides the core 5 MiB auto threshold. |
| `onError` | Isolated storage/stream observer. |

`path` may be a resolver. `pathFromParam(name, { prefix?, suffix? })` supports both direct and promised dynamic params used across Next versions while retaining core path validation.

## Stream inline documents

```ts
import { createInlineFeedRoute } from '@playstack/feeds-next'

export const GET = createInlineFeedRoute({
  format: 'rss',
  definition: (request) => feedForHost(request.headers.get('host')),
  entries: () => streamPublishedPosts(),
  content: 'full',
  cacheControl: 'public, max-age=60',
  onError: reportStreamFailure,
})
```

Inline feed options accept format, static or request-scoped definition, async entry source, full/excerpt mode, structured extensions, cache control, and error observer. `createInlineSitemapRoute` accepts the entry source, cache control, URL/byte ceilings, and observer. HEAD returns normal headers without invoking the entry source.

## Error behavior

Traversal-like or missing dynamic paths return 400; missing manifests/documents return 404; retryable storage failures return 503 with `Retry-After: 60`; unsupported methods return 405; other pre-response failures return 500. After streaming begins, failures reject the body stream and reach `onError` because status and headers can no longer change.

## Boundary

There is deliberately no `next` runtime or peer dependency. The application supplies generation, entries, storage, cache policy, and route placement; `@playstack/feeds` owns validation and serving semantics.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/feeds-next@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/feeds-next` | `./dist/index.d.ts` |
| `@playstack/feeds-next/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/feeds` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/inventory

Source: https://playstack.dev/docs/packages/commerce/inventory.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/inventory` answers whether a quantity can be promised now. Catalog, purchasing, payments, returns, and fulfillment remain outside its boundary.

```ts
import { createInventory } from '@playstack/inventory'

const inventory = createInventory({ persistence, events, ids, clock })

await inventory.reserve({
  reservationId: `order:${orderId}`,
  expiresAt: Date.now() + 30 * 60_000,
  lines: [{ itemId: 'stand-black', locationId: 'default', quantity: 1 }],
  idempotencyKey: `order:${orderId}:inventory`,
})
```

Multi-line reservations are all-or-nothing. Commit consumes on-hand stock; release and expiration restore availability. Repeated identical commands are no-ops, conflicting key reuse is rejected, and refunds never restock automatically.

Operations may join a compatible application transaction so an order and reservation commit atomically. `@playstack/inventory/prisma` uses PostgreSQL locking, while `@playstack/inventory/testing` supplies a serialized in-memory adapter.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/inventory@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/inventory@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/inventory` | `./dist/index.d.ts` |
| `@playstack/inventory/errors` | `./dist/errors.d.ts` |
| `@playstack/inventory/prisma` | `./dist/prisma.d.ts` |
| `@playstack/inventory/testing` | `./dist/testing.d.ts` |
| `@playstack/inventory/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/mail

Source: https://playstack.dev/docs/packages/infrastructure/mail.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/mail` separates typed application rendering and validated portable envelopes from Resend, Postmark, Amazon SES, Mailgun, SendGrid, SMTP, or test delivery providers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/mail@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose rendered mail

```ts
import { createMail } from '@playstack/mail'
import { createPostmarkMailProvider } from '@playstack/mail/postmark'

const mail = createMail({
  driver: createPostmarkMailProvider(process.env.POSTMARK_SERVER_TOKEN!, {
    messageStream: 'outbound',
  }),
  from: { address: 'hello@example.com', name: 'Example' },
  replyTo: 'support@example.com',
})

await mail.send({
  to: 'person@example.com',
  template: renderInvitation,
  props: { name: 'Ada', invitationUrl },
})
```

The application owns rendering and localization. A typed template returns subject plus HTML/text before provider I/O, so renderer failures are distinct from delivery failures. Chakra Email or any other renderer can implement it; this package has no React dependency.

## Driver configuration

| `createMail` property | Required | Purpose |
| --- | --- | --- |
| `driver` | Yes | Portable provider bridge with native client and capabilities. |
| `from` | Yes | Default address or `{ address, name }`. |
| `replyTo` | No | Default reply address, overridable per message. |

Messages accept `to`, `cc`, and `bcc` as one or many address inputs, optional sender/reply overrides, custom safe headers, and attachments with filename, MIME type, and byte/blob content. The total recipient ceiling is 50. Control/header injection and protected-header overrides fail before delivery.

## Provider bridge and capabilities

A provider exposes its native `client`, declares whether provider templates are supported, and sends a normalized rendered or provider-template message. `mail.client` and `mail.capabilities` preserve those escape hatches.

- Postmark supports rendered mail and provider `templateId`/exact-JSON `model`. Configure provider instances for the required message stream; an injected SDK client can be shared.
- Resend supports rendered mail and provider templates. Template variables are limited to strings and numbers and are validated before provider I/O.
- SMTP supports rendered mail and exposes the Nodemailer transport/`verify`; filesystem and URL attachment loading are disabled on the portable path.
- `CaptureMailProvider` from `@playstack/mail/testing` records normalized messages for tests. `messages()` returns the rendered/provider-template union; `rawMessages()` and `templateMessages()` return each side already narrowed, so a test can read `subject` and `text` without checking `kind` first.

```ts
import { CaptureMailProvider } from '@playstack/mail/testing'

const capture = new CaptureMailProvider()
const mail = createMail({ driver: capture, from: 'no-reply@example.com' })
await mail.send({ to: 'person@example.com', template: renderInvitation, props })
const [message] = capture.rawMessages()
expect(message?.text).toContain('/invitations/accept?token=')
```

## Results and failure policy

`send` returns provider message ID plus accepted/rejected recipients. Acceptance means submission, not inbox delivery. Input, unsupported-capability, and provider-rejection errors are non-retryable; rate-limit and definitive availability errors may be retried under application policy. `MailOutcomeUnknownError` means a send may already have succeeded, so reconcile before retrying.

SMTP success requires a nonempty message ID and explicit accepted/rejected arrays accounting for the requested recipients, with no foreign or conflicting evidence. An optional returned envelope must also match. Missing, malformed, mismatched, or partial evidence produces non-retryable uncertainty. `SmtpReceiptError` extends `MailOutcomeUnknownError` and exposes immutable indexed outcomes without recipient addresses. Complete explicit rejection is `MailRejectedError`; partial acceptance must never trigger a whole-message resend. A timeout or connection error can occur after submission and is not proof of rejection.

## Postmark attribution

The Postmark provider options support `tag` and `metadata`, forwarded as native `Tag` and `Metadata` for rendered and provider-template sends. Create a per-attempt provider around the same injected SDK client when attribution varies; no client-method wrapper is required. Tags allow up to 1,000 characters. Metadata allows up to ten fields, with keys up to 20 characters and values up to 80; case-insensitive duplicate keys are rejected before I/O. Attribution does not provide delivery idempotency.

## Boundary

The package submits immediately. Queueing, retry idempotency, provider webhooks, bounces, complaints, opens, and analytics belong to application workflows—typically `@playstack/queues`. Native provider-only attachments and administration remain available through `mail.client`.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/mail@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/mail` | `./dist/index.d.ts` |
| `@playstack/mail/errors` | `./dist/errors.d.ts` |
| `@playstack/mail/mailgun` | `./dist/mailgun.d.ts` |
| `@playstack/mail/types` | `./dist/types.d.ts` |
| `@playstack/mail/testing` | `./dist/testing.d.ts` |
| `@playstack/mail/postmark` | `./dist/postmark.d.ts` |
| `@playstack/mail/resend` | `./dist/resend.d.ts` |
| `@playstack/mail/ses` | `./dist/ses.d.ts` |
| `@playstack/mail/sendgrid` | `./dist/sendgrid.d.ts` |
| `@playstack/mail/smtp` | `./dist/smtp.d.ts` |
| `@playstack/mail/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@aws-sdk/client-sesv2` | `^3.1119.0` | Optional; only for the entry points that use it. |
| `@sendgrid/mail` | `^8.1.6` | Optional; only for the entry points that use it. |
| `mailgun.js` | `^14.0.0` | Optional; only for the entry points that use it. |
| `nodemailer` | `^10.0.13` | Optional; only for the entry points that use it. |
| `postmark` | `^5.1.0` | Optional; only for the entry points that use it. |
| `resend` | `^6.24.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-events

Source: https://playstack.dev/docs/packages/events-operations/nest-events.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-events` connects an application-owned dispatcher, optional outbox pump, and queue boundary to NestJS lifecycle without moving the domain event contract into Nest.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-events@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the module

```ts
PlaystackEventsModule.forRoot({
  dispatcher: eventBridge.events,
  pump,
  pollIntervalMs: 1_000,
  onPumpError: (error) => errors.captureError(error),
})
```

Use `forRootAsync({ imports, inject, useFactory })` for DI-resolved dependencies.

## Configuration reference

| `NestEventsOptions` property | Required | Purpose |
| --- | --- | --- |
| `dispatcher` | Yes | Handles recovered observational envelopes. |
| `pump` | No | Outbox pump exposing `runOnce()`. |
| `pollIntervalMs` | No | Positive, non-overlapping poll cadence. |
| `onPumpError` | No | Isolated sync/async operational failure observer. |

`PlaystackOutboxRunner` starts and stops with Nest and never overlaps pump executions. The module exports it so applications can disable autonomous polling and trigger runs through their own scheduler.

## Queue bridges

```ts
const publisher = new NestEventOutboxPublisher(queue)

await consumer.handle({ event: recoveredEnvelope })
```

`EventQueue.dispatch` receives the fixed `playstack.events.dispatch` job name, envelope payload, outbox ID as `jobId`, and trace context. Bind that job in the application worker to `PlaystackQueuedEventConsumer.handle`, which delegates to the core observational dispatcher.

`@Emits('accounts.member.role.changed')` adds method metadata for linting and documentation only. The method must still call `events.emit()` with the actual persistence transaction.

## Boundary

The module does not require a queue vendor, invent transactions, or make Nest the event schema. The core bridge remains framework-free; the application owns queue creation, processor registration, and lifecycle policy.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-events@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-events` | `./dist/index.d.ts` |
| `@playstack/nest-events/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/events` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/notifications

Source: https://playstack.dev/docs/packages/communication/notifications.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/notifications` defines what a user-directed notification means, validates its input, applies channel preferences, and routes work to queued mail, push delivery, or a persisted in-app inbox. It does not own broadcasts, audiences, templates, provider clients, application routes, or SMS.

## Install

```sh
npm install @playstack/notifications @playstack/delivery @playstack/events
```

## Define and compose notifications

```ts
import { createNotifications, defineNotifications } from '@playstack/notifications'

const registry = defineNotifications({
  'account.invitation.accepted': {
    input: invitationAcceptedSchema,
    category: 'transactional',
    channels: ['mail', 'push', 'inApp'],
    digestible: false,
    render: {
      mail: ({ input, locale }) => ({
        payload: { template: 'invite', input, locale },
      }),
      push: ({ input }) => ({
        payload: { title: 'Invitation accepted', body: input.summary },
      }),
      inApp: ({ input }) => ({
        title: 'Invitation accepted',
        body: input.summary,
        href: '/members',
      }),
    },
  },
})

const notifications = createNotifications({
  registry,
  preferences,
  locales,
  recipients,
  queue,
  delivery,
  inbox,
  events,
  clock,
  ids,
})
```

`enqueue()` validates input and creates endpoint-free, per-channel jobs. `process()` revalidates the job, preference, locale, and current recipient endpoint before rendering or dispatching it. Push recipients resolve to opaque device-token IDs and fan out through `@playstack/delivery/push`; provider credentials are loaded only by the delivery worker.

## Personal and account-scoped inboxes

Use an explicit recipient plus optional account for inbox operations. Omitted
`accountId` selects personal notifications only; it never means all accounts.
Your server must authorize the current recipient and account before calling these
methods. Account IDs in a request or job are not authorization evidence.

```ts
const scope = {
  recipient: { type: 'user', id: currentUserId },
  accountId: authorizedAccountId,
}
await notifications.listInbox({ ...scope, limit: 50 })
await notifications.countUnread(scope)
await notifications.markReadAndArchive(inboxId, scope)
const changed = await notifications.markReadBatch({ ...scope, limit: 100 })
```

Pass producer-owned `accountId` in enqueue options, alongside an optional stable
`idempotencyKey`. Attribution survives queueing and transactional preparation;
preference policies receive it, and renderers cannot replace it. Reusing a
notification/recipient identity under a different account rejects. Personal rows
remain separate from every account inbox, including cursor anchors and retention.

`markReadAndArchive` preserves independent first-read/archive timestamps in one
transaction. `markReadBatch` changes at most 1–1000 currently unread, unarchived
rows and commits their read events atomically; a zero count can mean eligible rows
are locked. It does not install a background drain or authorize bulk access.

The native inbox adapter requires the nullable `accountId` column and updated
recipient/account/order index before deployment. Existing unscoped rows remain
personal; do not infer account ownership from old content or links. Schema changes,
custom-adapter updates and application qualification remain explicit owner work.

## Configuration reference

### Prepare on the caller's transaction

For preparation only, use the public `createNotificationPreparer({ registry,
transactionKind, preferences, locales, recipients, clock, ids, safeOrigins? })`.
The resolvers are transaction-aware; no ordinary root, queue, delivery, inbox or
event ports are required. Its `prepareInTransaction` method shares all the
full-service validation, snapshot and closure safeguards below.

Configure optional `preparation: { transactionKind, preferences, locales,
recipients }` with transaction-aware resolvers. Each takes its ordinary input and
the original transaction context as a second argument; ordinary root resolvers
are never a fallback. `prepareInTransaction(job, transaction)` validates input,
rechecks native preference/category policy, resolves current locale/recipient and
renders. It returns deeply frozen `suppressed` or `prepared` delivery requests/
inbox data, without opening transactions, enqueueing, sending or emitting events.

Use it inside `PrismaNotificationQueue.consumeNext` to join Delivery intent,
encrypted protected content and required outbox writes—or inbox and its required
event—on the supplied handle. The packaged README has the maintained sequence.
Recipient outputs and results must be bounded portable JSON; jobs are snapshotted
before awaits, with up to 100 unique push targets. Policy/render/data failures
return fixed `preparation_failed`; missing capability or kind mismatch refuses.

Prepared values may contain addresses/proofs/rendered secrets: keep them private
and transient or encrypted, never ordinary jobs/logs/browser data. They are not
authorization receipts or send grants. Host consent/account/lifecycle and final
suppression checks, compatible locks, immutable hashes and later committed-begin
Delivery dispatch remain required. No new schema is needed. Ordinary `process`
remains non-caller-transaction and must not run inside the queue callback.

| `NotificationsOptions` property | Required | Purpose                                                                                   |
| ------------------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `registry`                      | Yes      | Typed definitions, Standard Schema input validators, channels, categories, and renderers. |
| `preferences`                   | Yes      | Resolves policy, quiet-hours, and marketing preferences per channel.                      |
| `locales`                       | Yes      | Resolves the recipient locale at enqueue and execution time.                              |
| `recipients`                    | Yes      | Resolves the current delivery subject and mail endpoint.                                  |
| `queue`                         | Yes      | Enqueues endpoint-free notification jobs.                                                 |
| `delivery`                      | Yes      | Hands rendered mail payloads to `@playstack/delivery`.                                    |
| `inbox`                         | For in-app channels | Transactional inbox persistence; omit for mail/push-only registries. |
| `admissions`                    | For keyed enqueue | Durable admission and queue-handoff replay persistence. |
| `preparation`                   | For caller-transaction preparation | Explicit same-handle preference, locale and recipient resolvers. |
| `events`                        | Yes      | Emits queued, delivered, failed, read, and archived lifecycle events.                     |
| `clock`, `ids`                  | Yes      | Application-owned time and identifiers.                                                   |
| `readRetentionMs`               | No       | Read-row retention; defaults to 90 days.                                                  |
| `safeOrigins`                   | No       | Explicit HTTPS origins allowed in in-app notification links.                              |

Transactional notifications bypass only marketing-preference decisions. They still honor policy and quiet-hours decisions and always pass through delivery suppression.

## Persistence and testing

### Outbound-only composition

Mail/push-only registries can omit `inbox`. An in-app definition without an inbox
fails configuration, and inbox read/mutation/retention methods reject without an
adapter. Do not supply a dummy store to compose mail-only notifications.

All three native database artifacts are independently optional:

| Artifact ID | Needed for |
| --- | --- |
| `notifications.prisma` | Native persisted in-app inbox. |
| `notifications.queue.prisma` | Native queued jobs and independent consumption replay receipts. |
| `notifications.admissions.prisma` | Caller-keyed logical notification admission. |

The updated CLI omits these schemas unless selected in `artifacts` or
`existingArtifacts`. Queue-only adoption does not propose unused inbox tables.
Use the [queue-only configuration](/docs/cli/configuration#selecting-schema-artifacts)
to validate existing native queue storage with `artifacts: []`, or explicitly
select `notifications.queue.prisma` for managed output. `existingArtifacts`
validation remains mandatory. An `inApp` definition still needs an inbox; keyed
enqueue still needs admissions, independently of queue replay receipts.
No selection applies migrations, deletes old files/tables or proves live readiness.

### Caller-keyed durable enqueue

```ts
import { PrismaNotificationAdmissionPersistence } from '@playstack/notifications/prisma'

const admissions = new PrismaNotificationAdmissionPersistence(prisma, {
  namespace: 'product:production',
})
const notifications = createNotifications({ ...options, admissions })
await notifications.enqueue(name, input, recipient, context, {
  idempotencyKey: 'invitation:opaque-id:accepted',
})
```

This composition fragment requires the optional `notifications.admissions.prisma`
artifact and a root Prisma client. Persist the logical key with the producer's
domain event and reuse it on every retry. Unkeyed calls remain new notifications;
a key without admission persistence rejects before queue I/O. Conflicting input
or recipient under the same key rejects instead of changing the existing intent.

Admissions retain the original notification ID, validated portable JSON input,
recipient, context and initial channel plan. Keep secrets, rendered content and
raw addresses out of jobs/admissions; storage is not encrypted. Worker-time
recipient and preference authority is rechecked.

A lost queue acknowledgment can repeat the same channel key. The queue/outbox
must retain that key or a tombstone for the full replay lifetime, including after
consumption; a short deduplication TTL or remove-on-complete is insufficient.
Confirmed handoffs are recorded before queued-event emission, so that event can
be retried without enqueueing again. Observational events remain at-least-once.

### Native queue and retained replay receipts

`PrismaNotificationQueue` from `@playstack/notifications/prisma` supplies that
queue contract without an application-written broker adapter. Explicitly select
and migrate `notifications.queue.prisma`, then configure a root Prisma client,
clock and immutable application/environment namespace. Construction starts no
worker; scheduling, bounded ticks and shutdown remain host-owned.

`consumeNext(callback)` locks one available job and supplies its physical
transaction to the callback. Join native Delivery intent registration, protected
content storage and a worker outbox on that handle, or join inbox insertion and
its required event. The callback must not call `notifications.process()` (which
has no caller-transaction contract), open another root transaction or perform
provider/network I/O. Recheck definition, preference, actor and recipient
authority during this admission. A later committed-begin Delivery worker owns
external effects.

Successful consumption inserts an independent `NotificationQueueReceipt` and
deletes the job atomically. Replaying the same key after job deletion cannot
recreate work; changed immutable input conflicts. Rollback leaves the job and
no committed downstream handoff. A tick returning `false` means no currently
claimable row, not proof that all workers have drained.

Receipts retain namespace, logical key, request hash and completion time, not the
consumed job payload. Those identifiers are still retained data. There is no
automatic TTL or safe replay-key reuse: deleting receipts while replay remains
possible reopens delivery. Review pending payloads, admission records, protected
content, downstream queues, provider records and backups together before claiming
complete erasure. See the [Audiences consent-mail recipe](/docs/packages/communication/audiences#durable-confirmation-mail).

The host owns durable producer replay, downstream deduplication, retention and
erasure. Admissions have no automatic expiry/deletion policy and do not join an
arbitrary domain transaction. Queue acceptance is not provider delivery; pair with
the [durable delivery worker](/docs/packages/communication/delivery#durable-attempts-and-replay)
for provider attempts. Neither layer promises exactly-once delivery.

`@playstack/notifications/prisma` exports `PrismaNotificationInboxPersistence`;
explicitly selecting the optional `notifications.prisma` artifact synchronizes
its inbox schema fragment. Applications own migration execution.
`@playstack/notifications/testing` provides deterministic in-memory queue and
inbox implementations.

In-app persistence and its delivered event share one transaction. Queue and observational event failures cannot make another notification channel fail.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/notifications@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/notifications` | `./dist/index.d.ts` |
| `@playstack/notifications/errors` | `./dist/errors.d.ts` |
| `@playstack/notifications/prisma` | `./dist/prisma.d.ts` |
| `@playstack/notifications/testing` | `./dist/testing.d.ts` |
| `@playstack/notifications/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Events and operations

Source: https://playstack.dev/docs/packages/events-operations.md

These packages connect work happening inside a capability to persistence, queues, projections, analytics destinations, and request-level enforcement without making those outside systems part of the domain contract.

## Events and event storage

| Package | Plan | Responsibility |
| --- | --- | --- |
| [`@playstack/events`](/docs/packages/events-operations/events) | <PlanTierDot tier="free" /> | Typed in-process domain events and Bridge seams. |
| [`@playstack/events-prisma`](/docs/packages/events-operations/events-prisma) | <PlanTierDot tier="pro" /> | Transactional Postgres outbox for Prisma applications. |
| [`@playstack/nest-events`](/docs/packages/events-operations/nest-events) | <PlanTierDot tier="pro" /> | NestJS lifecycle and queue delivery bindings. |
| [`@playstack/event-store`](/docs/packages/events-operations/event-store) | <PlanTierDot tier="free" /> | Event sourcing, upcasting, projections, and checkpoints. |
| [`@playstack/nest-event-store`](/docs/packages/events-operations/nest-event-store) | <PlanTierDot tier="pro" /> | Thin NestJS wiring for event-store services. |

## Enforcement and measurement

| Package | Plan | Responsibility |
| --- | --- | --- |
| [`@playstack/rate-limit`](/docs/packages/events-operations/rate-limit) | <PlanTierDot tier="free" /> | Portable token-bucket policy and atomic storage contract. |
| [`@playstack/rate-limit-redis`](/docs/packages/events-operations/rate-limit-redis) | <PlanTierDot tier="pro" /> | Atomic Redis rate-limit storage. |
| [`@playstack/rate-limit-prisma`](/docs/packages/events-operations/rate-limit-prisma) | <PlanTierDot tier="pro" /> | Postgres and Prisma rate-limit storage. |
| [`@playstack/rate-limit-do`](/docs/packages/events-operations/rate-limit-do) | <PlanTierDot tier="pro" /> | Cloudflare Durable Object rate-limit storage. |
| [`@playstack/nest-rate-limit`](/docs/packages/events-operations/nest-rate-limit) | <PlanTierDot tier="pro" /> | NestJS policy declarations, guards, and headers. |
| [`@playstack/analytics`](/docs/packages/events-operations/analytics) | <PlanTierDot tier="free" /> | Consent-aware analytics fan-out and provider capabilities. |
| [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react) | <PlanTierDot tier="pro" /> | React consent controls and route tracking. |
| [`@playstack/nest-analytics`](/docs/packages/events-operations/nest-analytics) | <PlanTierDot tier="pro" /> | Queued NestJS delivery for server analytics. |
| [`@playstack/feedback`](/docs/packages/events-operations/feedback) | <PlanTierDot tier="paid" /> | Durable intake for CLI feedback and failed-command reports. |
| [`@playstack/nest-feedback`](/docs/packages/events-operations/nest-feedback) | <PlanTierDot tier="paid" /> | NestJS route for the anonymous feedback intake. |

## Composition rule

Domain packages publish events through explicit Bridge seams. Applications decide whether an event stays in-process, enters an outbox, becomes a queued job, updates a projection, or fans out to analytics.

## Frameworks

Source: https://playstack.dev/docs/frameworks.md

Playstack keeps product capabilities independent from application frameworks, but an integration still has to meet the framework at its real boundaries: requests, sessions, dependency injection, rendering, background work, and shutdown.

Choose the guide for the runtime or UI layer you are building. Each guide starts with a small working composition and links back to the canonical package references for configuration details.

## Choose your starting point

| Framework | Start here | What Playstack adds |
| --- | --- | --- |
| [Next.js](/docs/frameworks/nextjs) | Full-stack React applications, route handlers, and server rendering. | Safe session handoff, Auth.js bridging, client auth state, analytics, and feed routes. |
| [NestJS](/docs/frameworks/nestjs) | APIs, workers, and service-oriented application backends. | Modules, guards, decorators, lifecycle bindings, queues, events, and account context. |
| [React](/docs/frameworks/react) | Browser applications and framework-owned component trees. | Headless providers, hooks, account switching, error boundaries, and analytics consent. |
| [Workers](/docs/frameworks/workers) | Web-standard edge functions and Cloudflare Workers. | Portable domain contracts, Web Crypto, streaming responses, and Durable Object adapters. |

## One model across every framework

The framework guide changes the composition root, not the domain model:

```text
application framework -> Playstack adapter -> portable Playstack capability
```

A Next.js route and a NestJS controller can use the same `@playstack/auth`, `@playstack/accounts`, and `@playstack/events` contracts. Their adapter packages differ because their request lifecycles differ. A Worker can use compatible domain packages directly without importing either framework.

## Package references remain canonical

Framework guides show how packages fit together. Package pages remain the source of truth for exported APIs, configuration properties, security constraints, events, persistence artifacts, and escape hatches. Follow the package links from a framework guide when you need the complete contract.

If your application spans more than one framework, compose each edge independently. A common shape is a Next.js frontend using the React bindings, a NestJS API using server modules and guards, and Worker-compatible domain packages shared between them.

## Integration planning

Source: https://playstack.dev/docs/cli/integration-planning.md

Playstack packages declare the application work needed to compose a capability in `playstack.integration.json`. The package remains authoritative for its framework requirements and templates; the repository tool decides where reviewed application-owned files belong.

## Inspect the selected closure

```sh
npx playstack add rate-limit \
  --api=apps/api \
  --adapter=redis \
  --dry-run \
  --json
```

The report declares `writes: false` and contains both the selected package closure and normalized `integrationRequirements`. Exact `surface`, `adapter:<name>`, and package conditions remove irrelevant requirements before the plan reaches Megamono.

| Requirement                        | What it communicates                                                             |
| ---------------------------------- | -------------------------------------------------------------------------------- |
| Templates                          | Integrity-checked, ejectable source plus the suggested application target.       |
| Module imports and provider tokens | Framework registration and dependency-injection boundaries.                      |
| Routes                             | Endpoint method, path purpose, and handler contract.                             |
| Environment                        | Semantic key, suggested names, whether it is required, and whether it is secret. |
| Database artifacts                 | Schema requirements tied to the selected named database target.                  |
| Framework configuration            | Raw-body, proxy, cache, or other host-framework changes.                         |
| Health checks                      | Bounded readiness checks expected for the selected provider.                     |
| Required tests                     | Contract, integration, and application-policy checks to retain.                  |
| Decisions                          | Product-specific questions that automation cannot safely answer.                 |

## Keep generation application-owned

Dry runs now expose `integrationCoverage` separately from requirements: `dependencyValidation` is `declared-ranges-only` and `applicationVerification` is `not-run`. Each inspected package reports manifest availability, selected templates and required tests; `applicationPersistence` lists application-owned adapter work. Missing integration declarations can be appropriate for utilities, but a successful installation plan is not completed wiring or a passing integration suite.

Playstack does not apply the JSON report or write routes and modules itself. Megamono consumes the plan with its Nx-aware project inventory, resolves destinations, reports conflicts, and materializes reviewed templates as application code. A repository without Megamono can copy or adapt the same package-owned templates manually.

Generated wiring is intentionally ejectable. Once materialized, the application owns authorization, credentials, route names, infrastructure lifecycle, deployment topology, and product policy. Managed database and other security-critical artifacts remain under Playstack's separate artifact synchronization contract.

## Published contracts are verified

Every integration manifest is validated against a shared schema before a package is published, and each referenced template ships with its SHA-256 hash, so a dry-run plan is executable however the package was installed.

Manifests cover adoption-focused surfaces including rate limiting, cache, queues (now including Cloudflare Worker templates), auth, accounts, billing, storage, webhooks, AT Protocol and localization. Some declare requirements without templates. Use the selected plan's coverage rather than treating this list as a universal wiring guarantee.

## Existing workspace discovery

`playstack init --survey` recognizes `project.json`, package-manifest Nx declarations, and npm/Yarn/pnpm workspace patterns and exclusions. Dual declarations merge by project root, with `project.json` taking precedence; duplicate names and conflicts are reported. Generated/symlinked directories are excluded, and root dependencies alone do not turn a source-less tooling project into a Next/Nest app.

This remains static discovery, not execution of Nx inference plugins. Review ambiguous projects and explicit surfaces, or use Megamono's richer project inventory. A survey does not authorize changing deployment targets or existing CI.

## Separate planning from installation

Review the JSON plan before running the corresponding mutation:

```sh
npx playstack add rate-limit \
  --api=apps/api \
  --adapter=redis \
  --yes
```

The CLI re-resolves package and peer ranges during installation. An existing dependency counts as compatible only when its declared range fits the verified adapter range; presence by package name alone is insufficient.

## React

Source: https://playstack.dev/docs/frameworks/react.md

Playstack domain React packages are headless bindings over portable services. They carry safe state through a component tree and expose application-neutral hooks; they do not configure a router, render branded UI, initialize vendor SDKs, or authorize server operations.

[Start with the React guide](/docs/frameworks/react/getting-started) to add session state and an application error boundary.

## Available React bindings

| Binding                                                                          | Core capability                                                      | Responsibility                                                            |
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| [`@playstack/auth-react`](/docs/packages/identity/auth-react)                    | [`@playstack/auth`](/docs/packages/identity/auth)                    | Session state, auth actions, revalidation, and cross-tab synchronization. |
| [`@playstack/accounts-react`](/docs/packages/identity/accounts-react)            | [`@playstack/accounts`](/docs/packages/identity/accounts)            | Visible-account loading and active-account selection.                     |
| [`@playstack/errors-react`](/docs/packages/foundation/errors-react)              | [`@playstack/errors`](/docs/packages/foundation/errors)              | Reporter context and resettable render error boundaries.                  |
| [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react) | [`@playstack/analytics`](/docs/packages/events-operations/analytics) | Analytics context, reactive consent, and explicit route tracking.         |
| [`@playstack/audiences-react`](/docs/packages/communication/audiences-react)    | [`@playstack/audiences`](/docs/packages/communication/audiences)     | Subscribe and preference-center state with safe public responses.         |
| [`@playstack/notifications-react`](/docs/packages/communication/notifications-react) | [`@playstack/notifications`](/docs/packages/communication/notifications) | In-app inbox state, unread counts, pagination, and optimistic mutations. |

The binding accepts an already configured core service or transport. This keeps provider initialization, API origins, consent persistence, and framework routing in the application.

## Provider order

A useful default places operational reporting outside stateful product providers:

```text
ErrorsProvider
└── ErrorsBoundary
    └── PlaystackAnalyticsProvider
        └── PlaystackAuthProvider
            ├── AccountsProvider when account switching is needed
            └── Audience or notification providers where their UI is rendered
```

This is not a mandatory global provider stack. Mount providers only around the subtrees that consume them, and keep server-derived initial state as narrow as possible.

## Security boundary

`RequireSession` and account-aware navigation prevent confusing client transitions; they do not establish authorization. The server must revalidate authentication, membership, roles, entitlements, and API-key scopes for every protected operation.

## Framework composition

Use these same bindings beneath Next.js, another React meta-framework, or a client-rendered application. Router-specific behavior is opt-in: analytics exposes dedicated Pages and App Router helpers, while other routers call `analytics.page()` after their own completed-navigation signal.

## Optional application UI

[Chakra](/docs/packages/ui/chakra), [App UI](/docs/packages/ui/app-ui) and [Admin UI](/docs/packages/ui/admin-ui) add themes, controlled application compositions and the initial scoped members section. These are independent of auth/backend adoption. The host owns routes, data, mutations and authorization; Next Client Components hold callbacks and the Chakra system. See [Application interfaces](/features/application-ui).

## Using Playstack with Megamono

Source: https://playstack.dev/docs/megamono.md

Megamono and Playstack meet at an explicit boundary: Megamono knows where applications and infrastructure projects belong; Playstack knows which packages and artifacts make a product capability complete.

<Callout title="New to Megamono?">

[Visit megamono.com](https://megamono.com) to learn what it is and explore its tools and documentation.

</Callout>

| Concern                                                                                                                                        | Owner            |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| Workspace, Nx graph, app paths, database-project placement, and generic CI                                                                     | Megamono         |
| Capability recipes, prerequisites, framework bindings, database compatibility, artifact manifests, integration contracts, and wiring templates | Playstack        |
| Discovery, placement, planning, and materialization of reviewed application wiring                                                             | Megamono         |
| Final controllers, routes, composition roots, provider credentials, migrations, and product policy                                             | Your application |

Neither side needs to duplicate the other. Playstack does not import Megamono, inspect an Nx graph, read `megamono.json`, or assume `apps/web` and `apps/api` unless those paths are passed explicitly.

## Start with repository shape

Use Megamono's guided initialization when adopting an existing repository or shaping a new monorepo:

```sh
megamono init
```

Create or register the application-owned database target before adding a persistence-bearing capability:

```sh
megamono add database primary --adapter=prisma
```

Database names are user-defined lowercase slugs. Megamono owns the database project and records the matching named Playstack target; it does not hide the selected ORM or run Playstack package migrations.

## Add a Playstack capability

Preview the exact package and surface selection first:

```sh
playstack info auth --web --api
playstack add auth \
  --web=apps/web \
  --api=apps/api \
  --database=primary \
  --dry-run
```

For automation, add `--json`. The versioned plan includes the entire package closure plus active routes, environment variables, module imports, framework configuration, health checks, required tests, application decisions, and integrity-checked ejectable templates.

Apply the same plan non-interactively after review:

```sh
playstack add auth \
  --web=apps/web \
  --api=apps/api \
  --database=primary \
  --yes
playstack init
```

Megamono may invoke this operation for a concise capability selection such as `auth`, `accounts`, `billing`, or `content`. It must pass the app paths and database name it already knows. The same Playstack commands remain independently executable in a plain Node repository.

Fully qualified `@playstack/*` package names are Megamono's direct-install escape hatch when you want a package rather than a capability recipe. Use `playstack info <capability>` when you want prerequisites and compatible web, API, and persistence surfaces selected together.

## Check storage, then materialize artifacts

For an existing Prisma schema, run the read-only compatibility report before writing:

```sh
playstack database compatibility --database=primary
playstack database sync --database=primary --dry-run
playstack database sync --database=primary
```

Then use the application's ORM tooling explicitly:

```sh
prisma migrate dev
```

The sequence is intentionally visible:

```text
megamono add database
  -> playstack add
  -> playstack database compatibility
  -> playstack database sync
  -> application migration tooling
```

Playstack never connects to the database, generates a migration history, runs schema push, or applies a migration. If an existing schema is semantically equivalent but not physically compatible, implement the package persistence interface in the application rather than declaring the managed artifact satisfied. See [Existing schema adoption](/docs/cli/existing-schema-adoption).

## Generate and compose the application boundary

Package installation is not application wiring. Integration-facing packages publish `playstack.integration.json`; Playstack filters each contract for the selected surface and adapters, then exposes it in `add --dry-run --json`. Megamono can place and materialize those templates because it knows the Nx project graph. The resulting files are ejectable and application-owned.

The current manifest-backed surfaces include rate limiting, cache and Next caching, queues, auth, accounts, billing, storage, webhooks, and AT Protocol. Requirements can describe module imports, provider tokens, environment variables, endpoint contracts, raw-body handling, framework configuration, health checks, required tests, and unresolved product decisions.

After Playstack and Megamono produce the reviewed plan:

1. construct the portable service with application-owned persistence, events, clocks, IDs, and provider clients;
2. register the optional NestJS or Next.js binding at the relevant app edge;
3. review or complete generated controllers, routes, providers, workers, health checks, and tests around the exported service; and
4. run the repository's ordinary tests, builds, migrations, and deployment checks.

This preserves a useful division of labor: Playstack authors the authoritative integration contract, Megamono places and materializes it, and the application keeps the generated code plus its product and runtime decisions.

## Verify the handoff

```sh
playstack doctor --check
```

Megamono can schedule this read-only command in repository health checks. Playstack remains the single owner of managed-artifact, lint, Skills, and ejected-upstream drift logic; Megamono does not reimplement those checks.

`playstack init --survey --json` uses the same Nx-aware inventory shape for direct adoption: nested Next and Nest applications, Pages or App Router, workers, Redis and BullMQ usage, datasource-bearing Prisma contexts, and generated schema derivatives are reported without human output or writes.

## @playstack/event-store

Source: https://playstack.dev/docs/packages/events-operations/event-store.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/event-store` configures native Castore event stores with injected time, read-time upcasting, optimistic concurrency, projections, checkpoints, and Playstack testing conventions.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/event-store@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and create a store

```ts
import { createEventStore } from '@playstack/event-store'

export const orders = createEventStore({
  eventStoreId: 'orders',
  eventTypes: [orderCreated, itemAdded, orderSubmitted],
  reducer: orderReducer,
  eventStorageAdapter: storage,
  clock,
  upcast: orderUpcaster,
  onEventPushed: publishOrderBridgeEvent,
})
```

The returned value is Castore’s native `EventStore`; aggregate APIs, commands, event types, query options, and escape hatches remain available directly.

## Store configuration

| `PlaystackEventStoreOptions` property | Required | Purpose |
| --- | --- | --- |
| `eventStoreId` | Yes | Stable Castore store identifier. |
| `eventTypes` | Yes | Aggregate event type definitions. |
| `reducer` | Yes | Rebuilds aggregate state from events. |
| `eventStorageAdapter` | For persistence | Castore-compatible storage bridge. |
| `clock` | No | Supplies missing event timestamps; defaults to system time. |
| `upcast` | No | Transforms historical events on read; identity by default. |
| `simulateSideEffect` | No | Native Castore command simulation hook. |
| `onEventPushed` | No | Native Castore append observer; bridge only intentionally selected events. |

`createUpcaster` composes explicit version steps while preserving event metadata. Appends retain Castore’s optimistic concurrency behavior and expose `ConcurrencyError` helpers.

## Projection bridges

| Contract | Members |
| --- | --- |
| `ProjectionEventSource` | `read({ after, limit, eventStoreId? })`. |
| `ProjectionAdapter` | Stable `id` and idempotent `apply(event)`. |
| `ProjectionCheckpointStore` | `read`, `advance`, and `reset` by projection ID. |
| `SnapshotStore` | `read`, `write`, and `delete` by aggregate ID. |

Projection delivery is at-least-once. `ProjectionRunner` skips positions at or behind its checkpoint but checkpoint advancement is intentionally not transactionally coupled to the destination write, so every adapter must apply an event idempotently.

Snapshot policy is explicit: `never` by default or `{ kind: 'every', events }`. Snapshots optimize loading; the persisted event stream remains authoritative.

## Persistence and testing

`@playstack/event-store/prisma` provides structural Postgres event storage and checkpoint adapters. The application owns its generated client, connection lifecycle, schema composition, and migrations. `@playstack/event-store/testing` provides a clocked in-memory adapter.

## Boundary

Choose event sourcing per aggregate. This package does not make every Playstack domain event event-sourced; regular `@playstack/events` integration envelopes remain the default cross-package seam.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/event-store@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/event-store` | `./dist/index.d.ts` |
| `@playstack/event-store/prisma` | `./dist/prisma.d.ts` |
| `@playstack/event-store/testing` | `./dist/testing.d.ts` |
| `@playstack/event-store/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/event-store/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/fulfillment

Source: https://playstack.dev/docs/packages/commerce/fulfillment.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/fulfillment` projects paid physical lines into fulfillment work while keeping personally identifiable address data encrypted and out of domain events.

```ts
import { createFulfillment } from '@playstack/fulfillment'
import { createManualFulfillmentProvider } from '@playstack/fulfillment/manual'

const fulfillment = createFulfillment({
  persistence,
  addresses: crypto,
  providers: [createManualFulfillmentProvider()],
  events,
  ids,
  clock,
})
```

The `addresses` codec can be implemented by `@playstack/crypto`. Persistence receives ciphertext, an opaque digest, and routing-safe country code. Explicit application operations control decryption.

The manual provider is a production headless staff workflow with start, partial work, ship, deliver, fail, and pre-shipment cancel transitions. `@playstack/fulfillment/prisma` provides the PostgreSQL adapter and can share a transaction with `@playstack/commerce`.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/fulfillment@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/fulfillment@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/fulfillment` | `./dist/index.d.ts` |
| `@playstack/fulfillment/errors` | `./dist/errors.d.ts` |
| `@playstack/fulfillment/manual` | `./dist/manual.d.ts` |
| `@playstack/fulfillment/prisma` | `./dist/prisma.d.ts` |
| `@playstack/fulfillment/testing` | `./dist/testing.d.ts` |
| `@playstack/fulfillment/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-auth

Source: https://playstack.dev/docs/packages/identity/nest-auth.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-auth` connects `@playstack/auth` session validation and its fixed CSRF transport to NestJS guards and decorators.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-auth@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose

```ts
PlaystackAuthModule.forRoot({
  sessions,
  allowedOrigins: ['https://app.example'],
})
```

Use `forRootAsync({ imports, inject, useFactory })` when the session strategy depends on other Nest providers.

```ts
@UseGuards(PlaystackAuthGuard, PlaystackCsrfGuard)
@Post('/profile')
update(@CurrentSession() session: SessionClaims) {}

@Public()
@Get('/health')
health() {}
```

## Configuration reference

| `NestAuthOptions` property | Required | Purpose |
| --- | --- | --- |
| `sessions` | Yes | Any structural `SessionStrategy`, or a factory `({ devices }) => SessionStrategy` that receives the injected device validator. |
| `allowedOrigins` | Yes | Exact origins accepted for cookie-authenticated unsafe requests. |
| `devices` | No | A `DeviceSessionValidator` such as the `@playstack/devices` service; also resolvable from the `NEST_DEVICE_SESSION_VALIDATOR` provider. |
| `guards` | No | The composition `PlaystackGuardStack` runs: `'cookie'` (default), `'bearer'`, or guard classes in order. |
| `globalGuards` | No | Registration only: install the composed stack as a global guard; routes opt out with `@Public()`. |
| `imports` | No | `forRoot` only: modules exporting `NEST_DEVICE_SESSION_VALIDATOR` or application guards. |
| `sessionCookieName` | No | Host-only cookie name; must begin `__Host-`. |
| `csrfCookieName` | No | Synchronizer cookie name; must begin `__Host-`. |
| `csrfHeaderName` | No | Header carrying the synchronizer token. |
| `credentialExtraction` | No | Opt into `'strict'` raw-header extraction for ambiguous credential refusal. |
| `credentialHeaders` | Strict custom transports | Trusted reader returning original header pairs when the framework does not expose them. |

`PlaystackAuthGuard` accepts the host-only cookie or a strict bearer token and attaches only `SessionClaims` as `request.playstackSession`. A validation failure thrown by the strategy, such as a revoked device, is answered with 401; retryable persistence failures keep their original error. `PlaystackCsrfGuard` validates Origin plus synchronizer token for cookie-authenticated unsafe methods; bearer requests do not use ambient browser credentials and bypass CSRF.

With `credentialExtraction: 'strict'`, original Node/Fastify raw header pairs (or
the trusted reader) must agree with the normalized headers. Duplicate security
headers, malformed bearer values, repeated credential/CSRF cookies, and a bearer
combined with a session cookie fail closed. Parsed cookies alone cannot prove
the original request was unambiguous. Qualify the real proxy/framework path;
session, origin and CSRF guards share this extraction profile.

## Split-origin cookie routes

When the page and the API are different origins, the page cannot read a CSRF cookie set on the API origin, so the synchronizer-token guards do not apply. `PlaystackOriginGuard` is the defence for that profile: an unsafe browser request must present an `Origin` from `allowedOrigins` and, when the browser sends it, a `Sec-Fetch-Site` that is not `cross-site`. A request with no `Origin`, no Fetch Metadata and no cookie carries no ambient credential and passes, so a CLI, a mobile app or a server can call the same routes with a bearer token or explicit credentials. `isBrowserRequest(request)` exposes that decision for controllers that choose a transport per client. A refusal answers 403 with `{ error: 'origin_not_allowed' }`.

| Guard | Profile |
| --- | --- |
| `PlaystackCsrfGuard` | Same-origin cookie sessions with a synchronizer token; bearer requests are exempt. |
| `PlaystackCookieCsrfGuard` | Cookie-mutating routes on that profile; never bearer-exempt. |
| `PlaystackOriginGuard` | Split-origin bearer contract; browser requests need an allowed `Origin`, non-browser requests pass. |

## Registered devices via dependency injection

`@playstack/devices` is not a dependency of this package. Its service satisfies the `DeviceSessionValidator` contract from `@playstack/auth`, so the module accepts it through Nest injection and passes it to a session factory.

```ts
import { Module } from '@nestjs/common'
import { databaseSessions } from '@playstack/auth'
import { NEST_DEVICE_SESSION_VALIDATOR, PlaystackAuthModule } from '@playstack/nest-auth'

@Module({
  providers: [{ provide: NEST_DEVICE_SESSION_VALIDATOR, useValue: devices }],
  exports: [NEST_DEVICE_SESSION_VALIDATOR],
})
class DevicesModule {}

PlaystackAuthModule.forRoot({
  imports: [DevicesModule],
  sessions: ({ devices }) =>
    databaseSessions({ persistence, crypto, events, clock, ids, deviceValidator: devices, requireDevice: true }),
  allowedOrigins: ['https://app.example'],
})
```

With `forRootAsync`, inject the service and return it as `devices`, or export the provider from an imported module; either way the `sessions` factory receives it and `NEST_AUTH_OPTIONS` exposes the resolved validator. Register `sessions.deviceRevocationHandler()` for `devices.device.revoked` on the shared emitter so revoking a device revokes its sessions in the same transaction.

## Composing guards

The application composes the guards; the package never installs one on its own. Name a default composition by transport profile and place `PlaystackGuardStack` where it belongs, install that composition globally with `globalGuards: true` and opt routes out with `@Public()`, or list your own layering of package guards, other Playstack bindings and application guards.

```ts
PlaystackAuthModule.forRoot({ sessions, allowedOrigins, guards: 'bearer' })

@UseGuards(PlaystackGuardStack)
@Controller('accounts')
export class AccountsController {}

// Your own order, mixing bindings and application guards:
PlaystackAuthModule.forRoot({
  sessions,
  allowedOrigins,
  guards: [PlaystackOriginGuard, TenantGuard, PlaystackAuthGuard, PlaystackAccountGuard],
})
```

`'cookie'` is `PlaystackCsrfGuard` then `PlaystackAuthGuard`; `'bearer'` is `PlaystackOriginGuard` then `PlaystackAuthGuard`; `playstackGuardStacks` exports both for spreading into `@UseGuards`. The stack resolves each guard once from the application's providers, or constructs it with its dependencies when it is not registered, evaluates them in order, and stops at the first denial. A bad composition fails at bootstrap. Every guard stays exported for individual placement.

## Boundary

The underlying `SessionStrategy` is the replacement seam. The adapter does not own users, accounts, provider normalization, persistence, or authorization, and it never installs a guard the application did not ask for. Applications control guard placement and ordering.

## Authorization and credentials are separate surfaces

The package includes optional device-authorization and credential-route integration resources in addition to session/refresh/sign-out composition. Templates are application-owned wiring: configure registration policy, input validation, consent, CSRF/raw transport behavior, rate limits and required transactional adapters before enabling routes. Device and authorization-code success responses are not replayable recovery stores.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-auth@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-auth` | `./dist/index.d.ts` |
| `@playstack/nest-auth/device` | `./dist/device.d.ts` |
| `@playstack/nest-auth/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/auth` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-errors

Source: https://playstack.dev/docs/packages/foundation/nest-errors.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-errors` attaches an existing `@playstack/errors` reporter to NestJS dependency injection and exception boundaries.

## Install

```sh
npm install @playstack/core @playstack/errors @playstack/nest-errors @nestjs/common @nestjs/core reflect-metadata rxjs
```

## Compose it in the application module

```ts
import { Module } from '@nestjs/common'
import {
  PlaystackErrorsFilter,
  PlaystackErrorsModule,
} from '@playstack/nest-errors'

@Module({
  imports: [
    PlaystackErrorsModule.forRoot({
      errors,
      captureHttpExceptions: false,
      resolveCaptureOptions(exception, host) {
        const request = host.switchToHttp().getRequest<AuthenticatedRequest>()
        return {
          accountId: request.account?.id,
          userId: request.user?.id,
          tags: { route: request.route?.path ?? 'unknown' },
        }
      },
    }),
  ],
})
export class AppModule {}

const app = await NestFactory.create(AppModule)
app.useGlobalFilters(app.get(PlaystackErrorsFilter))
```

The module exports the filter instead of installing an `APP_FILTER` implicitly. Apply it globally, to a controller, or to an individual route. Nest’s original HTTP response still passes through `BaseExceptionFilter` after capture.

## Configuration reference

| `NestErrorsOptions` property | Type | Required | Purpose |
| --- | --- | --- | --- |
| `errors` | `ErrorReporter` | Yes | Existing application reporter. |
| `captureHttpExceptions` | `boolean` | No | Include expected HTTP responses below 500; defaults to `false`. |
| `resolveCaptureOptions` | `(exception, host) => options \| false \| undefined` | No | Add request-derived metadata, suppress capture with `false`, or use defaults with `undefined`. |

Unknown exceptions and HTTP responses of 500 or greater are captured by default. Default metadata includes framework, transport type, status code, and an appropriate warning or error severity.

For configuration that depends on other Nest providers, use `PlaystackErrorsModule.forRootAsync({ imports, inject, useFactory })`.

## Injection bridge

Inject `PLAYSTACK_ERROR_REPORTER` to receive the exact configured reporter in another provider:

```ts
@Injectable()
class JobRunner {
  constructor(
    @Inject(PLAYSTACK_ERROR_REPORTER)
    private readonly errors: ErrorReporter,
  ) {}
}
```

`NEST_ERRORS_OPTIONS`, `PLAYSTACK_ERROR_REPORTER`, and `PlaystackErrorsFilter` are exported from the dynamic module.

## Boundary

The adapter never reads request bodies, headers, cookies, or URLs automatically. The application decides which opaque identity and route metadata are safe to attach. It also owns reporting drivers, vendor SDK lifecycle, filter placement, and every Nest peer dependency.

## Host-owned responses

A response-free capture helper and pure exception mapping allow the host to keep its HTTP envelope. Capture must not double-send or turn expected domain/tenancy errors into 500s. Rate-limit custom bodies retain library-owned status and headers. Use application integration tests for sanitized unknown errors, expected 4xx responses and reporter failure.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-errors@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-errors` | `./dist/index.d.ts` |
| `@playstack/nest-errors/errors` | `./dist/errors.d.ts` |
| `@playstack/nest-errors/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/errors` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-feeds

Source: https://playstack.dev/docs/packages/content-intelligence/nest-feeds.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-feeds` connects feed regeneration definitions to an application-owned queue and scheduler without selecting BullMQ, cron, or another provider.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-feeds@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the module

```ts
const generationQueue = new NestFeedGenerationQueue({
  dispatch: (name, payload, { jobId }) => jobs.add(name, payload, { jobId }),
})

PlaystackFeedsModule.forRoot({
  queue: generationQueue,
  namespace: 'marketing',
  definitions: [
    {
      id: 'public',
      generate: (job) => generatePublicDocuments(job.reason),
    },
  ],
  scheduledDefinitionIds: ['public'],
  onScheduleError: (error, definitionId) => report(error, definitionId),
})
```

## Configuration reference

| `NestFeedsOptions` property | Required | Purpose |
| --- | --- | --- |
| `queue` | Yes | Core `FeedGenerationQueue` bridge. |
| `namespace` | Yes | Qualifies deterministic regeneration job IDs. |
| `definitions` | Yes | Unique canonical IDs plus `generate(job)` implementations. |
| `scheduledDefinitionIds` | No | Registered definitions requested on each scheduler tick. |
| `onScheduleError` | No | Isolated per-definition scheduling failure observer. |

Configuration is validated and frozen at module creation. Use `forRootAsync({ imports, inject, useFactory })` for DI-resolved setup.

## Regeneration and worker bridges

Inject `PlaystackFeedRegenerationService` and call `request(definitionId, reason)` for `content-change`, `manual`, or `scheduled` work. All reasons use the same `feeds:<namespace>:<definitionId>` job ID so the queue coalesces overlap.

The queue client bridge dispatches the fixed `playstack.feeds.generate` job. Bind it to `PlaystackFeedGenerationConsumer.handle(job.data)` in the worker; recovered payloads are validated and may invoke only a registered definition.

Call `PlaystackFeedScheduler.tick()` from the application scheduler. Ticks never overlap; one definition’s failure does not block later requests.

## Boundary

The package installs no scheduler or queue framework. Applications own cron cadence, broker lifecycle, queue processor registration, and storage; the core feed package owns generation behavior.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-feeds@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-feeds` | `./dist/index.d.ts` |
| `@playstack/nest-feeds/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/feeds` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/notifications-react

Source: https://playstack.dev/docs/packages/communication/notifications-react.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/notifications-react` supplies headless React state for an authenticated in-app inbox. It does not install authentication, routes, fetch globals, realtime connections, or visual styling.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/notifications-react@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose an inbox

```tsx
'use client'

import {
  createNotificationsInboxCache,
  PlaystackNotificationsProvider,
  useNotificationsInbox,
} from '@playstack/notifications-react'

const cache = createNotificationsInboxCache()

function Inbox() {
  const inbox = useNotificationsInbox()

  return inbox.notifications.map((notification) => (
    <button
      key={notification.id}
      onClick={() => inbox.markRead(notification.id)}
    >
      {notification.title}
    </button>
  ))
}

export function InboxBoundary() {
  return (
    <PlaystackNotificationsProvider client={inboxClient} cache={cache}>
      <Inbox />
    </PlaystackNotificationsProvider>
  )
}
```

The package root is a Client Component entry point and can be mounted below a Next.js App Router Server Component boundary.

## Provider configuration

| Property | Required | Purpose |
| --- | --- | --- |
| `client` | Yes | Application-owned authenticated list, read, and archive client. |
| `cache` | Yes | Explicit external-store-compatible cache boundary. |
| `cacheKey` | No | Separates inbox state when the application has multiple subjects or roots. |
| `pageSize` | No | Number of rows requested per page. |
| `includeArchived` | No | Includes archived rows in list requests. |
| `now` | No | Application-controlled time for optimistic read state. |
| `onError` | No | Sends original client errors to caller-owned observability. |

`useNotificationsInbox()` exposes notifications, unread count, pagination state, `refresh`, `loadMore`, `markRead`, and `archive`. Mutations update optimistically and roll back on rejection; pagination de-duplicates rows by ID. `NotificationsInbox` offers the same state as a render-prop component.

`client.client` preserves the underlying HTTP or query client as the provider escape hatch.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/notifications-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/notifications-react` | `./dist/index.d.ts` |
| `@playstack/notifications-react/errors` | `./dist/errors.d.ts` |
| `@playstack/notifications-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/notifications` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/queues

Source: https://playstack.dev/docs/packages/infrastructure/queues.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/queues` defines typed background jobs with enforced application/environment namespaces, runtime payload validation, retry policy, failure hooks, idempotent IDs, and W3C trace propagation.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/queues@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Define and dispatch a job

```ts
import { QueueJob, createQueue } from '@playstack/queues'

class SendWelcomeEmail extends QueueJob<{ userId: string }, void> {
  static readonly jobName = 'emails.send-welcome'
  static readonly defaults = {
    attempts: 5,
    backoff: { type: 'exponential', delay: 1_000 } as const,
  }

  static parse(value: unknown) {
    return welcomePayloadSchema.parse(value)
  }

  async handle({ userId }: { userId: string }) {
    await welcomeMail.send(userId)
  }

  async failed(payload, error, context) {
    await operations.record({ payload, error, context })
  }
}

const emails = createQueue('emails', {
  app: 'billing',
  environment: 'production',
  driver,
  trace: activeTraceProvider,
})

await emails.dispatch(SendWelcomeEmail, { userId }, { jobId: `welcome-${userId}` })
```

Where custom job IDs are supported (for example BullMQ), they are queue-scoped, URL-safe, non-numeric, and contain no colons. Dispatch can report `queued` or `duplicate`; do not infer deduplication for providers that reject custom IDs. Omit `jobId` from the example for Inngest and Cloudflare.

## Queue and dispatch properties

| Property              | Purpose                                                              |
| --------------------- | -------------------------------------------------------------------- |
| Queue `name`          | Logical queue segment.                                               |
| `app` / `environment` | Produce the fixed `{app}:{environment}:{queue}` namespace.           |
| `driver`              | Runtime-neutral enqueue bridge.                                      |
| `trace`               | Optional provider capturing current W3C context.                     |
| Dispatch `jobId`      | Stable idempotency key.                                              |
| Dispatch `trace`      | Explicit context, inherited provider context, or `null` to suppress. |

Payload validation runs before enqueue and again at the worker boundary. Trace metadata stays under envelope `_trace` and is never merged into the typed payload.

## Process jobs

Native scheduled jobs expose a frozen
`context.occurrence: { id, scheduleId, scheduledAt }`. The epoch-millisecond slot
and ID survive retries even when a worker starts late. Different ticks and fresh
schedule-template generations have distinct IDs. Ordinary delayed dispatches
have no occurrence. Use it to identify scheduled work; retain application
idempotency and native checkpoint/receipt ownership checks.

`queue.control.schedule` prepares a fresh template generation and a
`metadata._schedule: { id }` declaration. Supported adapters attach actual runtime
occurrence facts; the processor rejects missing/mismatched evidence. It never
infers scheduled time from worker wall time or retry delay. Custom schedulers use
`createQueueScheduledOccurrence(queueQualifiedName, templateDispatchId,
scheduleId, scheduledAt)` and pass it in `QueueDeliveryRuntime.occurrence`.
Custom dispatch-ID factories must remain unique across upserts as well as jobs.

Memory supports interval materialization; BullMQ supports its declared native
schedules. Their catch-up semantics remain distinct.
An upsert can retain older queued generations without relabeling or canceling
their occurrences.
BullMQ 5.16.0 can also keep producing the old template after a same-ID upsert;
use explicit removal/recreation on that profile and reconcile retained jobs.
The adapter does not silently reset cadence or rewrite queued authority.
Cloudflare and the current Inngest event adapter refuse schedule templates;
explicit dated product payloads remain supported. Prior preview schedules require
explicit retirement/recreation and retained-work reconciliation; no automatic
template rewrite is performed.

```ts
const processor = createQueueProcessor({
  app: 'billing',
  environment: 'production',
  queue: 'emails',
  jobs: [registerJob(SendWelcomeEmail, () => new SendWelcomeEmail(dependencies))],
  trace: traceRunner,
  onFailureHookError: (error) => errors.captureError(error),
})
```

| Processor property            | Purpose                                                   |
| ----------------------------- | --------------------------------------------------------- |
| `app`, `environment`, `queue` | Rejects deliveries reaching the wrong worker.             |
| `jobs`                        | Unique definitions plus explicit dependency factories.    |
| `trace`                       | Restores W3C metadata around handling.                    |
| `onFailureHookError`          | Isolates errors from the one-time exhausted-failure hook. |

## Report progress and control jobs

Handlers receive a portable progress reporter. It accepts either a percentage or structured `{ completed, total, stage }` data and fails explicitly when a provider cannot persist progress.

```ts
async handle(payload, context) {
  await context.progress.update({
    completed: 28,
    total: 61,
    stage: 'converting',
  })
}
```

Drivers with a portable control plane expose progress subscriptions, interval and cron schedules, bounded job listing, counts, and inactive-job cancellation through `queue.control`. Cancellation reports active and terminal jobs honestly; it does not claim to interrupt an already executing handler.

## BullMQ bridge

`createBullMqQueueDriver` and `createBullMqWorker` accept app, environment, queue, BullMQ connection/options, registered jobs, and worker tuning. The Redis prefix is `{app}:{environment}` and BullMQ queue name is the logical queue. Both expose the exact native Queue or Worker for Queuebert, metrics, pause/resume, and shutdown.

BullMQ `>=5.16.0 <7` is supported. Installation planning validates the application's declared range against that peer contract rather than accepting any existing package with the same name.

`@playstack/nest-queues` adds named queue injection, `@QueueHandler()` discovery, optional worker startup, and application-shutdown handling. Provider-specific concurrency, locking, stalled-job policy, flows, priorities, and rate limiting remain visible BullMQ configuration.

## Inngest bridge

`createInngestQueueDriver` dispatches the portable envelope as a namespaced Inngest event. `createInngestQueueFunction` processes that event with the same registered jobs, payload validation, retry policy, failure hooks, and trace restoration used by other drivers.

```ts
import { Inngest } from 'inngest'
import { createQueue } from '@playstack/queues'
import { createInngestQueueDriver, createInngestQueueFunction } from '@playstack/queues/inngest'

export const inngest = new Inngest({ id: 'billing' })

const driver = createInngestQueueDriver({
  client: inngest,
  app: 'billing',
  environment: 'production',
  queue: 'emails',
})

export const emails = createQueue('emails', {
  app: 'billing',
  environment: 'production',
  driver,
})

export const emailJobs = createInngestQueueFunction({
  client: inngest,
  app: 'billing',
  environment: 'production',
  queue: 'emails',
  jobs: [registerJob(SendWelcomeEmail, () => new SendWelcomeEmail())],
  concurrency: 20,
  throttle: { limit: 100, period: '1m' },
})
```

Serve the generated function from a Next.js App Router endpoint:

```ts
import { serve } from 'inngest/next'
import { emailJobs, inngest } from '@/lib/queues'

export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [emailJobs],
})
```

NestJS applications using Express mount the same function through `inngest/express`. Inngest supports delayed jobs, up to 21 total attempts, fixed and exponential backoff, and native concurrency and throttling. It does not support portable custom `jobId`, durable progress, or the BullMQ control-plane semantics, so unsupported operations fail before provider I/O.

## Cloudflare Queues bridge

`@playstack/queues/cloudflare` exports `createCloudflareQueueDriver` and `createCloudflareQueueHandler`. Producers use a structural Worker binding; consumers use individual acknowledgements/retries and a required durable terminal-failure callback. The physical queue name is reviewed separately from the logical namespace. Worker integration templates ship with `playstack add queues --adapter=cloudflare --dry-run`; this adapter is not a Nest worker.

Cloudflare supports bounded JSON batches and delayed delivery, but not custom job IDs, progress, schedules, administrative queries or cancellation. At-least-once delivery still requires application idempotency. See [Cloudflare Queues](/integrations/cloudflare-queues) for payload limits, quarantine behavior and deployment notes.

## Testing and boundary

`@playstack/queues/testing` provides a deterministic memory driver. The core does not choose a broker, DI container, concurrency, shutdown order, or monitoring. Applications own Redis or Inngest client lifecycle and should queue only work with explicit retry and terminal-failure policy.

## Existing queues and strict readiness

BullMQ `>=5.16 <7` is supported; selected monitoring integrations retain their own peer restrictions. `assertBullMqRedisNoEviction(queue.client)` is a fail-closed startup/readiness check, distinct from the non-blocking dispatch observer. It fails if the policy is incompatible or cannot be verified.

The optional [Queuebert bridge](/integrations/queuebert) preserves reviewed native queues and processor statistics. Set a per-queue `expectedPrefix` for an existing physical namespace instead of renaming queues. The bridge does not start workers or authorize administrative HTTP. Monitoring compatibility is not payload compatibility: Playstack dispatch wraps data in an envelope, while legacy processors may read raw `job.data`. Coordinate producers, workers and schedulers through an explicit version/drain/cutover/rollback plan. Mixed-payload Redis fixtures cover selected BullMQ 5/6 paths, not an automatic consumer migration.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/queues@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/queues/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/queues/cloudflare` | `./dist/cloudflare.d.ts` |
| `@playstack/queues` | `./dist/index.d.ts` |
| `@playstack/queues/errors` | `./dist/errors.d.ts` |
| `@playstack/queues/types` | `./dist/types.d.ts` |
| `@playstack/queues/testing` | `./dist/testing.d.ts` |
| `@playstack/queues/bullmq` | `./dist/bullmq.d.ts` |
| `@playstack/queues/inngest` | `./dist/inngest.d.ts` |
| `@playstack/queues/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `bullmq` | `>=5.16.0 <7` | Optional; only for the entry points that use it. |
| `inngest` | `^4.18.1` | Optional; only for the entry points that use it. |
| `ioredis` | `>=5.0.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Accounts, registry tokens, and upgrades

Source: https://playstack.dev/docs/registry-access.md

Free is a package licensing classification; an account is a way to prepare registry access and manage subscriptions. Public npm remains the intended account-free channel for Free packages. Neither a Free badge nor a generated token proves that a particular package/version is published. Check [package availability](/docs/packages#access-policy) first.

## Prepare a Free account

When a dashboard is available for your preview environment, use the sign-in or create-account links on [Pricing](/pricing). If those links are absent, request preview access there instead; no public dashboard or checkout availability is implied.

1. Register and verify your email, then select the account that owns the project.
2. Open **Packages**. Verified account owners/admins can create an ordinary read-only registry token without purchasing a subscription.
3. Copy the secret when shown and store it in your development environment or CI secret store. Use the dashboard's registry URL and package-manager configuration for that environment.
4. Reference the token through an environment variable in your package-manager configuration. Never commit the literal token, include it in browser code, bake it into an image layer, or print it in build logs. Use separate revocable tokens for separate deployments.

An ordinary Free token can read assigned Free shared packages when published and not withdrawn. It grants no paid access, private package ownership, or access to unknown/pending classifications. Account suspension, membership policy, expiry, and revocation still apply.

## Upgrade the same account

The account owner opens **Subscription** and chooses an available offer. Existing subscribers manage their plan through the billing portal rather than starting a second checkout. Plus remains planned with no exclusive packages; only configured, enabled offers can be purchased.

Paid authorization is checked against the account's current entitlement and release eligibility, not permanently embedded in an ordinary token. The same token can gain access after upgrade; it need not be regenerated solely for a plan change. A checkout success redirect is not proof of access: the verified provider webhook and registry projection must complete. If the dashboard still shows pending or unavailable billing, do not repeat an uncertain purchase automatically.

Eligible paid releases remain installable after a subscription ends under the release policy; newer paid releases require renewed eligibility. Withdrawal, suspension, and revoked credentials still deny access. A separately approved operator deployment-token exception is not an ordinary Free account benefit.

## Personal email preferences

**Account Settings → Your email preferences** records your optional choice to receive product updates and occasional early-access invitations. It is personal across your accounts and off by default. Registration, token creation, and purchase do not opt you in or grant special package access. This preference is separate from the site's pricing/release list and from required account-security messages; recording a choice does not itself activate marketing delivery.

Read the [privacy note](/privacy) for the service's data categories. For the reusable package mechanics rather than this dashboard's product policy, see [Registry](/docs/packages/infrastructure/registry) and [API keys](/docs/packages/identity/api-keys).

## Communication

Source: https://playstack.dev/docs/packages/communication.md

Communication packages separate who should receive a message, what the message means, how it leaves the application, and how provider callbacks return. Applications can adopt each boundary independently or compose the complete path.

## Audiences

| Package                                                                        | Plan                       | Responsibility                                                                 |
| ------------------------------------------------------------------------------ | -------------------------- | ------------------------------------------------------------------------------ |
| [`@playstack/audiences`](/docs/packages/communication/audiences)               | <PlanTierDot tier="pro" /> | Lists, consent evidence, subscriptions, segments, and signed preference flows. |
| [`@playstack/audiences-react`](/docs/packages/communication/audiences-react)   | <PlanTierDot tier="pro" /> | Headless subscribe and preference-center state for React.                      |
| [`@playstack/nest-audiences`](/docs/packages/communication/nest-audiences)     | <PlanTierDot tier="pro" /> | Guarded public and management routes plus remote-list workers for NestJS.      |
| [`@playstack/audiences-resend`](/docs/packages/communication/audiences-resend) | <PlanTierDot tier="pro" /> | Resend Contacts and Segments remote authority.                                 |

## Notifications and delivery

| Package                                                                              | Plan                        | Responsibility                                                     |
| ------------------------------------------------------------------------------------ | --------------------------- | ------------------------------------------------------------------ |
| [`@playstack/notifications`](/docs/packages/communication/notifications)             | <PlanTierDot tier="free" /> | Typed user-directed notifications across mail and an in-app inbox. |
| [`@playstack/notifications-react`](/docs/packages/communication/notifications-react) | <PlanTierDot tier="pro" />  | Headless React inbox state and optimistic mutations.               |
| [`@playstack/nest-notifications`](/docs/packages/communication/nest-notifications)   | <PlanTierDot tier="pro" />  | NestJS notification queue-consumer binding.                        |
| [`@playstack/delivery`](/docs/packages/communication/delivery)                       | <PlanTierDot tier="free" /> | Queued provider dispatch and shared suppression policy.            |
| [`@playstack/delivery-history`](/docs/packages/communication/delivery-history)       | <PlanTierDot tier="pro" />  | Durable provider-neutral delivery timelines.                       |
| [`@playstack/nest-delivery`](/docs/packages/communication/nest-delivery)             | <PlanTierDot tier="free" /> | NestJS delivery worker and unsubscribe endpoints.                  |
| [`@playstack/webhooks`](/docs/packages/communication/webhooks)                       | <PlanTierDot tier="free" /> | Verified, idempotent Stripe, GitHub, and custom webhook receipt.   |

## Composition rule

Audiences resolves recipients but does not compose campaigns. Notifications resolves user preferences and renders typed messages. Delivery applies suppression and selects a provider slot. Webhooks verify provider feedback at the application edge. Keeping these responsibilities separate prevents a mail vendor or framework from becoming part of the product contract.

## Ejection and merging

Source: https://playstack.dev/docs/cli/ejection-merging.md

Ejection is the final customization option after configuration and replacing a runtime seam. It moves one eligible source artifact from package ownership to application ownership without disconnecting it from future upstream review.

## Before ejecting

Use the smallest change that satisfies the application:

1. Configure the package through its public options.
2. Replace an explicit bridge or adapter seam.
3. Eject only when neither boundary can represent the required behavior.

Security-critical artifacts cannot be ejected. Signature verification, token validation, and similar package guarantees must remain package-managed so fixes arrive through version updates.

## Select an artifact

```sh
npx playstack eject @playstack/example --target=example-source --dry-run
npx playstack eject @playstack/example --target=example-source
```

The package manifest must declare the artifact as ejectable. The CLI verifies the published hash, writes the application-owned target, saves the installed upstream base beneath `.playstack/base`, and records the relationship in `.playstack/ejected.json`.

After ejection, edit the application target normally. Do not edit the saved base; it exists solely to support deterministic three-way merges.

## Reconcile an upstream change

```sh
npx playstack merge example-source
npx playstack merge --all
```

A merge compares three inputs:

| Input              | Ownership                                                                       |
| ------------------ | ------------------------------------------------------------------------------- |
| Saved base         | The exact upstream source present when the artifact was ejected or last merged. |
| Application target | The repository's current, application-owned implementation.                     |
| New upstream       | The source shipped by the newly installed package version.                      |

A clean merge updates the application target and advances the saved base. An unchanged upstream produces no write.

## Conflict behavior

Conflicts never overwrite the application target with unresolved markers. The CLI writes a conflict report under `.playstack/conflicts` and returns the unsafe-conflict exit code so a human can review the competing changes.

When merging more than two ejected artifacts with `--all`, the CLI defaults to a dry run to avoid applying a large unreviewed ownership change at once.

## Updates and security

`playstack doctor --check` reports when an ejected artifact's upstream hash has changed. Security-tagged upstream changes are called out distinctly.

`playstack update` attempts safe three-way merges after updating installed packages. In pull-request mode, package and managed changes form the primary branch, clean ejected merges form a dependent review branch, and each conflict receives a separate draft branch. Conflict markers are never committed.

Read [Updates and CI](/docs/cli/updates-ci) before automating this workflow.

## Packages

Source: https://playstack.dev/docs/packages.md

Playstack is organized by product capability. Start with the problem you are solving, then select only the portable domain packages and application-edge adapters you need.

Every package has its own page with installation guidance, examples, and an API entry-point and peer-requirement table generated from its package manifest. The prose explains the selected options and the responsibilities left to your application; it is not an exhaustive reference for every exported symbol. See [compatibility and support](/docs/compatibility) for runtime and framework guidance.

## Release availability

This documentation snapshot describes the upcoming **`0.1.0-beta.4`** train.
The [quickstart](/docs/getting-started) targets the same train.
Confirm the exact approved registry version
and integrity before installation; keep the whole Playstack graph on one version.
Same-version Yalc updates are local development builds, not immutable registry
releases or evidence that a fresh CI builder can install successfully.

See the [beta 4 change summary](/changelog/beta-4-coordinated-package-train)
for current release guidance and the retained
[beta 3 snapshot](/changelog/beta-3-adoption-and-operator-controls) for supplied
capabilities and remaining activation gates.
Start with the current [adoption and custom-adapter changes](/changelog/beta-4-coordinated-package-train#adoption),
then retain the [beta 3 upgrade checks](/changelog/beta-3-adoption-and-operator-controls#upgrade-checklist)
when refreshing an existing beta 2 install or beta 3 local snapshot.

## Feature groups

| Group                                                           | Packages | Includes                                                                                                  |
| --------------------------------------------------------------- | -------: | --------------------------------------------------------------------------------------------------------- |
| [Foundation](/docs/packages/foundation)                         |       13 | Core contracts, errors, validation, cryptography, localization, tooling, and runtime integration support. |
| [Identity and access](/docs/packages/identity)                  |       29 | Accounts, authentication, billing, licensing, repository access, devices, and MFA.                        |
| [Commerce](/docs/packages/commerce)                             |       10 | Catalog, one-time payments, inventory, orders, fulfillment, and NestJS bindings.                          |
| [Events and operations](/docs/packages/events-operations)       |       15 | Events, event storage, analytics, rate limiting, and their production adapters.                           |
| [Communication](/docs/packages/communication)                   |       11 | Audiences, notifications, outbound delivery, durable history, and inbound webhooks.                       |
| [Infrastructure and delivery](/docs/packages/infrastructure)    |       13 | Cache, queues, storage, local-first synchronization, and their framework edges.                           |
| [Content and intelligence](/docs/packages/content-intelligence) |       10 | Content, feeds, search, AI operations, and framework integrations.                                        |
| [Application UI](/docs/packages/ui)                             |        3 | Chakra themes, controlled app compositions, account and audience operator sections.                       |

## Access policy

Playstack is in preview. Tier labels describe licensing and intended access, not proof that a particular version is published or registry access is active. [Request preview access](/pricing) if you do not yet have installation instructions. You can read the guides and choose packages before obtaining access.

- <PlanTierDot tier="free" /> **Free** packages are MIT licensed, with public npm as the intended account-free distribution channel. They cover authentication, API keys, subscription billing, mail, queues, storage, cache, search, events, localization, validation, rate limiting and their framework bindings.
- <PlanTierDot tier="pro" /> **Pro** includes every paid package: team accounts and seats, commerce, licensing, the private registry, audiences, analytics, local-first sync and the application UI compositions.
- <PlanTierDot tier="plus" /> **Plus** is a planned middle tier. No package is exclusive to it yet.

Paid packages use the Playstack Pro License, which permits closed-source applications. Once access is enabled for your account, use a dashboard-issued registry token with `https://registry.playstack.dev/`. Never use a browser session token as a registry credential. Under the subscription policy, releases published while your subscription is active remain eligible after it ends; newer releases require renewed access. Packages marked **Paid · tier to be announced** grant no access until assigned.

See [Getting started](/docs/getting-started) for separate `.npmrc` and terminal instructions. For installation errors, check credentials, package access and the exact published version before replacing tokens.

Browse the [Free](/docs/free), [Paid](/docs/paid), [Plus](/docs/plus) and [Pro](/docs/pro) catalogs, or [request pricing access](/pricing).

## Reading the reference

Package pages combine examples with configuration and behavior notes. They are not a substitute for the exact TypeScript declarations shipped with your installed version. Use your editor's **Go to Definition** on an imported function or type to inspect its arguments, return value and overloads. Public subpath exports select optional runtime, provider and testing surfaces; avoid imports from private `src` or `dist` paths.

Examples labeled **composition example** assume the application-owned services named above them already exist. Those names are not additional Playstack exports. If you want a complete program first, run the [storage quickstart](/docs/getting-started#your-first-working-example).

## Package boundaries

Domain packages do not choose your web framework or persistence technology. Their commands accept explicit dependencies and optional operation context, and their event seams can be connected to the rest of the application.

Framework and infrastructure adapters belong at the application edge. They expose the underlying clients and lifecycle hooks needed for connection reuse, transactions, shutdown, observability, and test doubles.

Package scopes remain stable when a package moves between Free, Plus, and Pro. Playstack also uses fixed versioning across every tier, so a dependency graph containing two Playstack versions should be treated as a configuration error.

## Workers

Source: https://playstack.dev/docs/frameworks/workers.md

Many Playstack domain packages target Web platform primitives so they can run in Node.js and Workers without separate business logic. Worker compatibility is tested per package; Node framework adapters and provider SDK assumptions remain outside the Worker bundle.

[Start with the Workers guide](/docs/frameworks/workers/getting-started) to compose Web Crypto and a native streaming response inside a fetch handler.

## Good Worker starting points

| Capability                                          | Package                                                                                                      | Worker boundary                                                                                          |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| Shared errors, results, time, and operation context | [`@playstack/core`](/docs/packages/foundation/core)                                                          | Dependency-free contracts.                                                                               |
| Encryption, digests, signatures, and tokens         | [`@playstack/crypto`](/docs/packages/foundation/crypto)                                                      | Injects `globalThis.crypto`; key configuration remains application-owned.                                |
| Validation                                          | [`@playstack/validation`](/docs/packages/foundation/validation)                                              | Portable schemas and HTTP validation errors.                                                             |
| Domain events                                       | [`@playstack/events`](/docs/packages/events-operations/events)                                               | In-process bridge with application-owned durable delivery.                                               |
| Authentication and accounts                         | [`@playstack/auth`](/docs/packages/identity/auth), [`@playstack/accounts`](/docs/packages/identity/accounts) | Portable services with injected persistence and request handling.                                        |
| Rate limiting                                       | [`@playstack/rate-limit`](/docs/packages/events-operations/rate-limit)                                       | Atomic store contract with a [`Durable Object adapter`](/docs/packages/events-operations/rate-limit-do). |
| Feeds and sitemaps                                  | [`@playstack/feeds`](/docs/packages/content-intelligence/feeds)                                              | Native streams, `Response`, and structural R2 storage.                                                   |
| Audit                                               | [`@playstack/audit`](/docs/packages/identity/audit)                                                          | Append-only chains with application-owned storage.                                                       |

Check each package reference before importing infrastructure or framework adapters. A portable core package may be Worker-compatible while one of its database or NestJS bindings is intentionally Node-only.

## Runtime rules

- Prefer Web `Request`, `Response`, `ReadableStream`, and `crypto.subtle` at the boundary.
- Construct services once at module scope when their dependencies are safe to reuse across requests.
- Derive request-specific operation context inside `fetch` and pass it into domain commands.
- Keep secrets in Worker bindings and parse them into narrow package configuration at composition time.
- Use Durable Objects, queues, R2, or another service through explicit structural adapters.
- Use `ctx.waitUntil()` only for observational work that is safe outside the response transaction.

## Deployment boundaries stay visible

Playstack does not hide Cloudflare bindings behind ambient globals. The Worker entry receives `env` and decides which binding satisfies each package contract. Native clients or bindings remain exposed where an application needs provider-specific capabilities.

## Cloudflare queue bindings

Use `@playstack/queues/cloudflare` for typed producers and Worker batch handlers. [Cloudflare Queues](/integrations/cloudflare-queues) documents individual acknowledgements, bounded JSON batches, durable quarantine and explicit unsupported operations. It does not replace arbitrary ingestion payloads automatically or install a Nest worker.

[Localization](/docs/packages/foundation/i18n) also runs through the portable policy/catalog boundary; choose an ICU runtime compatible with the selected Worker deployment.

## @playstack/accounts

Source: https://playstack.dev/docs/packages/identity/accounts.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/accounts` models the tenant a user is acting within, the membership that permits that scope, and the invitation and ownership lifecycle around it.

## Install

```sh
npm install @playstack/accounts @playstack/auth @playstack/core @playstack/events
```

## Compose the account service

```ts
import { createAccountsService } from '@playstack/accounts'

export const accounts = createAccountsService({
  persistence,
  users: {
    assertActive: (userId, transaction) => userDirectory.assertActive(userId, transaction),
    emailForUser: (userId, transaction) => userDirectory.emailForUser(userId, transaction),
  },
  crypto: applicationCrypto,
  delivery: accountInviteDelivery,
  ownershipTransferDelivery,
  events,
  clock,
  ids,
  invitationTtlMs: 24 * 60 * 60_000,
  ownershipTransferTtlMs: 24 * 60 * 60_000,
  deletionGraceMs: 30 * 24 * 60 * 60_000,
  deletionCascades: [removeProductWorkspace],
})

const resolution = await accounts.resolve(session.userId, session.accountId)
```

## Configuration reference

| `AccountsServiceOptions` property | Required | Purpose |
| --- | --- | --- |
| `persistence` | Yes | Transactional account, member, and invitation storage. |
| `users` | Yes | Validates active users and resolves invitation email addresses. |
| `crypto` | Yes | Generates and hashes opaque invitation tokens. |
| `delivery` | Yes | Delivers the one-time invitation secret. |
| `ownershipTransferDelivery` | Yes | Delivers the separately scoped ownership-transfer bearer. |
| `events` | Yes | Emits typed membership and account lifecycle events. |
| `clock`, `ids` | Yes | Application-owned time and identifiers. |
| `invitationTtlMs` | No | Positive invitation lifetime; defaults to 30 minutes. |
| `ownershipTransferTtlMs` | No | Positive proposal lifetime; defaults to 24 hours. |
| `deletionGraceMs` | No | Recoverable deletion window; defaults to 30 days. |
| `deletionCascades` | No | Uniquely named same-database purge hooks. |
| `ownership` | No | `single` by default; `multi` permits multiple owner memberships. |
| `administration` | No | `owners-and-admins` by default; `owners-only` restricts administration. |
| `mutationPolicy` | No | Transaction-aware restrictions for product-owned lifecycle rules. |
| `deletionNotifications` | No | Disabled-by-default trusted lifecycle-recipient authority with exact purpose allowlist and stored-intent authorization. |

`AccountRole` includes `owner`, `admin`, and `member` while allowing application-defined role strings. Domain authorization remains application policy.

## Bridge contracts

| Contract | Key members |
| --- | --- |
| `AccountsPersistence` | Transaction, account lookup/save, member lookup/list/save/delete, and replay-safe invitation lookup/save. |
| `AccountsUserDirectory` | `assertActive(userId)` and `emailForUser(userId)`. |
| `AccountsCrypto` | `randomToken` and `digestSha256`; compatible with `@playstack/crypto`. |
| `AccountInviteDelivery` | `deliver({ invitationId, accountId, email, role, token, expiresAt })`. |
| `AccountSessionStrategy` | `switchAccount(token, accountId, context?)`; implemented by database auth sessions. |

An account switch validates current membership, creates a new account-scoped session, and revokes the old session through the supplied strategy. Register auth’s account-membership-removal handler for `accounts.member.removed` when both persistence adapters share a transaction kind.

## Management and ownership lifecycle

```ts
const members = await accounts.listMembers(accountId, actingUserId)
const invitations = await accounts.listInvitations(accountId, actingUserId)

await accounts.updateAccount(accountId, { name: 'Acme Studio' }, actingUserId)
await accounts.leaveAccount(accountId, actingUserId)

const proposal = await accounts.requestOwnershipTransfer(
  accountId,
  nextOwnerId,
  currentOwnerId,
)
await accounts.acceptOwnershipTransfer(deliveredToken, nextOwnerId)
```

The target must already be a member. A proposal is single-use, owner-only to
request or cancel, and expires after 24 hours by default. Acceptance updates
the owner pointer and both membership roles atomically. The current owner
cannot leave or be removed through generic membership operations in the default
single-owner mode.

### Compose lifecycle changes atomically

Account updates, leave, role changes, member removal, invitation acceptance and
revocation, transfer acceptance and cancellation, and deletion request and restore
accept a final optional transaction after operation context. Purge accepts
`purgeDeletedAccounts(options, transaction?)`. Account, member and invitation
lists and pending-transfer reads can use the caller's transaction too.

The service joins that physical handle for directory checks, policy, writes,
transactional events and purge hooks. It does not open a nested transaction.
Invitation acceptance reads current contact inside that transaction; restoration
rechecks the purge deadline after policy work. Abort the outer transaction on
failure and expose success only after commit. Native Prisma refuses obvious root
clients, and memory requires a live unit of work; custom adapters and event/session
handlers still need rollback qualification on the actual shared database handle.

Mutations that issue no bearer can join caller transactions even when the
service uses ordinary post-commit delivery ports. Full service construction still
requires configured delivery ports. Invitation and transfer issuance retain their
transactional encrypted-intent requirements when joining a caller transaction. `switchAccount` remains a separate session-strategy
call with no caller-transaction parameter. Product account policy and route
adoption remain application-owned.

### Optional multiple owners

Set `ownership: 'multi'` to give every owner membership equal owner authority.
`Account.ownerId` remains a primary-owner pointer, not the only administrator.
Account-locked role changes/removal/leave protect the last owner; if the primary
leaves, the remaining owner with the lexicographically smallest user ID becomes
primary in the same transaction. Required ownership and membership events must
commit with the change.

Owners can invite/promote owners; admins cannot grant or revoke ownership. A
transfer moves the requesting owner's role to a non-owner member, preserving
other owners. Personal-account membership restrictions remain application policy,
checked on direct mutations and invitation acceptance. Conversion to a team must
be explicit and audited, not inferred from an invitation.

### Resolve within a protected write

`resolve(userId, accountId, { transaction, lock: 'update' })` joins the caller's
transaction and requests an authorization lock. Resolution invokes the configured
`users.assertActive` with the transaction as well as checking account and
membership state. Pass the actual shared handle through user-directory and
persistence ports; do not open independent transactions for protected writes.
Without options, resolution retains its own transaction boundary.

### Trusted account-closure reminders

`requestAccountDeletion` returns the original account, actor, opaque deletion
generation and deadlines; retries preserve the same generation. Restore clears
it, and a later deletion gets a fresh ID even at the same timestamp. Native
`accounts.account.deleted` carries the generation so a required transactional
consumer can record an identifier-only immutable reminder intent.

`resolveAccountDeletionRecipient(intent, transaction)` is a narrow worker-only
composition, disabled unless `deletionNotifications` supplies an exact purpose
allowlist and verifies every field against trusted stored intent on that handle.
It rechecks the original actor's current activity/ownership, deletion generation,
current contact and expiry without permitting ordinary deleted-account resolution.
Never expose it as an account/user-ID email endpoint, store addresses in jobs, or
treat the intent ID as authentication. The directory ports must propagate the
original physical handle and use locks compatible with deactivation/contact changes.

Migrate the packaged nullable `Account.deletionId` and `Account.deletedBy` columns
before deploying the updated native Prisma adapter; update application-owned
persistence mappings too. No automatic backfill or application migration is
performed. This read is not a delivery grant: join required local handoff/evidence
to the caller transaction, await its commit, and use native Notifications/Delivery/
Mail with current suppression and uncertain-outcome handling. Purge-owned intents
and contact-bearing delivery work must be removed by the account lifecycle; no
contact snapshot may survive purge. Provider sends occur outside transactions,
so restore/deactivation after commit can still race an external send. Actual
day-one/day-six scheduling, templates, erasure and provider qualification remain
host integration gates.

## Registration and recoverable deletion

Register `accounts.userRegistrationHandler()` on the transactional
`auth.user.registered` event when auth and accounts share a transaction kind.
The handler creates the account and owner membership before the new auth user
commits; returning `null` deliberately skips provisioning.

`requestAccountDeletion()` hides an account immediately and returns its purge
deadline. `cancelAccountDeletion()` restores it during the grace period.
`purgeDeletedAccounts({ limit })` locks due accounts, invokes the configured
same-database cascades on the shared transaction, and removes package-owned
rows. Billing cancellation and cross-database erasure consume the lifecycle
events as application workflows; Playstack never starts a scheduler.

## Fail-closed Prisma scoping

```ts
import { withAccountScope } from '@playstack/accounts'

const db = withAccountScope(rootPrisma, resolution.accountId, {
  tenantModels: ['project', 'document'],
  nonTenantModels: ['country'],
})

const projects = await db.project.findMany({ where: { archived: false } })
```

| `AccountScopeOptions` property | Purpose |
| --- | --- |
| `tenantModels` | Allowlist of models that receive enforced `accountId` constraints. |
| `nonTenantModels` | Allowlist of explicitly safe global models. |

The scoped client denies unknown models, raw SQL, root transactions, unsupported operations, account-filter overrides, mismatched compound keys, and nested writes. Cross-tenant jobs must use the explicitly named root client instead of weakening the scoped client.

## Persistence and testing

`@playstack/accounts/prisma` exports `prismaAccountsPersistence(prisma)`. It serializes account mutations, uses advisory locks for slug allocation, locks invitation rows during acceptance, and normalizes dates. The managed schema fragment remains application-migrated. In-memory adapters are available from `@playstack/accounts/testing`.

## Boundary

Accounts depends on authentication but does not replace it. It does not own arbitrary resource authorization, billing policy, or cross-tenant administration. Accounts/team management and its bindings remain paid as an explicit policy exception; reconsideration for Free requires a later adoption decision. Authentication does not require Accounts.

## Restrict service policy without replacing the core

`administration: 'owners-only'` restricts membership administration; the default remains owners-and-admins. `mutationPolicy.assertAllowed(input, transaction)` can enforce product-owned personal-account rules before writes. Hooks restrict built-in permissions rather than grant extra authority, and must use the supplied transaction.

`slugPolicy` supports normalization and reserved-slug decisions. `createAccountPolicy()` provides named permissions for the Nest guard; service policy and resource authorization remain separate. Single-owner remains the default; choose `ownership: 'multi'` explicitly rather than trying to grant ownership through a restrictive hook. Personal-to-team conversion is never automatic.

An optional Nest `resolveRequestAccount` strategy supports user-wide sessions with current membership checks. A session already scoped to an account cannot select another; ordinary account switching retains its session-rotation behavior.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/accounts@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/accounts` | `./dist/index.d.ts` |
| `@playstack/accounts/errors` | `./dist/errors.d.ts` |
| `@playstack/accounts/prisma` | `./dist/prisma.d.ts` |
| `@playstack/accounts/testing` | `./dist/testing.d.ts` |
| `@playstack/accounts/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/accounts/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/ai

Source: https://playstack.dev/docs/packages/content-intelligence/ai.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/ai` supplies tenant-aware keys, entitlement and rate-limit preflight, idempotent usage metering, safe telemetry, and deterministic cache context around native Vercel AI SDK calls.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/ai@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose

```ts
import { createAi, type AiTokenUsage } from '@playstack/ai'
import { generateText } from 'ai'

const ai = createAi({
  persistence,
  events,
  connections,
  rateLimiter,
  entitlements,
  connectionScope: 'account',
  allowPlatformFallback: false,
  providers: [{
    id: 'anthropic',
    createModel: (modelId, { apiKey }) => anthropic(modelId, { apiKey }),
  }],
  prices: {
    'anthropic:claude-sonnet-4-6': {
      inputMicrosPerMillionTokens: configuredInputPrice,
      outputMicrosPerMillionTokens: configuredOutputPrice,
      currency: 'USD',
    },
  },
  clock,
  ids,
})

function usageEvidence(value: { inputTokens?: number; outputTokens?: number; totalTokens?: number }): AiTokenUsage {
  const { inputTokens, outputTokens, totalTokens } = value;
  const details = totalTokens === undefined ? undefined : { providerReportedTotalTokens: totalTokens };
  if (inputTokens !== undefined && outputTokens !== undefined)
    return { state: 'known', promptTokens: inputTokens, completionTokens: outputTokens, ...(details ? { details } : {}) };
  if (inputTokens !== undefined) return { state: 'partial', promptTokens: inputTokens, ...(details ? { details } : {}) };
  if (outputTokens !== undefined) return { state: 'partial', completionTokens: outputTokens, ...(details ? { details } : {}) };
  if (details) return { state: 'partial', details };
  return { state: 'unknown' };
}

const call = await ai.beginCall('anthropic:claude-sonnet-4-6', { accountId, actor: { type: 'user', id: actorId } })
const result = await generateText({ model: call.model, prompt })
await ai.completeCall(call, {
  usage: usageEvidence(result.totalUsage),
})
```

`call.model` is the provider’s exact model object. Streaming, tools, structured output, and UI hooks remain native SDK APIs.
The model can retain credentials internally: never serialize or log calls/models.
Only validated accounting projections exclude those opaque objects.

## Core configuration

| `AiOptions` property | Required | Purpose |
| --- | --- | --- |
| `persistence` | Yes | Transactional idempotent usage records and summaries. |
| `events` | Yes | Emits `ai.call.completed` or `ai.call.failed` atomically with accounting; qualify audit consumers for both. |
| `connections` | Yes | Lists scoped BYO connections and decrypts selected access tokens. |
| `rateLimiter` | Yes | Fail-closed `ai.generate` account/user or `ai.generate.system` account/custom policy. |
| `entitlements` | Yes | Authorizes provider/model and may supply rate-limit overrides. |
| `providers` | Yes | Provider ID, native model factory, and optional platform key resolver. |
| `prices` | Yes | Model-ref input/output micros per million tokens and currency. |
| `connectionScope` | Yes | `user`, `accountMember`, or `account`. |
| `allowPlatformFallback` | No | Enables platform keys only when a provider supplies one. |
| `telemetry`, `errorReporter`, `scrubber` | No | Application-owned operational bridges. |
| `recordContent` | No | Includes scrubbed prompt/completion telemetry; defaults false. |
| `cache` | No | Explicit `{ enabled, hasher }`; disabled when absent. |
| `clock`, `ids` | Yes | Call timing and identifiers. |

## Provider and policy bridges

| Contract | Responsibility |
| --- | --- |
| `AiProvider` | `createModel(modelId, { apiKey })` and optional `platformApiKey()`. |
| `AiConnections` | Scoped `list` plus decrypted `accessToken(connectionId)`. |
| `AiEntitlements` | Authorize account/actor/provider/model and return optional bucket overrides. |
| `AiRateLimiter` | Consume the matching user/system named policy with account and user/custom subjects. |
| `AiUsagePersistence` | Transactional `record` with created/duplicate result and account/time summary. |
| `AiTelemetry` | Isolated started/completed/failed observations. |
| `AiErrorReporter` | Receives scrubbed failure reports. |
| `AiCacheHasher` | SHA-256 digest for normalized deterministic inputs. |

A present but unusable BYO connection never falls back. Platform fallback requires both the global opt-in and a provider resolver. Bypassed/open rate-limit decisions are rejected so inference stays fail-closed.

## Metering, telemetry, and caching

Attribution is `actor: { type: 'user' | 'system', id }`. System actors require
account-scoped keys and cannot supply `accountMemberId`; they use an independently
configured `ai.generate.system` policy with tagged custom subject
`JSON.stringify(['ai-system', actor.id])`. No synthetic user is created. The two
named policies have separate account buckets; shared budgets and actor/account
authorization remain explicit product composition.

`completeCall(call, { usage })` records success. `failCall(call, cause, { usage }?)`
records failure (unknown usage by default) and returns `{ error, usage }` after
commit. Model-construction failures also record failed/unknown receipts. Provider
errors are generic/nonretryable; no raw diagnostic code/message/stack is retained.
Observers run only after the first commit. Reporting failure must not be caught
as provider failure or used to rerun inference.

Usage is `known` with both base counts, `partial` with one base count or only
numeric details, or `unknown` with none. Details preserve cached/cache-write prompt,
reasoning and independent provider-reported total tokens. Subcategories are not
charged again. The receipt total is normalized prompt-plus-completion; preserve a
different provider total explicitly. Known usage uses the call's captured flat
price; incomplete total/cost remains null. This configured estimate is not a
provider invoice or a product billing decision.

Exact replay preserves the first receipt/timing and emits once. Changed attribution,
outcome, measurements, details or price under the same call ID refuses. Summaries
return outcome/coverage counts and per-metric
`{ observedTotal, reportedCalls, missingCalls, total }`; total is null when any
measurement is missing. Observed subtotals are not complete bills. Empty histories
are zero. There is no durable in-flight admission or automatic crash recovery;
unreported calls remain host reconciliation work.

Prompt/completion recording is off by default. When enabled, content is scrubbed before telemetry and credentials never enter receipts, errors, events, or traces. `cacheKey()` returns a key only when caching is enabled and temperature is zero; storage remains an application-selected concern.

## Persistence and boundary

`@playstack/ai/prisma` supplies structural PostgreSQL persistence and a managed, non-ejectable schema fragment. Applications own clients and migrations. The package does not wrap the AI SDK, store cache results, load keys, define subscription plans, or hide provider-native behavior.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/ai@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/ai` | `./dist/index.d.ts` |
| `@playstack/ai/errors` | `./dist/errors.d.ts` |
| `@playstack/ai/prisma` | `./dist/prisma.d.ts` |
| `@playstack/ai/testing` | `./dist/testing.d.ts` |
| `@playstack/ai/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/ai/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/connections` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/rate-limit` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/commerce

Source: https://playstack.dev/docs/packages/commerce/commerce.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/commerce` resolves server-authored offers, persists immutable order snapshots, optionally reserves inventory, creates hosted checkout after commit, and confirms orders only from verified payment projections.

```ts
import { createCommerce } from '@playstack/commerce'

const commerce = createCommerce({
  persistence,
  catalog,
  payments,
  inventory,
  entitlements,
  fulfillment,
  events,
  ids,
  orderNumbers,
  clock,
  allowedRedirectOrigins: ['https://shop.example.com'],
})
```

Payment and fulfillment are separate order axes. A redirect never confirms payment, provider-total drift quarantines an order for reconciliation, and checkout expiration releases inventory. Paid orders remain confirmed through later refunds and disputes.

Catalog, payments, inventory, entitlements, and fulfillment are structural seams. The Prisma adapters can share a transaction so order creation and stock reservation—and later order confirmation and fulfillment request creation—commit atomically.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/commerce@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/commerce@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/commerce` | `./dist/index.d.ts` |
| `@playstack/commerce/errors` | `./dist/errors.d.ts` |
| `@playstack/commerce/prisma` | `./dist/prisma.d.ts` |
| `@playstack/commerce/testing` | `./dist/testing.d.ts` |
| `@playstack/commerce/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/local-first

Source: https://playstack.dev/docs/packages/infrastructure/local-first.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/local-first` supplies one synchronization engine for web, mobile, and desktop clients while leaving product entities and platform storage explicit.

```ts
const client = createLocalFirstClient({
  definition,
  storage,
  transport,
  runtime,
  scope: () => ({ type: 'account', id: currentAccountId }),
  clock,
  ids,
})

await client.mutate('draft.save', draft)
await client.sync()
```

An optimistic projection and its outbound mutation commit in one local transaction. Sync then acquires a scoped lease, pushes bounded batches, records retry, conflict, or dead-letter outcomes, and applies pulled changes with their cursor atomically.

Authentication, authorization, conflict policy, product schemas, retention, attachments, and the server change source remain application-owned. The package does not make payments, inventory, or other invariant-heavy writes automatically safe offline.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/local-first@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Shipped adapters and non-goals

IndexedDB and Expo SQLite adapters are published. React local-first bindings, a Tauri transaction bridge and Nest sync protocol bindings remain planned. Platform storage and process-local leases do not establish a native filesystem transaction or replace product revision/tombstone protocols.

Keep canonical files, unsaved buffers, conflict recovery, retention and atomic grouped changes under product policy. A generic outbox cannot qualify a native sync replacement without crash/restart and real-client conformance tests.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/local-first@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/local-first` | `./dist/index.d.ts` |
| `@playstack/local-first/testing` | `./dist/testing.d.ts` |
| `@playstack/local-first/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/local-first/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-event-store

Source: https://playstack.dev/docs/packages/events-operations/nest-event-store.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-event-store` registers native Castore stores and idempotent projection adapters, then schedules persisted-log projection work through application-owned BullMQ infrastructure.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-event-store@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose root and features

```ts
PlaystackEventStoreModule.forRoot({
  app: 'catalog-api',
  environment: 'production',
  source,
  checkpoints,
  batchSize: 100,
  schedulerEveryMs: 1_000,
})

PlaystackEventStoreModule.forFeature({
  stores: [orders],
  projections: [
    { id: 'order-search', adapter: orderSearch, reset: () => search.clear() },
  ],
  commandHandlers: [SubmitOrderHandler],
})
```

## Root configuration

| Property | Required | Purpose |
| --- | --- | --- |
| `app`, `environment` | Yes | Qualify the BullMQ prefix consistently with `@playstack/queues`. |
| `source` | Yes | Reads projection events from the persisted log. |
| `checkpoints` | Yes | Stores per-projection positions. |
| `batchSize` | No | Events read per job. |
| `schedulerEveryMs` | No | Projection scheduling cadence. |
| `bullRoot` | No | BullMQ root options when the application has not already configured them. |
| `enableCqrs` | No | Enables optional Nest CQRS command-handler registration. |

`forRootAsync` keeps `app`, `environment`, imports, injection, and `enableCqrs` at module construction time; its factory returns only source, checkpoints, and runtime tuning.

## Feature and replay properties

Each projection definition has a stable `id`, idempotent adapter, optional event-store filter, and optional `reset` callback that clears derived state. `ProjectionReplayService.replay(id)` calls reset, clears the checkpoint, and rebuilds from the first persisted event.

Projection jobs always read the authoritative persisted log; they never depend on Nest’s in-process `EventBus`. Delivery remains at-least-once.

## Boundary

This module is non-global thin wiring. Aggregate behavior, storage, upcasters, projection logic, BullMQ root lifecycle, and queue processors remain in core packages or application code.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-event-store@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-event-store` | `./dist/index.d.ts` |
| `@playstack/nest-event-store/bullmq` | `./dist/bullmq.d.ts` |
| `@playstack/nest-event-store/cqrs` | `./dist/cqrs.d.ts` |
| `@playstack/nest-event-store/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/bullmq` | `^10.0.0 \|\| ^11.0.0` | Optional; only for the entry points that use it. |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/cqrs` | `^10.0.0 \|\| ^11.0.0` | Optional; only for the entry points that use it. |
| `@playstack/event-store` | `0.1.0-beta.4` | Required by the package. |
| `bullmq` | `>=5.16.0 <7` | Optional; only for the entry points that use it. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-notifications

Source: https://playstack.dev/docs/packages/communication/nest-notifications.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-notifications` binds a configured `@playstack/notifications` facade to Nest dependency injection and a validated queue consumer. It does not install routes, select a queue driver, own request authentication, or register global providers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-notifications@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Configure

```ts
PlaystackNotificationsModule.forRoot(notifications)
```

For Nest-managed dependencies:

```ts
PlaystackNotificationsModule.forRootAsync({
  imports: [ApplicationNotificationsModule],
  inject: [APPLICATION_NOTIFICATIONS],
  useFactory: (notifications) => notifications,
})
```

## Connect the worker

```ts
await notificationConsumer.handle(job.data)
```

Bind the application queue job to `PlaystackNotificationConsumer.handle()`. The consumer parses the untrusted envelope with `parseNotificationJob()` before calling the core service's `process()` method.

Inject `NOTIFICATIONS_SERVICE` to access the original configured facade. Queueing, delivery, inbox persistence, preferences, locale resolution, and events remain core or application seams.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-notifications@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-notifications` | `./dist/index.d.ts` |
| `@playstack/nest-notifications/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/notifications` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/validation

Source: https://playstack.dev/docs/packages/foundation/validation.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/validation` lets browser forms, APIs, and generated OpenAPI descriptions agree on schemas and validation failures.

## Install

```sh
npm install @playstack/core @playstack/validation zod
```

Install the optional Nest peers only when using `@playstack/validation/nest`.

## Compose one schema across boundaries

Keep schemas in an application contract library, parse at the transport edge, and pass only validated values to domain code.

```ts
import { emailSchema, parse, z } from '@playstack/validation'

export const createUserSchema = z.object({
  email: emailSchema,
  displayName: z.string().trim().min(1).max(80),
})

export type CreateUserInput = z.output<typeof createUserSchema>

export function createUser(input: unknown) {
  const command = parse(createUserSchema, input)
  return users.create(command)
}
```

## Stable wire contract

`formatValidationError` and `validationBodyFromError` produce the same response shape for every HTTP framework:

```ts
interface ValidationErrorBody {
  error: { code: string; retryable: boolean; message: string }
  fields: Readonly<Record<string, readonly string[]>>
  form: readonly string[]
}
```

`fields` uses dot-separated schema paths. Issues without a path go in `form`. `malformedJsonErrorBody()` and `refinementFailedErrorBody()` produce the smaller standard error body.

## Configuration reference

The portable package has no global configuration. Parsing functions accept an optional Zod error map:

| API | Input | Result |
| --- | --- | --- |
| `parse(schema, input, options?)` | Any synchronous Zod schema | Validated output or `ValidationFailedError`. |
| `parseAsync(schema, input, options?)` | Schema with async refinements | Validated output or `ValidationFailedError`. |
| `formatValidationError(error, message?)` | A `ZodError` | Portable `ValidationErrorBody`. |
| `validationBodyFromError(error)` | `ValidationFailedError` | Portable `ValidationErrorBody`. |

Built-in schemas cover normalized email, kebab-case slugs, offset ISO timestamps, sort direction, and cursor pagination with a default limit of 25 and maximum of 100.

## NestJS bridge

```ts
import {
  PlaystackValidationPipe,
  cleanupOpenApiDoc,
  createZodDto,
} from '@playstack/validation/nest'

class CreateUserDto extends createZodDto(createUserSchema) {}

@Post()
create(
  @Body(new PlaystackValidationPipe(createUserSchema)) input: CreateUserInput,
) {
  return users.create(input)
}

const document = cleanupOpenApiDoc(SwaggerModule.createDocument(app, config))
```

`PlaystackValidationPipe` accepts the schema and the same optional error-map configuration. Zod issues become HTTP 400 responses; unexpected refinement failures become HTTP 500 responses. DTO and serializer helpers are re-exported from `nestjs-zod` so request, response, and OpenAPI schemas remain aligned.

## React bridge

`applyValidationErrors` works with any form library that exposes a `setError(path, { type, message })` callback:

```ts
import { applyValidationErrors } from '@playstack/validation/react'

const { fieldCount, form } = applyValidationErrors(response, setError)
setFormErrors(form)
```

It returns the number of field messages applied plus the form-level messages; it does not choose a React form library or render error UI.

## Boundary

Validation is not a sanitizer, form library, localization framework, authorization layer, or database-schema generator. The application owns schema placement, localized error maps, and transport-specific parsing of malformed JSON.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/validation@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/validation` | `./dist/index.d.ts` |
| `@playstack/validation/errors` | `./dist/errors.d.ts` |
| `@playstack/validation/nest` | `./dist/nest.d.ts` |
| `@playstack/validation/react` | `./dist/react.d.ts` |
| `@playstack/validation/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Optional; only for the entry points that use it. |
| `@nestjs/swagger` | `^7.4.2 \|\| ^8.0.0 \|\| ^11.0.0` | Optional; only for the entry points that use it. |
| `nestjs-zod` | `^5.5.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Free packages

Source: https://playstack.dev/docs/free.md

Free covers the capabilities most applications need first: authentication, API keys, subscription billing, mail, queues, storage, cache, search, events, localization, validation and rate limiting, together with the framework bindings that make them usable in NestJS, Next.js and React. The line is drawn where the Laravel ecosystem draws it: what Laravel ships for free, Playstack ships for free. There are 51 Free packages.

Free is a licensing classification, not a publication receipt. Public npm is the intended account-free distribution channel; check [preview access](/docs/packages#access-policy) before installing. Free and paid packages use the same version, so you can add a Pro capability later without replacing your existing integrations.

## Catalog

- [`@playstack/ai`](/docs/packages/content-intelligence/ai)
- [`@playstack/api-keys`](/docs/packages/identity/api-keys)
- [`@playstack/auth`](/docs/packages/identity/auth)
- [`@playstack/auth-authjs`](/docs/packages/identity/auth-authjs)
- [`@playstack/auth-contracts`](/docs/packages/identity/auth-contracts)
- [`@playstack/auth-next`](/docs/packages/identity/auth-next)
- [`@playstack/auth-react`](/docs/packages/identity/auth-react)
- [`@playstack/billing`](/docs/packages/identity/billing)
- [`@playstack/cache`](/docs/packages/infrastructure/cache)
- [`@playstack/cache-next`](/docs/packages/infrastructure/cache-next)
- [`@playstack/cli`](/docs/cli)
- [`@playstack/client-auth`](/docs/packages/identity/client-auth)
- [`@playstack/connections`](/docs/packages/identity/connections)
- [`@playstack/core`](/docs/packages/foundation/core)
- [`@playstack/crypto`](/docs/packages/foundation/crypto)
- [`@playstack/delivery`](/docs/packages/communication/delivery)
- [`@playstack/errors`](/docs/packages/foundation/errors)
- [`@playstack/errors-react`](/docs/packages/foundation/errors-react)
- [`@playstack/events`](/docs/packages/events-operations/events)
- [`@playstack/events-prisma`](/docs/packages/events-operations/events-prisma)
- [`@playstack/i18n`](/docs/packages/foundation/i18n)
- [`@playstack/i18n-react`](/docs/packages/foundation/i18n-react)
- [`@playstack/lint`](/docs/packages/foundation/lint)
- [`@playstack/mail`](/docs/packages/infrastructure/mail)
- [`@playstack/mfa`](/docs/packages/identity/mfa)
- [`@playstack/nest-api-keys`](/docs/packages/identity/nest-api-keys)
- [`@playstack/nest-auth`](/docs/packages/identity/nest-auth)
- [`@playstack/nest-billing`](/docs/packages/identity/nest-billing)
- [`@playstack/nest-delivery`](/docs/packages/communication/nest-delivery)
- [`@playstack/nest-errors`](/docs/packages/foundation/nest-errors)
- [`@playstack/nest-events`](/docs/packages/events-operations/nest-events)
- [`@playstack/nest-i18n`](/docs/packages/foundation/nest-i18n)
- [`@playstack/nest-notifications`](/docs/packages/communication/nest-notifications)
- [`@playstack/nest-queues`](/docs/packages/infrastructure/nest-queues)
- [`@playstack/nest-rate-limit`](/docs/packages/events-operations/nest-rate-limit)
- [`@playstack/nest-search`](/docs/packages/content-intelligence/nest-search)
- [`@playstack/nest-storage`](/docs/packages/infrastructure/nest-storage)
- [`@playstack/notifications`](/docs/packages/communication/notifications)
- [`@playstack/notifications-react`](/docs/packages/communication/notifications-react)
- [`@playstack/prisma-runtime`](/docs/packages/foundation/prisma-runtime)
- [`@playstack/queues`](/docs/packages/infrastructure/queues)
- [`@playstack/rate-limit`](/docs/packages/events-operations/rate-limit)
- [`@playstack/rate-limit-do`](/docs/packages/events-operations/rate-limit-do)
- [`@playstack/rate-limit-prisma`](/docs/packages/events-operations/rate-limit-prisma)
- [`@playstack/rate-limit-redis`](/docs/packages/events-operations/rate-limit-redis)
- [`@playstack/search`](/docs/packages/content-intelligence/search)
- [`@playstack/search-minisearch`](/docs/packages/content-intelligence/search-minisearch)
- [`@playstack/search-next`](/docs/packages/content-intelligence/search-next)
- [`@playstack/skills`](/docs/packages/foundation/skills)
- [`@playstack/storage`](/docs/packages/infrastructure/storage)
- [`@playstack/validation`](/docs/packages/foundation/validation)

Every `@playstack/*` package ships at the same fixed version, whatever its tier, so a Free package and a Pro package always compose without version drift. Read [package access](/docs/packages#access-policy) for licensing and installation details.

## Infrastructure and delivery

Source: https://playstack.dev/docs/packages/infrastructure.md

Infrastructure packages normalize the small set of behavior application capabilities need while leaving connections, credentials, lifecycle, observability, and native provider access under application control.

## Packages

| Package                                                                                   | Plan                       | Responsibility                                               |
| ----------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------ |
| [`@playstack/cache`](/docs/packages/infrastructure/cache)                                 | <PlanTierDot tier="pro" /> | JSON caching, tag invalidation, and stampede-safe reads.     |
| [`@playstack/cache-next`](/docs/packages/infrastructure/cache-next)                       | <PlanTierDot tier="pro" /> | Shared Next.js caching and signed revalidation.              |
| [`@playstack/storage`](/docs/packages/infrastructure/storage)                             | <PlanTierDot tier="pro" /> | Streaming object storage and explicit URL capabilities.      |
| [`@playstack/nest-storage`](/docs/packages/infrastructure/nest-storage)                   | <PlanTierDot tier="pro" /> | Named NestJS storage-driver injection.                       |
| [`@playstack/mail`](/docs/packages/infrastructure/mail)                                   | <PlanTierDot tier="pro" /> | Typed rendering, envelopes, and provider delivery.           |
| [`@playstack/queues`](/docs/packages/infrastructure/queues)                               | <PlanTierDot tier="pro" /> | Typed jobs, namespacing, retries, failure hooks, and traces. |
| [`@playstack/nest-queues`](/docs/packages/infrastructure/nest-queues)                     | <PlanTierDot tier="pro" /> | NestJS queue injection and worker lifecycle.                 |
| [`@playstack/local-first`](/docs/packages/infrastructure/local-first)                     | <PlanTierDot tier="pro" /> | Atomic local mutations and push/pull synchronization.        |
| [`@playstack/local-first-indexeddb`](/docs/packages/infrastructure/local-first-indexeddb) | <PlanTierDot tier="pro" /> | IndexedDB transactions and browser coordination.             |
| [`@playstack/local-first-expo`](/docs/packages/infrastructure/local-first-expo)           | <PlanTierDot tier="pro" /> | Expo SQLite transactions and mobile coordination.            |
| [`@playstack/registry`](/docs/packages/infrastructure/registry)                           | <PlanTierDot tier="pro" /> | Private package registry core with host-owned access.        |
| [`@playstack/nest-registry`](/docs/packages/infrastructure/nest-registry)                 | <PlanTierDot tier="pro" /> | NestJS binding for the read-only npm registry protocol.      |
| [`@playstack/registry-swift`](/docs/packages/infrastructure/registry-swift)               | <PlanTierDot tier="pro" /> | Swift Package Manager registry reads over the same core.     |

## Application-owned clients

Every adapter accepts or exposes the underlying provider client. That lets the composition root control pooling, credentials, shutdown, transactions, metrics, and provider-specific escape hatches.

## Adopt independently

These packages do not form a required platform. Use the cache without the queue, the storage contract without mail, or none of them when a provider SDK already gives the application the right boundary.

## Lint and Skills

Source: https://playstack.dev/docs/cli/lint-skills.md

`@playstack/lint` and `@playstack/skills` are installed independently from the CLI. The CLI adopts their output without making either package a runtime dependency.

## Install the guidance layers

```sh
npm install --save-dev @playstack/cli @playstack/lint @playstack/skills
npx playstack init
```

The two layers serve different work:

| Layer             | Responsibility                                                               |
| ----------------- | ---------------------------------------------------------------------------- |
| Lint and `doctor` | Deterministic rules that should produce the same result on every run.        |
| Skills            | Contextual judgement about boundaries, ownership, extraction, and tradeoffs. |

## Emit the lint fragment

```sh
npx playstack lint init --diff
npx playstack lint init
```

The managed output is a flat ESLint configuration fragment. Import it from the repository's root ESLint configuration.

The recommended preset can start as warnings so existing projects adopt checks deliberately. Extension and Chakra presets require explicit file globs because those rules are only valid inside their corresponding boundaries. Chakra configuration may also declare the permitted semantic tokens.

```json
{
  "lint": {
    "target": "eslint.playstack.config.mjs",
    "recommended": true,
    "extension": {
      "files": ["apps/extension/src/**/*.{ts,tsx}"]
    },
    "chakra": {
      "files": ["apps/site/src/**/*.{ts,tsx}"],
      "tokens": ["fg.default", "bg.canvas", "border.default"]
    }
  }
}
```

## Synchronize Skills

```sh
npx playstack skills sync --diff
npx playstack skills sync
npx playstack skills sync --check
```

The CLI validates `@playstack/skills/playstack.skills.json` and every declared content hash before writing to the configured targets. The default target is `.agents/skills`.

Installed state records the package version, stable skill ID, source, individual file hashes, and aggregate content hash in `.playstack/skills.json`. A local edit is reported as unsafe and is never overwritten silently.

A repository-specific skill should live in a user-owned override directory. Map that directory to the packaged stable ID in `playstack.json`; the override then shadows the managed source without modifying it.

## Check both layers together

```sh
npx playstack doctor --check
```

Doctor verifies the managed lint fragment, Skills corpus, package artifacts, and ejected upstream state in one CI-safe command.

Explore the [Skills overview](/skills) or the complete [`@playstack/skills` reference](/docs/packages/foundation/skills) for the seven included reviewers and their boundaries.

## @playstack/accounts-react

Source: https://playstack.dev/docs/packages/identity/accounts-react.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/accounts-react` exposes account selection state without merging account membership into the authentication client.

## Use it for

- Loading the accounts visible to the current user.
- Tracking the active account in React state.
- Switching account context through application-defined transport.
- Composing account-aware application shells.

## Install and compose

```sh
npm install @playstack/accounts @playstack/accounts-react react
```

Place the accounts provider inside the auth provider through a small application-owned bridge, so accounts stay optional and session replacement stays explicit:

```tsx
import { PlaystackAccountsProvider, createFetchAccountsClient } from '@playstack/accounts-react'
import { useAuth, useSession } from '@playstack/auth-react'

const accountsClient = createFetchAccountsClient({
  baseUrl: 'https://api.example.com',
  headers: () => ({ authorization: `Bearer ${transport.getAccessToken()}` }),
})

function AccountsBridge({ children }) {
  const { session } = useSession()
  const { replaceSession, refresh } = useAuth()
  return (
    <PlaystackAccountsProvider
      client={accountsClient}
      session={session}
      onSessionChange={usesBearerTransport ? refresh : replaceSession}
    >
      {children}
    </PlaystackAccountsProvider>
  )
}
```

With the same-origin cookie transport, `onSessionChange={replaceSession}` adopts the session the switch returned. With the split-origin bearer transport, the API rotated the refresh cookie when it issued the account-scoped session, so pass `refresh` instead and the transport obtains the access token that matches the new session.

```tsx
const { account, accounts, status, error, switchAccount, revalidate } = useAccount()
```

The provider clears account data when the authenticated user changes. Switching is allowed only to an account in the loaded list, and the returned session must match the same user, selected account, and listed role before state changes.

## Provider properties

| Property | Default | Purpose |
| --- | --- | --- |
| `client` | Required | `AccountsClient`: `listAccounts()` and `switchAccount(accountId)`. |
| `session` | Required | The current `AuthSessionView` or `null`, from `useSession()`. |
| `initialAccounts` | `undefined` | Pre-loaded account list; omit to fetch on mount. |
| `onSessionChange` | None | Receives the switched session; `replaceSession` or `refresh` from `useAuth()`. |
| `sessionParser` | Exact projection | Parser for sessions that carry validated `extensions`. |
| `onError` | None | Isolated error observer. |

## HTTP contract

The default client expects `GET /api/accounts` to return `{ accounts: AccountView[] }` (`id`, `name`, `slug`, `role`) and `POST /api/accounts/switch` to accept `{ accountId }` and return `{ session: AuthSessionView }`. Configure `baseUrl`, `accountsEndpoint`, `switchEndpoint`, `fetch` and a `headers` callback: echo the CSRF cookie for same-origin cookie sessions, or supply the transport's bearer token for split-origin sessions. Requests include cookies. Account and response objects reject unknown fields and duplicate IDs, and valid `{ error: { code, retryable, message } }` failures are preserved as `AccountsClientError.remote`.

Set `sessionParser` on both the client and the provider when sessions carry an application-validated `extensions` envelope; the default parser enforces the exact `AuthSessionView` projection and refuses extensions.

## Boundary

The package does not authorize membership and deliberately does not depend on `@playstack/auth-react`. Server operations still validate account scope and role on every request.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/accounts-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/accounts-react` | `./dist/index.d.ts` |
| `@playstack/accounts-react/errors` | `./dist/errors.d.ts` |
| `@playstack/accounts-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/accounts` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/auth-contracts` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/crypto

Source: https://playstack.dev/docs/packages/foundation/crypto.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/crypto` provides Workers-compatible authenticated encryption, digests, signatures, verification, and secure token generation while keeping key ownership explicit.

## Install

```sh
npm install @playstack/core @playstack/crypto
```

## Compose it once

Resolve secrets at the deployment boundary and inject a clock and Web Crypto implementation. Capability packages receive the resulting suite or the smaller function they require.

```ts
import { systemClock } from '@playstack/core'
import { createCrypto } from '@playstack/crypto'

export const applicationCrypto = createCrypto({
  crypto: globalThis.crypto,
  clock: systemClock(),
  encryptionKeys: {
    keys: [
      { id: 'enc-2026-08', key: process.env.ENCRYPTION_KEY_CURRENT!, active: true },
      { id: 'enc-2026-05', key: process.env.ENCRYPTION_KEY_PREVIOUS! },
    ],
  },
  signingKeys: {
    keys: [
      { id: 'sig-2026-08', key: process.env.SIGNING_KEY_CURRENT!, active: true },
      { id: 'sig-2026-05', key: process.env.SIGNING_KEY_PREVIOUS! },
    ],
  },
  maximumTokenTtl: '15m',
  replayStore: singleUseTokenStore,
})

const token = await applicationCrypto.sign({ userId }, { purpose: 'email-verification', ttl: '10m' })
```

The key strings are canonical padded base64. Encryption keys must decode to exactly 32 bytes; signing keys must decode to at least 32 bytes. Keep old keys available for verification and decryption until their data has expired or been re-encrypted, but mark exactly one key in each keyset active.

## Node-only and browser declarations

For encryption-only consumers, `createEncryption` from
`@playstack/crypto/encryption` needs only Web Crypto, rotating AES-256 keys and an
explicit server-configured purpose. It requires no signing keyset or clock.
Its bounded `pse1` envelope authenticates the key ID and purpose; use separate
purposes for refresh recovery and OAuth attempts. It preserves empty, binary and
UTF-8 plaintext and supports re-encryption after key rotation. The existing full
suite's `v1` envelope is a separate contract, not a fallback format.

The public digest, HMAC, and encryption capabilities use minimal DOM-independent interfaces rather than requiring ambient `SubtleCrypto`. Node-only consumers can use `lib: ["es2022"]`, Node types, and `skipLibCheck: false`; browser consumers can retain their DOM libraries. Inject the runtime's actual Web Crypto implementation. Enabling browser globals or disabling declaration checking is not required for server projects, and this type boundary does not supply a runtime crypto polyfill.

## Configuration reference

| `CryptoSuiteOptions` property | Type           | Required | Purpose                                                                           |
| ----------------------------- | -------------- | -------- | --------------------------------------------------------------------------------- |
| `crypto`                      | `WebCrypto`    | Yes      | Supplies `subtle` and `getRandomValues`; usually `globalThis.crypto`.             |
| `clock`                       | `Clock`        | Yes      | Controls issue, expiry, and verification time.                                    |
| `encryptionKeys`              | `KeysetConfig` | Yes      | AES-GCM keys with one active key.                                                 |
| `signingKeys`                 | `KeysetConfig` | Yes      | HMAC-SHA-256 keys with one active key.                                            |
| `replayStore`                 | `ReplayStore`  | No       | Atomically consumes token IDs for single-use verification.                        |
| `maximumTokenTtl`             | `string`       | No       | Maximum signed-token lifetime; defaults to `5m`. Supports `ms`, `s`, `m`, or `h`. |

Each `KeyConfig` has an `id`, a base64 `key`, and optional `active`. IDs are embedded in output envelopes so key rotation does not require trial decryption.

## Operations

| Method                                | Use                                                                                |
| ------------------------------------- | ---------------------------------------------------------------------------------- |
| `encrypt` / `decrypt` / `decryptText` | Authenticated AES-GCM envelopes for strings or bytes.                              |
| `needsReencryption` / `reencrypt`     | Detect and migrate values written by an older encryption key.                      |
| `randomToken`                         | Generate an opaque base64url token; defaults to 32 bytes and requires at least 16. |
| `digestSha256`                        | Produce a stable digest for storing opaque-token lookups.                          |
| `verifyHmacSha256`                    | Compare an expected HMAC without timing-sensitive string comparison.               |
| `sign` / `verify`                     | Create and verify purpose-bound, expiring structured tokens.                       |

## Drain legacy encrypted envelopes

`createSecretMigrationCodec()` composes the current suite with application-owned legacy decoders. A decoder must positively identify its format; ambiguous and unknown values fail closed, and a malformed current Playstack envelope never falls back to a legacy reader.

Successful legacy decryption returns a current replacement envelope but performs no write. The application persists that replacement with compare-and-swap after the surrounding domain operation succeeds, allowing retired formats and keys to be drained without making them permanent Playstack behavior.

## Replay-store bridge

Single-use verification calls one application-owned bridge:

```ts
interface ReplayStore {
  consume(jti: string, expiresAtSeconds: number): Promise<boolean>
}
```

`consume` must be atomic across all instances. Return `true` only for the first consumption; retain the marker until `expiresAtSeconds`. Redis `SET NX` or a database uniqueness constraint are suitable implementations. If `singleUse: true` is requested without this bridge, verification fails closed.

## Testing

`@playstack/crypto/testing` exports `DeterministicCrypto` and `MemoryReplayStore`. Use them with `FakeClock` from `@playstack/core/testing` to test rotation, expiry, and replay behavior without nondeterminism.

## Boundary

The package does not load environment variables, manage or derive keys, hash passwords, or provide persistent replay storage. The application owns its secret manager, rotation procedure, and bridge lifecycle.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/crypto@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/crypto` | `./dist/index.d.ts` |
| `@playstack/crypto/encryption` | `./dist/encryption.d.ts` |
| `@playstack/crypto/primitives` | `./dist/primitives.d.ts` |
| `@playstack/crypto/errors` | `./dist/errors.d.ts` |
| `@playstack/crypto/testing` | `./dist/testing.d.ts` |
| `@playstack/crypto/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/crypto/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/delivery

Source: https://playstack.dev/docs/packages/communication/delivery.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/delivery` normalizes outbound dispatch across mail, broadcast, push, in-app, and SMS slots while enforcing one suppression model. It does not own audiences, consent, message composition, provider DNS, or durable delivery history.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/delivery@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose and enqueue

```ts
import { createDelivery } from '@playstack/delivery'

const delivery = createDelivery({
  slots: [transactionalMail, broadcastMail, push],
  queue,
  persistence: suppressions,
  normalizer: endpoints,
  hasher: keyedEndpointHasher,
  events,
  clock,
  ids,
})

await delivery.enqueue({
  idempotencyKey: 'password-reset:user-123:token-456',
  subject: { type: 'user', id: 'user-123' },
  endpoint: { channel: 'mail', value: 'person@example.com' },
  category: 'transactional',
  payload: { template: 'password-reset', tokenId: 'token-456' },
})
```

The service checks suppression before enqueue and again before provider dispatch.
That check cannot recall an in-flight submission or atomically lock a remote
provider together with consent. `DeliveryService.process()` is immediate dispatch:
it does not persist attempts or durably deduplicate repeated calls.

## Durable attempts and replay

Use the optional `createDeliveryWorker` with `PrismaDeliveryAttemptPersistence`
from `@playstack/delivery/attempts/prisma` when a queue must survive crashes and
uncertain acknowledgments. Select and migrate `delivery.attempts.prisma`; provide
the actual root Prisma client, not an enclosing transaction/savepoint.

Register immutable intent, its protected payload reference and the host outbox
record in one physical transaction before publishing work. The worker's `prepare`
callback uses its supplied transaction handle to check current authorization and
suppression, resolve protected content and recompute the request hash. Do not
trust a queued hash or recipient as authorization.

Only a newly committed submission transition grants provider I/O. Replayed begins,
lost commit acknowledgments, active submissions and quarantined expiries do not
grant another send. Provider exceptions and malformed receipts become unknown
outcomes by default, not permission to retry or fail over to another transport.
Keep provider SDK retries under the same policy.

For a positively established temporary refusal, the optional synchronous
`classifyDispatchFailure(error, intent)` hook may return
`{ kind: 'confirmed-temporary-rejection', retryAt }`, using a future absolute
integer deadline in milliseconds. The provider-specific evidence must prove the
**entire invocation was not accepted**. A timeout, generic status, arbitrary
`retryable` flag, partial SMTP acceptance or lost acknowledgment is insufficient.
Return `undefined` for unqualified exceptions; do not perform I/O in this hook.

Malformed receipts and receipt-recording failures never enter this classifier.
Malformed/throwing classifier results or an expired submission lease remain
unknown and quarantined. Qualified retries preserve original attempt/age budgets
and live preparation checks; the transactional `recorded` hook can persist their
scheduling handoff. `pending` is not permission to acknowledge the job or resend
immediately. The host must schedule recovery at or after the deadline.

For confirmed permanent refusal of the entire invocation, return exactly
`{ kind: 'confirmed-permanent-rejection' }`. The worker records terminal quarantine
with `submission_permanently_rejected`, rather than uncertain quarantine; neither
automatically retries. This does not declare an endpoint invalid or add suppression.
The same evidence, lease/revision and transactional recording requirements apply.
Timeouts, partial acceptance and malformed receipts must remain unknown.

The `recorded` callback shares the outcome transaction. A
`DeliveryReceiptRecordingError` preserves accepted receipt evidence when local
recording fails: repair that recording without resending. Acceptance is not inbox
delivery. Use explicitly authorized reconciliation for safe retries.

The worker returns state, not an unconditional queue acknowledgment. Acknowledge
only a durable terminal disposition or durable recovery handoff; pending, leased
or submitting work still needs recovery. The host owns queue retention, outbox
publication, recovery scheduling and shutdown. Neither composition promises
exactly-once external delivery. Qualify the actual database/driver transaction
boundary; see [Prisma runtime](/docs/packages/foundation/prisma-runtime#node-prismapg-dispatch-fence).

## Configuration reference

| `DeliveryOptions` property | Required | Purpose                                                                                                |
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `slots`                    | Yes      | Named provider boundaries declaring channel, provider, sending domain, capabilities, and `dispatch()`. |
| `queue`                    | Yes      | Persists the fully resolved delivery job.                                                              |
| `persistence`              | Yes      | Transactional subject, endpoint, channel, and topic suppressions.                                      |
| `normalizer`               | Yes      | Applies application-owned endpoint normalization.                                                      |
| `hasher`                   | Yes      | Produces keyed endpoint hashes for lookup without retaining raw addresses.                             |
| `events`                   | Yes      | Emits dispatch and suppression lifecycle events.                                                       |
| `clock`, `ids`             | Yes      | Application-owned time and identifiers.                                                                |
| `slotResolver`             | No       | Chooses a compatible slot; the default requires exactly one match.                                     |
| `onWarning`                | No       | Observes potentially unsafe configuration such as shared mail and broadcast domains.                   |

Each `DeliverySlot` exposes provider capabilities and remains the native-client escape hatch. Applications can route by tenant or region through a custom `DeliverySlotResolver`.

## Suppression semantics

Marketing sends honor subject, endpoint, channel, and topic suppressions. Transactional sends bypass marketing-only channel and topic opt-outs, but never subject-wide or invalid-endpoint suppression.

`addSuppressionInTransaction()` and `removeSuppressionInTransaction()` let another
feature change its state and suppression atomically. Both adapters must join the
same physical transaction; matching transaction-kind strings alone are not proof.

`delivery.checkSuppressionInTransaction(request, tx)` is an explicit preparation
capability. Use the worker's original handle alongside consent, current authority
and protected payload resolution; no root-read fallback is allowed. The native
PostgreSQL store requires Read Committed and serializes the whole namespaced
subject with add/remove operations, including when no suppression exists. Hosts
must preserve compatible lock ordering and use the native writer contract.
Other isolation levels and adapters without the capability fail closed. Await a
newly committed attempt begin before provider I/O: this gate does not recall sends
or prevent suppression racing after commit.

For remote-authoritative audiences without outbound delivery, use
`createDeliverySuppressionService({ persistence, normalizer, hasher, events, clock, ids })`.
It implements `DeliverySuppressionWriter` with the same native persistence and
events, without delivery slots, provider clients or a queue.

## Deliver to registered devices

`@playstack/delivery/push` resolves encrypted targets owned by `@playstack/devices` immediately before provider I/O, keeping raw push credentials out of queued jobs.

Named adapters under `/apns`, `/expo`, `/fcm`, and `/webpush` accept application-created
clients. Outcomes include accepted, transient-failure, permanent-failure and
outcome-unknown. Unknown outcomes are non-retryable uncertainty, not evidence of an
invalid token. Only provider-confirmed permanent failures invalidate the target.
An application-owned `PushTargetSource` can replace registered-device persistence;
it must preserve live authorization and permanent-failure feedback.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/delivery@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/delivery/attempts/prisma` | `./dist/attempts-prisma.d.ts` |
| `@playstack/delivery/attempts` | `./dist/attempts.d.ts` |
| `@playstack/delivery/prisma` | `./dist/prisma.d.ts` |
| `@playstack/delivery` | `./dist/index.d.ts` |
| `@playstack/delivery/errors` | `./dist/errors.d.ts` |
| `@playstack/delivery/push` | `./dist/push.d.ts` |
| `@playstack/delivery/expo` | `./dist/expo.d.ts` |
| `@playstack/delivery/apns` | `./dist/apns.d.ts` |
| `@playstack/delivery/fcm` | `./dist/fcm.d.ts` |
| `@playstack/delivery/webpush` | `./dist/webpush.d.ts` |
| `@playstack/delivery/testing` | `./dist/testing.d.ts` |
| `@playstack/delivery/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/local-first-indexeddb

Source: https://playstack.dev/docs/packages/infrastructure/local-first-indexeddb.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/local-first-indexeddb` stores engine records and product projections in one real IndexedDB transaction. Product code keeps ownership of its object stores, indexes, and migrations through the native transaction handle.

```ts
const storage = createIndexedDbLocalFirstStorage({
  name: 'linegraph',
  version: 1,
  productStores: ['drafts', 'revisions'],
  upgrade(database) {
    database.createObjectStore('drafts', { keyPath: 'id' })
  },
})

const runtime = createBrowserLocalFirstRuntime({
  channelName: 'linegraph:sync',
})
```

Web Locks exclude competing tabs when available. `BroadcastChannel` wakes peers but is not treated as a correctness lock. Transaction callbacks must not await network or unrelated timer work because browsers may commit an idle transaction.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/local-first-indexeddb@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/local-first-indexeddb@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/local-first-indexeddb` | `./dist/index.d.ts` |
| `@playstack/local-first-indexeddb/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/local-first-indexeddb/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-catalog

Source: https://playstack.dev/docs/packages/commerce/nest-catalog.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-catalog` binds an existing catalog service to application authorization and audit context.

```ts
PlaystackCatalogModule.forRoot({
  catalog,
  authorize: (request, permission, resourceId) =>
    policy.allows(request, permission, resourceId),
  resolveContext: (request) => auditContext(request),
})
```

The package supplies injectable services and synchronous or async module registration. It does not install routes, validate DTOs, select authentication, or provide an administration UI.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-catalog@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-catalog@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-catalog` | `./dist/index.d.ts` |
| `@playstack/nest-catalog/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/catalog` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/rate-limit

Source: https://playstack.dev/docs/packages/events-operations/rate-limit.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/rate-limit` evaluates Workers-compatible token-bucket, fixed-window or
rolling-window/cooldown policies with private subject keys and atomic storage.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/rate-limit@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and define policy

```ts
import { HmacSha256KeyHasher, createRateLimiter, defineRateLimits } from '@playstack/rate-limit'

const definitions = defineRateLimits({
  'api.request': {
    failureMode: 'closed',
    buckets: [
      { subject: 'account', capacity: 1_000, refill: { tokens: 1_000, everyMs: 60_000 } },
      { subject: 'apiKey', capacity: 100, refill: { tokens: 100, everyMs: 60_000 } },
    ],
  },
})

export const limiter = createRateLimiter({
  definitions,
  environment: 'production',
  store,
  clock,
  keyHasher: new HmacSha256KeyHasher(crypto.subtle, rateLimitKey),
  onEvent: (event) => operations.record(event),
})

const decision = await limiter.consume('api.request', {
  subjects: { account: accountId, apiKey: key.id },
})
```

Raw subject values are HMAC-protected before storage. Use a deployment secret of at least 32 bytes and plan its rotation as a deliberate bucket reset.

## Definition properties

| Property                    | Meaning                                                           |
| --------------------------- | ----------------------------------------------------------------- |
| Definition name             | Lowercase dot notation such as `api.request`.                     |
| `failureMode`               | Explicit `closed` or `open` behavior when storage is unavailable. |
| `buckets`                   | One or more unique subject policies evaluated atomically.         |
| `subject`                   | `account`, `accountMember`, `apiKey`, `custom`, `ip`, or `user`.  |
| `capacity`                  | Maximum burst capacity in tokens.                                 |
| `refill.tokens` / `everyMs` | Positive refill amount and interval.                              |
| `cost`                      | Optional default operation cost; defaults to one token.           |

Values compile to integer microtokens, allowing fractional token policies without floating-point storage drift.

## Preserve fixed-window behavior

Applications deliberately selecting epoch-aligned fixed windows can use `defineFixedWindowRateLimits()` and `createFixedWindowRateLimiter()` from `@playstack/rate-limit/fixed-window`. This is not equivalent to every existing throttler or cooldown policy.

Fixed windows align to epoch boundaries, block until the next boundary after the limit is spent, and avoid partial spending across multi-subject policies. Choosing the strategy in composition keeps the behavior visible; it is not a hidden option that changes token-bucket semantics.

## Rolling expiry and denial cooldown

```ts
import {
  createRollingWindowRateLimiter,
  defineRollingWindowRateLimits,
} from '@playstack/rate-limit/rolling-window'

const limiter = createRollingWindowRateLimiter({
  definitions: defineRollingWindowRateLimits({
    'api.request': {
      failureMode: 'closed',
      windows: [{ subject: 'ip', limit: 10, windowMs: 60_000, cooldownMs: 60_000 }],
    },
  }),
  environment: 'production', store, clock, keyHasher,
})
```

Each accepted hit expires individually. First refusal starts the full cooldown;
blocked attempts neither spend quota nor extend cooldown. Cooldown expiry starts
empty. All subjects are evaluated atomically: denial spends no peer quota and only
exhausted subjects enter cooldown. Limits are bounded at 10,000 per subject.

Use the PostgreSQL `PrismaRollingWindowRateLimitStore` for distributed enforcement;
the memory store is for tests/local single-process use. Rolling Redis and Durable
Object adapters are not supplied. Existing token-bucket/fixed-window defaults are
unchanged. Storage failure remains unavailable under closed policy, not a 429;
fail-open is an explicit bypass. Do not retry an ambiguous commit.

The host owns proxy/subject policy, health exemptions, retry headers and a reviewed
counter cutover. New `psrl:rw:v1:` keys cannot silently share old counters without
changing quota. [PostgreSQL setup](/docs/packages/events-operations/rate-limit-prisma#rolling-window-store)
describes server-clock authority and bounded cleanup.

## Limiter configuration and consumption

| `RateLimiterOptions` property | Required | Purpose                                                        |
| ----------------------------- | -------- | -------------------------------------------------------------- |
| `definitions`                 | Yes      | Validated named policies.                                      |
| `environment`                 | Yes      | Lowercase identifier included in storage-key isolation.        |
| `store`                       | Yes      | Atomic multi-bucket bridge.                                    |
| `clock`                       | Yes      | Application time for stores that do not provide server time.   |
| `keyHasher`                   | Yes      | One-way subject-key derivation.                                |
| `onEvent`                     | No       | Isolated observer for denied or fail-open-bypassed operations. |

`consume` supplies required subject IDs, optional operation cost multiplier, and optional per-subject capacity/refill/cost overrides. Every subject declared by the policy must be resolved before calling it.

## Storage bridge

```ts
interface RateLimitStore {
  name: string
  consume(input: { nowMs: number; buckets: readonly CompiledBucket[] }): Promise<RateLimitDecision>
}
```

The store must decide and update every bucket atomically. A denied group spends none of them. The decision returns overall and per-bucket limit, remaining capacity, and optional retry time.

Use `@playstack/rate-limit/testing` only for tests and local single-process development. Select Redis, Prisma, or Durable Objects at a distributed deployment boundary.

## Boundary

Rate limiting is not a billing meter, entitlement engine, concurrency semaphore, or CDN defense. Resolve authenticated caller and tenant scope first; use edge/CDN defenses independently.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/rate-limit@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/rate-limit/rolling-window` | `./dist/rolling-window.d.ts` |
| `@playstack/rate-limit` | `./dist/index.d.ts` |
| `@playstack/rate-limit/errors` | `./dist/errors.d.ts` |
| `@playstack/rate-limit/fixed-window` | `./dist/fixed-window.d.ts` |
| `@playstack/rate-limit/algorithm` | `./dist/algorithm.d.ts` |
| `@playstack/rate-limit/definitions` | `./dist/definitions.d.ts` |
| `@playstack/rate-limit/types` | `./dist/types.d.ts` |
| `@playstack/rate-limit/testing` | `./dist/testing.d.ts` |
| `@playstack/rate-limit/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/search

Source: https://playstack.dev/docs/packages/content-intelligence/search.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/search` lets a product map its own records, filters, result shape, and ranking vocabulary into a portable search source.

```ts
const engine = createLexicalSearchEngine(records, {
  document: (record) => ({
    id: record.id,
    fields: { title: record.title, body: record.body, tags: record.tags },
    result: { id: record.id, title: record.title, href: record.href },
    group: record.parentId,
  }),
  fields: { title: 10, tags: 6, body: 1 },
  maxResultsPerGroup: 2,
})
```

The engine supports weighted fields, synonyms, filters, popular results, priority, stable ties, and per-group limits. Fetch handlers and clients validate the application-defined protocol, forward cancellation, default to private/no-store responses, and do not leak provider failures.

The package knows nothing about Chakra Docs, filesystems, databases, hosted search providers, or framework routes.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/search@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/search@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/search` | `./dist/index.d.ts` |
| `@playstack/search/client` | `./dist/client.d.ts` |
| `@playstack/search/errors` | `./dist/errors.d.ts` |
| `@playstack/search/http` | `./dist/http.d.ts` |
| `@playstack/search/lexical` | `./dist/lexical.d.ts` |
| `@playstack/search/testing` | `./dist/testing.d.ts` |
| `@playstack/search/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/search/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Content and intelligence

Source: https://playstack.dev/docs/packages/content-intelligence.md

These packages help applications transform authored content, publish standards-based documents, and execute AI work without binding domain code to one loader, framework, model provider, or delivery process.

## Packages

| Package                                                                                 | Plan                        | Responsibility                                            |
| --------------------------------------------------------------------------------------- | --------------------------- | --------------------------------------------------------- |
| [`@playstack/content`](/docs/packages/content-intelligence/content)                     | <PlanTierDot tier="free" /> | Loader-independent content transforms and schema helpers. |
| [`@playstack/content-next`](/docs/packages/content-intelligence/content-next)           | <PlanTierDot tier="pro" />  | Explicit Pages and App Router content resolution.         |
| [`@playstack/feeds`](/docs/packages/content-intelligence/feeds)                         | <PlanTierDot tier="free" /> | RSS, Atom, JSON Feed, sitemap, and storage generation.    |
| [`@playstack/feeds-next`](/docs/packages/content-intelligence/feeds-next)               | <PlanTierDot tier="pro" />  | Next.js route bindings using Web response primitives.     |
| [`@playstack/nest-feeds`](/docs/packages/content-intelligence/nest-feeds)               | <PlanTierDot tier="pro" />  | NestJS scheduling, regeneration, and queue consumers.     |
| [`@playstack/ai`](/docs/packages/content-intelligence/ai)                               | <PlanTierDot tier="free" /> | Tenant-aware context around the Vercel AI SDK.            |
| [`@playstack/search`](/docs/packages/content-intelligence/search)                       | <PlanTierDot tier="free" /> | Portable mapped lexical search and HTTP contracts.        |
| [`@playstack/search-minisearch`](/docs/packages/content-intelligence/search-minisearch) | <PlanTierDot tier="pro" />  | Fuzzy and prefix search through MiniSearch.               |
| [`@playstack/search-next`](/docs/packages/content-intelligence/search-next)             | <PlanTierDot tier="pro" />  | Next.js App and Pages Router search bindings.             |
| [`@playstack/nest-search`](/docs/packages/content-intelligence/nest-search)             | <PlanTierDot tier="pro" />  | Named NestJS search endpoints and readiness.              |

## Standards at the center

Content transforms operate on values rather than files. Feed generation emits public standards rather than a Playstack format. AI calls remain Vercel AI SDK calls with Playstack supplying the surrounding tenancy, key, limit, metering, and telemetry context.

## Delivery at the edge

Use framework packages only for request, scheduling, and worker integration. Storage clients, route policy, content loaders, and AI providers remain application-owned.

## Paid packages

Source: https://playstack.dev/docs/paid.md

Paid packages add optional capabilities such as team accounts, commerce, licensing, a private package registry, audiences, analytics, local-first sync and application UI compositions. Of 53 paid packages, 51 are assigned to Pro; the others await tier assignment.

Paid packages use the Playstack Pro License, which permits closed-source applications. Installation requires approved registry access; a documentation page does not activate a subscription. See [preview access](/docs/packages#access-policy). Under the subscription policy, releases published while a subscription is active remain eligible after it ends.

## Catalog

- [`@playstack/accounts`](/docs/packages/identity/accounts) — Pro
- [`@playstack/accounts-react`](/docs/packages/identity/accounts-react) — Pro
- [`@playstack/admin-ui`](/docs/packages/ui/admin-ui) — Pro
- [`@playstack/analytics`](/docs/packages/events-operations/analytics) — Pro
- [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react) — Pro
- [`@playstack/app-ui`](/docs/packages/ui/app-ui) — Pro
- [`@playstack/atproto`](/docs/packages/identity/atproto) — Pro
- [`@playstack/audiences`](/docs/packages/communication/audiences) — Pro
- [`@playstack/audiences-react`](/docs/packages/communication/audiences-react) — Pro
- [`@playstack/audiences-resend`](/docs/packages/communication/audiences-resend) — Pro
- [`@playstack/audit`](/docs/packages/identity/audit) — Pro
- [`@playstack/catalog`](/docs/packages/commerce/catalog) — Pro
- [`@playstack/chakra`](/docs/packages/ui/chakra) — Pro
- [`@playstack/commerce`](/docs/packages/commerce/commerce) — Pro
- [`@playstack/content`](/docs/packages/content-intelligence/content) — Pro
- [`@playstack/content-next`](/docs/packages/content-intelligence/content-next) — Pro
- [`@playstack/delivery-history`](/docs/packages/communication/delivery-history) — Pro
- [`@playstack/devices`](/docs/packages/identity/devices) — Pro
- [`@playstack/entitlements`](/docs/packages/identity/entitlements) — Pro
- [`@playstack/event-store`](/docs/packages/events-operations/event-store) — Pro
- [`@playstack/extension`](/docs/packages/identity/extension) — Pro
- [`@playstack/extension-react`](/docs/packages/identity/extension-react) — Pro
- [`@playstack/feedback`](/docs/packages/events-operations/feedback) — tier to be announced
- [`@playstack/feeds`](/docs/packages/content-intelligence/feeds) — Pro
- [`@playstack/feeds-next`](/docs/packages/content-intelligence/feeds-next) — Pro
- [`@playstack/fulfillment`](/docs/packages/commerce/fulfillment) — Pro
- [`@playstack/github-app`](/docs/packages/identity/github-app) — Pro
- [`@playstack/inventory`](/docs/packages/commerce/inventory) — Pro
- [`@playstack/licensing`](/docs/packages/identity/licensing) — Pro
- [`@playstack/licensing-issuer`](/docs/packages/identity/licensing-issuer) — Pro
- [`@playstack/local-first`](/docs/packages/infrastructure/local-first) — Pro
- [`@playstack/local-first-expo`](/docs/packages/infrastructure/local-first-expo) — Pro
- [`@playstack/local-first-indexeddb`](/docs/packages/infrastructure/local-first-indexeddb) — Pro
- [`@playstack/nest-accounts`](/docs/packages/identity/nest-accounts) — Pro
- [`@playstack/nest-analytics`](/docs/packages/events-operations/nest-analytics) — Pro
- [`@playstack/nest-audiences`](/docs/packages/communication/nest-audiences) — Pro
- [`@playstack/nest-audit`](/docs/packages/identity/nest-audit) — Pro
- [`@playstack/nest-catalog`](/docs/packages/commerce/nest-catalog) — Pro
- [`@playstack/nest-commerce`](/docs/packages/commerce/nest-commerce) — Pro
- [`@playstack/nest-entitlements`](/docs/packages/identity/nest-entitlements) — Pro
- [`@playstack/nest-event-store`](/docs/packages/events-operations/nest-event-store) — Pro
- [`@playstack/nest-feedback`](/docs/packages/events-operations/nest-feedback) — tier to be announced
- [`@playstack/nest-feeds`](/docs/packages/content-intelligence/nest-feeds) — Pro
- [`@playstack/nest-fulfillment`](/docs/packages/commerce/nest-fulfillment) — Pro
- [`@playstack/nest-github-app`](/docs/packages/identity/nest-github-app) — Pro
- [`@playstack/nest-inventory`](/docs/packages/commerce/nest-inventory) — Pro
- [`@playstack/nest-licensing`](/docs/packages/identity/nest-licensing) — Pro
- [`@playstack/nest-payments`](/docs/packages/commerce/nest-payments) — Pro
- [`@playstack/nest-registry`](/docs/packages/infrastructure/nest-registry) — Pro
- [`@playstack/payments`](/docs/packages/commerce/payments) — Pro
- [`@playstack/registry`](/docs/packages/infrastructure/registry) — Pro
- [`@playstack/registry-swift`](/docs/packages/infrastructure/registry-swift) — Pro
- [`@playstack/webhooks`](/docs/packages/communication/webhooks) — Pro

Every `@playstack/*` package ships at the same fixed version, whatever its tier, so a Free package and a Pro package always compose without version drift. Read [package access](/docs/packages#access-policy) for licensing and installation details.

## Plus packages

Source: https://playstack.dev/docs/plus.md

Plus is a planned tier between Free and Pro. No package is exclusive to it yet: every paid package is currently included in [Pro](/docs/pro), and Plus will take shape around what early teams ask for rather than around an artificial split of the catalog.

## What to use today

- Start with the [Free packages](/docs/free) for authentication, billing, mail, queues, storage, search, events and their framework bindings.
- Review the [Paid catalog](/docs/paid) for team accounts, commerce, licensing, the registry and the application UI, all included in Pro.
- [Request pricing access](/pricing) to hear about Plus first.

## Updates and CI

Source: https://playstack.dev/docs/cli/updates-ci.md

Ordinary artifact synchronization and drift checks remain offline. Explicit
package installation with `playstack add` and version updates with `playstack
update` may invoke the repository's package manager and use its configured
registry authentication. Update pull-request mode also interacts with the
configured Git host. Neither operation makes runtime providers part of an
artifact check.

## Preview and apply an update

```sh
npx playstack update --dry-run
npx playstack update
```

The CLI selects the package manager from the committed `packageManager` field or repository lockfile. It updates only installed `@playstack/*` dependencies and keeps their fixed versions aligned.

After the package-manager step, the CLI:

1. synchronizes managed Prisma, email, content, and translation artifacts;
2. refreshes the configured lint fragment and Skills corpus;
3. attempts safe three-way merges for ejected artifacts; and
4. reports conflicts without overwriting application-owned files.

## Open review branches

```sh
npx playstack update --pr
```

Pull-request mode requires a clean tracked worktree and a configured host adapter. It partitions changes by ownership:

| Review branch  | Contains                                                           |
| -------------- | ------------------------------------------------------------------ |
| Primary        | Fixed package versions and package-owned managed output.           |
| Ejected merge  | Clean application-owned merges, based on the primary branch.       |
| Conflict draft | One isolated artifact conflict without committed conflict markers. |

Security manifest flags label the affected review and always disable auto-merge. Clean ejected changes remain human-reviewed because the application owns their behavior.

Automated pull-request updates disable dependency lifecycle/build scripts during
both the frozen bootstrap install and the version update. The generated checkout
does not persist Git credentials, package-manager and ordinary Git processes do
not receive the GitHub mutation token, and separately scoped registry
authentication remains available for package resolution. Before installing
dependencies, the workflow records the runner's Node, selected package manager,
Git, and GitHub CLI executable paths and starts the exact CLI package entry
rather than a shared bin name. The CLI validates those paths, constrains child
command lookup, and removes inherited Git helper/configuration overrides.
Repository write authority is introduced only for those trusted executables, an
isolated hook-bypassed push of the validated commit object, and pull-request
operations run outside the worktree and pinned to the current `github.com`
repository. Authenticated Git and GitHub CLI calls use isolated configuration.
Ordinary local `playstack update` retains the repository's normal
package-manager build policy. Tokenless local PR updates preserve existing Git
and GitHub CLI authentication only for the exact trusted mutation calls, not
dependency processes.

## Configure scheduled updates

```json
{
  "updates": {
    "gitHost": "github",
    "schedule": "0 9 * * 1",
    "requiredCheck": "test",
    "autoMerge": false
  }
}
```

```sh
npx playstack ci init
```

`ci init` emits `.github/workflows/playstack-update.yml` from the committed policy. It records:

- the weekly cron schedule;
- the existing CI job required before merge; and
- whether eligible managed updates may auto-merge.

An existing workflow is never overwritten unless `--force` is explicit. The generated workflow runs the same reviewed update command rather than introducing a second update implementation.

## Doctor in ordinary CI

Scheduled updates and ordinary drift checks are separate concerns. Add the offline check to the repository's normal verification workflow:

```sh
npx playstack doctor --check
```

Interactive `playstack init` can offer that edit for an existing GitHub Actions job, but it prints the proposed change and asks independently before writing it.

For ownership behavior during an update, see [Ejection and merging](/docs/cli/ejection-merging).

## @playstack/delivery-history

Source: https://playstack.dev/docs/packages/communication/delivery-history.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/delivery-history` records cross-provider dispatch timelines and normalized feedback for `@playstack/delivery`. It is not an audit log, suppression authority, message-body archive, or provider dispatcher.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/delivery-history@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose

```ts
import { createDeliveryHistory } from '@playstack/delivery-history'
import { prismaDeliveryHistoryPersistence } from '@playstack/delivery-history/prisma'

const history = createDeliveryHistory({
  persistence: prismaDeliveryHistoryPersistence(prisma),
  clock,
  retentionMs: 90 * 24 * 60 * 60 * 1_000,
})

events.subscribe('delivery.send.dispatched', history)
await history.recordFeedback(normalizedProviderFeedback)
```

## Configuration and operations

| Property or operation | Purpose |
| --- | --- |
| `persistence` | Atomically records dispatches, appends feedback, runs cursor queries, and prunes rows. |
| `clock` | Provides the retention cutoff. |
| `retentionMs` | Required positive lifetime for retained history. |
| `recordFeedback(feedback)` | Applies an idempotent provider event by stable provider event ID. |
| `query(query)` | Returns up to 100 records using cursor pagination and indexed filters. |
| `prune()` | Removes rows older than the configured retention window. |

Bounced and complained states are terminal, opened implies delivered, and older feedback cannot move a record backward. Provider webhook normalization remains an application-edge responsibility.

`@playstack/delivery-history/prisma` supplies the persistence adapter and the `delivery-history.prisma` managed artifact. `@playstack/delivery-history/testing` supplies an in-memory implementation. Neither stores message bodies or raw destinations.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/delivery-history@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/delivery-history` | `./dist/index.d.ts` |
| `@playstack/delivery-history/errors` | `./dist/errors.d.ts` |
| `@playstack/delivery-history/prisma` | `./dist/prisma.d.ts` |
| `@playstack/delivery-history/testing` | `./dist/testing.d.ts` |
| `@playstack/delivery-history/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/local-first-expo

Source: https://playstack.dev/docs/packages/infrastructure/local-first-expo.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/local-first-expo` accepts an open Expo SQLite database and uses exclusive transactions so product state and synchronization records commit or roll back together.

```ts
const storage = createExpoSqliteLocalFirstStorage({ database })
const runtime = createExpoLocalFirstRuntime({
  client: { netInfo, appState },
  isOnline: () => netInfo.isConnected ?? true,
  isForeground: () => appState.currentState === 'active',
  subscribe: subscribeToMobileWakeEvents,
})
```

The application owns the database file, product schema, NetInfo and AppState dependencies, and background-execution policy. The runtime lease is process-local, so server idempotency remains required.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/local-first-expo@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/local-first-expo@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/local-first-expo` | `./dist/index.d.ts` |
| `@playstack/local-first-expo/package.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/local-first-expo/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-accounts

Source: https://playstack.dev/docs/packages/identity/nest-accounts.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-accounts` revalidates account membership and carries the resulting scope into NestJS requests.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-accounts@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose and order guards

```ts
PlaystackAccountsModule.forRoot({ accounts })

@UseGuards(PlaystackAuthGuard, PlaystackAccountGuard)
@AccountRoles('owner', 'admin')
@Get('/settings')
settings(@CurrentAccount() account: AccountResolution) {}
```

Run `PlaystackAuthGuard` first. The account guard requires an account-scoped session, calls the configured service’s `resolve(userId, accountId)` on every request, attaches `request.playstackAccount`, then enforces optional role metadata.

## Configuration and request bridge

| Surface | Contract |
| --- | --- |
| `NestAccountsOptions.accounts` | An object exposing `resolve(userId, accountId)`. |
| `AccountRequest.playstackSession` | Session claims supplied by the auth guard. |
| `AccountRequest.playstackAccount` | Validated `AccountResolution` attached by this guard. |
| `@AccountRoles(...roles)` | Optional allowlist checked after membership resolution. |

Use `forRootAsync({ imports, inject, useFactory })` for DI-based configuration. Guards and decorators are exported rather than installed globally so their order remains visible.

## Boundary

The adapter does not create memberships, infer tenancy from arbitrary request values, scope database clients, or replace domain authorization. It translates only already-defined auth and account contracts into Nest request state.

## Request-selected account strategy

Supply `resolveRequestAccount(context, session)` only after authenticating the request. The guard revalidates live membership, roles and named permissions; selection alone grants nothing. Already scoped sessions cannot select another account. Without this resolver, the default remains account-scoped sessions. Keep resource ownership checks inside each product operation.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-accounts@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-accounts` | `./dist/index.d.ts` |
| `@playstack/nest-accounts/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/accounts` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/auth` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-payments

Source: https://playstack.dev/docs/packages/commerce/nest-payments.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-payments` exposes an injectable payment service for normalized payment events, refunds, and reconciliation. Raw request verification remains in `@playstack/webhooks`.

The module requires explicit `payments:refund` and `payments:reconcile` authorization for staff operations and forwards the application operation context into transactional payment events. It installs no controllers and never exposes provider secrets or raw request bodies.

Compose the portable service first, then register it through `PlaystackPaymentsModule.forRoot()` or `forRootAsync()` when its dependencies live in Nest providers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-payments@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-payments@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-payments` | `./dist/index.d.ts` |
| `@playstack/nest-payments/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/payments` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/webhooks` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/rate-limit-redis

Source: https://playstack.dev/docs/packages/events-operations/rate-limit-redis.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/rate-limit-redis` implements the atomic `RateLimitStore` contract with one Redis Lua invocation.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/rate-limit-redis@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the store

```ts
import { RedisRateLimitStore } from '@playstack/rate-limit-redis'

const store = new RedisRateLimitStore(redis)
const limiter = createRateLimiter({ definitions, store, clock, keyHasher, environment })
```

## Client bridge

The client needs only the Node Redis-compatible structural surface:

```ts
interface RedisEvalClient {
  eval(
    script: string,
    options: { keys: readonly string[]; arguments: readonly string[] },
  ): Promise<unknown>
}
```

`store.client` exposes that exact injected instance for lifecycle and diagnostics.

## Atomic behavior

The production Lua script reads, refills, decides, and updates every bucket in one call. It uses Redis server time rather than the application timestamp, retains refill state without spending any bucket on denial, and derives key expiry from twice the time required to refill capacity with a one-minute minimum.

Core has already HMAC-protected every key; this adapter does not log or reinterpret subject identifiers. Redis errors and malformed decisions become `RateLimitUnavailableError` for the core policy’s explicit failure mode.

## Boundary

The application owns Redis topology, authentication, connection creation and shutdown, clustering policy, observability, and deployment. Policy definition and subject resolution remain in `@playstack/rate-limit`.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/rate-limit-redis@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/rate-limit-redis` | `./dist/index.d.ts` |
| `@playstack/rate-limit-redis/fixed-window` | `./dist/fixed-window.d.ts` |
| `@playstack/rate-limit-redis/ioredis` | `./dist/ioredis.d.ts` |
| `@playstack/rate-limit-redis/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/search-minisearch

Source: https://playstack.dev/docs/packages/content-intelligence/search-minisearch.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/search-minisearch` implements the same mapped search contract with fuzzy matching, prefix matching, suggestions, weighted fields, synonyms, arbitrary filters, and stable result ordering.

```ts
const search = createMiniSearchEngine(records, {
  document: mapSearchDocument,
  fields: { title: 10, tags: 6, body: 1 },
  fuzzy: { distance: 0.2, minTermLength: 4 },
  prefix: { enabled: true, lastTermOnly: true, minTermLength: 2 },
})
```

Only values placed in the mapped result can leave the index. The adapter snapshots input at construction and runs in both Node and Workers runtimes.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/search-minisearch@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/search-minisearch@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/search-minisearch` | `./dist/index.d.ts` |
| `@playstack/search-minisearch/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/search` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Pro packages

Source: https://playstack.dev/docs/pro.md

Pro includes 51 assigned paid packages: team accounts, commerce, licensing, the private registry, audiences, analytics, local-first sync and application UI compositions, on top of the [Free baseline](/docs/free). Packages awaiting tier assignment are not automatically included. Individual and Team plans differ in accounts and seats, not in which assigned packages they include.

- [`@playstack/accounts`](/docs/packages/identity/accounts) — Pro
- [`@playstack/accounts-react`](/docs/packages/identity/accounts-react) — Pro
- [`@playstack/admin-ui`](/docs/packages/ui/admin-ui) — Pro
- [`@playstack/analytics`](/docs/packages/events-operations/analytics) — Pro
- [`@playstack/analytics-react`](/docs/packages/events-operations/analytics-react) — Pro
- [`@playstack/app-ui`](/docs/packages/ui/app-ui) — Pro
- [`@playstack/atproto`](/docs/packages/identity/atproto) — Pro
- [`@playstack/audiences`](/docs/packages/communication/audiences) — Pro
- [`@playstack/audiences-react`](/docs/packages/communication/audiences-react) — Pro
- [`@playstack/audiences-resend`](/docs/packages/communication/audiences-resend) — Pro
- [`@playstack/audit`](/docs/packages/identity/audit) — Pro
- [`@playstack/catalog`](/docs/packages/commerce/catalog) — Pro
- [`@playstack/chakra`](/docs/packages/ui/chakra) — Pro
- [`@playstack/commerce`](/docs/packages/commerce/commerce) — Pro
- [`@playstack/content`](/docs/packages/content-intelligence/content) — Pro
- [`@playstack/content-next`](/docs/packages/content-intelligence/content-next) — Pro
- [`@playstack/delivery-history`](/docs/packages/communication/delivery-history) — Pro
- [`@playstack/devices`](/docs/packages/identity/devices) — Pro
- [`@playstack/entitlements`](/docs/packages/identity/entitlements) — Pro
- [`@playstack/event-store`](/docs/packages/events-operations/event-store) — Pro
- [`@playstack/extension`](/docs/packages/identity/extension) — Pro
- [`@playstack/extension-react`](/docs/packages/identity/extension-react) — Pro
- [`@playstack/feeds`](/docs/packages/content-intelligence/feeds) — Pro
- [`@playstack/feeds-next`](/docs/packages/content-intelligence/feeds-next) — Pro
- [`@playstack/fulfillment`](/docs/packages/commerce/fulfillment) — Pro
- [`@playstack/github-app`](/docs/packages/identity/github-app) — Pro
- [`@playstack/inventory`](/docs/packages/commerce/inventory) — Pro
- [`@playstack/licensing`](/docs/packages/identity/licensing) — Pro
- [`@playstack/licensing-issuer`](/docs/packages/identity/licensing-issuer) — Pro
- [`@playstack/local-first`](/docs/packages/infrastructure/local-first) — Pro
- [`@playstack/local-first-expo`](/docs/packages/infrastructure/local-first-expo) — Pro
- [`@playstack/local-first-indexeddb`](/docs/packages/infrastructure/local-first-indexeddb) — Pro
- [`@playstack/nest-accounts`](/docs/packages/identity/nest-accounts) — Pro
- [`@playstack/nest-analytics`](/docs/packages/events-operations/nest-analytics) — Pro
- [`@playstack/nest-audiences`](/docs/packages/communication/nest-audiences) — Pro
- [`@playstack/nest-audit`](/docs/packages/identity/nest-audit) — Pro
- [`@playstack/nest-catalog`](/docs/packages/commerce/nest-catalog) — Pro
- [`@playstack/nest-commerce`](/docs/packages/commerce/nest-commerce) — Pro
- [`@playstack/nest-entitlements`](/docs/packages/identity/nest-entitlements) — Pro
- [`@playstack/nest-event-store`](/docs/packages/events-operations/nest-event-store) — Pro
- [`@playstack/nest-feeds`](/docs/packages/content-intelligence/nest-feeds) — Pro
- [`@playstack/nest-fulfillment`](/docs/packages/commerce/nest-fulfillment) — Pro
- [`@playstack/nest-github-app`](/docs/packages/identity/nest-github-app) — Pro
- [`@playstack/nest-inventory`](/docs/packages/commerce/nest-inventory) — Pro
- [`@playstack/nest-licensing`](/docs/packages/identity/nest-licensing) — Pro
- [`@playstack/nest-payments`](/docs/packages/commerce/nest-payments) — Pro
- [`@playstack/nest-registry`](/docs/packages/infrastructure/nest-registry) — Pro
- [`@playstack/payments`](/docs/packages/commerce/payments) — Pro
- [`@playstack/registry`](/docs/packages/infrastructure/registry) — Pro
- [`@playstack/registry-swift`](/docs/packages/infrastructure/registry-swift) — Pro
- [`@playstack/webhooks`](/docs/packages/communication/webhooks) — Pro

[Request pricing access](/pricing) to hear when Pro subscriptions open.

Every `@playstack/*` package ships at the same fixed version, whatever its tier, so a Free package and a Pro package always compose without version drift. Read [package access](/docs/packages#access-policy) for licensing and installation details.

## @playstack/devices

Source: https://playstack.dev/docs/packages/identity/devices.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/devices` gives authentication, MFA, and notification features one shared registered-device identity without making any of those packages own it.

## Install

```sh
npm install @playstack/devices @playstack/core @playstack/events
```

## Compose the service

```ts
import { createDevices } from '@playstack/devices'

export const devices = createDevices({
  persistence,
  cipher: applicationCrypto,
  events,
  clock,
  ids,
  lastSeenWriteIntervalMs: 60 * 60_000,
})

await devices.registerDevice({
  id: installationId,
  userId,
  appId: 'synth',
  platform: 'ios',
  appVersion: '2.4.0',
})
```

## Configuration reference

| `DevicesOptions` property | Required | Purpose |
| --- | --- | --- |
| `persistence` | Yes | Transactional devices, encrypted push records, and revocation state. |
| `cipher` | Yes | Encrypts and decrypts push bearer credentials. |
| `events` | Yes | Publishes registered, trusted, and revoked lifecycle events. |
| `clock`, `ids` | Yes | Application-owned time and identifiers. |
| `lastSeenWriteIntervalMs` | No | Non-negative write throttle; defaults to one hour. |

## Device and push properties

Device registration accepts an application installation `id`, `userId`, `appId`, platform (`android`, `ios`, `macos`, or `web`), and optional name, model, OS version, and app version. Trust always has an absolute expiry and never slides when the device is seen.

Push registration accepts a device/user pair, transport (`apns`, `expo`, `fcm`, or `webpush`), credential, and optional prior push-token ID for atomic rotation. Web Push uses `{ endpoint, keys: { auth, p256dh } }`; other transports use an opaque string.

## Auth session validation

`assertActive(userId, deviceId)` resolves only while the device exists, belongs to the user, and is not revoked, so the service satisfies the `DeviceSessionValidator` contract of [`@playstack/auth`](/docs/packages/identity/auth) without an adapter.

```ts
const sessions = databaseSessions({ persistence, crypto, events, clock, ids, deviceValidator: devices, requireDevice: true })
events.registerInternal('devices.device.revoked', sessions.deviceRevocationHandler())
```

In NestJS, provide the service under `NEST_DEVICE_SESSION_VALIDATOR` and [`@playstack/nest-auth`](/docs/packages/identity/nest-auth) injects it into the session factory.

## Bridge contracts

| Contract | Key members |
| --- | --- |
| `SecretCipher` | `encrypt(plaintext)` and `decryptText(ciphertext)`; compatible with `@playstack/crypto`. |
| `DevicesPersistence` | Transaction, device lookup/save/list, push lookup/save/list, and bulk invalidation. |

The persistence transaction handle is also used by synchronous auth and audit handlers. Revocation must invalidate push records and publish `devices.device.revoked` atomically so auth can revoke device-backed sessions in the same transaction.

```ts
import { prismaDevicesPersistence } from '@playstack/devices/prisma'

const persistence = prismaDevicesPersistence(prisma)
```

The Prisma adapter serializes registration and locks token rotation and revocation rows. The managed, non-ejectable schema remains application-migrated. Test adapters are available from `@playstack/devices/testing`.

## Boundary

The package does not fingerprint visitors, pair devices, deliver notifications, or store auth sessions. Auth and MFA depend on its explicit validator/trust methods and events; devices remains independent of both.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/devices@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/devices` | `./dist/index.d.ts` |
| `@playstack/devices/errors` | `./dist/errors.d.ts` |
| `@playstack/devices/prisma` | `./dist/prisma.d.ts` |
| `@playstack/devices/testing` | `./dist/testing.d.ts` |
| `@playstack/devices/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/devices/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-delivery

Source: https://playstack.dev/docs/packages/communication/nest-delivery.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-delivery` binds the portable delivery service to a validated Nest queue consumer and unsubscribe controller. It does not configure providers, own templates, authenticate users, or render preference pages.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-delivery@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Configure

```ts
PlaystackDeliveryModule.forRoot({
  delivery,
  tokens: unsubscribeTokens,
})
```

Use `forRootAsync({ imports, inject, useFactory })` for Nest-managed composition. The module remains non-global.

## Worker and routes

Bind the `playstack.delivery.dispatch` queue job to the consumer selected by the application:

```ts
await deliveryConsumer.handle(job.data)
```

`PlaystackDeliveryConsumer` validates the untrusted job before calling the core worker operation.

The module also registers:

| Route | Behavior |
| --- | --- |
| `GET /delivery/unsubscribe/:token` | Verifies the token and returns a read-only render model. |
| `POST /delivery/unsubscribe/:token` | Requires RFC 8058 one-click data or `{ confirm: true }`, then adds the exact suppression. |

`tokens.verify()` is an application seam responsible for signature, purpose, and expiry validation. Inject `DELIVERY_SERVICE` for the core facade or `NEST_DELIVERY_OPTIONS` for the configured verifier.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-delivery@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-delivery` | `./dist/index.d.ts` |
| `@playstack/nest-delivery/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/delivery` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-inventory

Source: https://playstack.dev/docs/packages/commerce/nest-inventory.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-inventory` binds inventory administration to application authorization and operation context.

```ts
PlaystackInventoryModule.forRoot({
  inventory,
  authorize: (request, permission, resourceId) =>
    policy.allows(request, permission, resourceId),
  resolveContext: (request) => auditContext(request),
})
```

Availability reads are advisory; only the portable reservation command promises stock. A queue-neutral worker expires reservations, while the application continues to own routes, scheduling, queue selection, DTOs, and UI.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-inventory@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-inventory@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-inventory` | `./dist/index.d.ts` |
| `@playstack/nest-inventory/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/inventory` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-queues

Source: https://playstack.dev/docs/packages/infrastructure/nest-queues.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-queues` binds portable Playstack queues to NestJS dependency injection without hiding the selected provider.

```ts
PlaystackQueuesModule.forRoot({
  app: 'fontstack',
  environment: 'production',
  queues: {
    'font-processing': { driver: processingDriver, worker: processingWorker },
    publishing: { driver: publishingDriver, worker: publishingWorker },
  },
})
```

`@QueueHandler()` discovers existing injectable handlers, and `@InjectPlaystackQueue(name)` selects a stable named queue. An optional worker starts after discovery and closes with the application.

Progress, schedules, cancellation, counts, and bounded inspection remain available through the queue control surface. BullMQ concurrency, locks, stalled-job policy, flows, priorities, and rate limits remain native application options.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-queues@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Existing queues and strict readiness

BullMQ `>=5.16 <7` is supported; selected monitoring integrations retain their own peer restrictions. `assertBullMqRedisNoEviction(queue.client)` is a fail-closed startup/readiness check, distinct from the non-blocking dispatch observer. It fails if the policy is incompatible or cannot be verified.

The optional [Queuebert bridge](/integrations/queuebert) preserves reviewed native queues and processor statistics. Set a per-queue `expectedPrefix` for an existing physical namespace instead of renaming queues. The bridge does not start workers or authorize administrative HTTP. A job/processor migration still needs drain, cutover and rollback decisions.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-queues@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-queues` | `./dist/index.d.ts` |
| `@playstack/nest-queues/testing` | `./dist/testing.d.ts` |
| `@playstack/nest-queues/queuebert` | `./dist/queuebert.d.ts` |
| `@playstack/nest-queues/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/queues` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/prisma-runtime

Source: https://playstack.dev/docs/packages/foundation/prisma-runtime.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/prisma-runtime` selects the correct application-owned generated Prisma client for the current deployment runtime without wrapping Prisma.

## Install

```sh
npm install @playstack/core @playstack/prisma-runtime
```

Install the generated Prisma clients and database adapters required by your application separately.

## Compose runtime-specific clients

```ts
import { resolvePrismaClient, type PrismaRuntime } from '@playstack/prisma-runtime'
import { PrismaClient as NodePrismaClient } from '../generated/node/client'
import { PrismaClient as EdgePrismaClient } from '../generated/edge/client'

export function createDatabase(runtime: PrismaRuntime) {
  return resolvePrismaClient({
    runtime,
    node: () => new NodePrismaClient(),
    serverless: () => new EdgePrismaClient({ adapter: neonAdapter }),
    test: () => createEphemeralTestClient(),
  })
}

export const prisma = createDatabase(resolveApplicationRuntime())
```

Pass the selected native client directly to Prisma-backed package adapters:

```ts
const eventStore = createPrismaEventStore({ prisma })
const rateLimits = createPrismaRateLimitStore({ prisma })
```

## Configuration reference

| `ResolvePrismaClientOptions` property | Type | Required | Purpose |
| --- | --- | --- | --- |
| `runtime` | `edge \| node \| test \| worker` | Yes | Explicit runtime selected by the application. |
| `node` | `() => TClient` | For `node` | Creates the pooled standard client. |
| `serverless` | `() => TClient` | For `edge` and `worker` | Creates the HTTP or serverless-adapter client. |
| `test` | `() => TClient` | For `test` | Creates an isolated test client. |

The function returns `TClient` unchanged. A missing factory fails with `PrismaRuntimeError('invalid_config')`; a factory failure is wrapped as `client_creation_failed` with its original cause.

## Adapter boundary

This package defines no custom client bridge. The generic return type preserves the exact generated Prisma client, including extensions, transaction types, and native methods. Each `@playstack/*/prisma` adapter declares the smaller structural surface it consumes.

## Node PrismaPg dispatch fence

The optional `@playstack/prisma-runtime/prisma-pg` export `fencePrismaPgFactory`
fences transaction dispatch/closure for exact matching Prisma/client/adapter-pg
versions, independently of the portable root selector:

| Exact version | Qualified engine | Shutdown contract |
| --- | --- | --- |
| 6.19.3, 6.19.0 | Rust `library` | Stop admission, drain complete operations, await factory disposal, then disconnect. |
| 7.9.0, 7.9.1, 7.10.0, 7.10.1 | `client` | Closure/late-dispatch fence with explicit factory disposal. |

Ranges, mismatched versions, other drivers, Workers/edge and migration/shadow
databases are outside this contract. Verify actual installed versions and generated
engine; configuration values are host attestations, not automatic discovery.
Use one factory and exclusively owned pool per generated-client lifetime. Do not
retain unwrapped driver/transaction references or bypass closure with raw SQL.

Commit drains admitted work; rollback cancels queued work and waits for started
work. Unknown closure or lease-release outcomes quarantine admission. An exception
does not authorize replay or another provider send. Reconcile the original durable
identity instead of retrying every connection/commit failure.

For Prisma 6, attest `engineType: 'library'`,
`shutdownPolicy: 'drain-before-disconnect'` and `poolOwnership: 'exclusive'`.
`$disconnect()` is not cancellation and can hide disposal failure: the explicit
`factory.dispose()` result is authoritative. Supervise an owner whose disposal
fails; do not reopen its pool. Restart requires a fresh client/factory.

The 7.9.1 lane has native and packed ESM/CJS PostgreSQL qualification, including
lost commit acknowledgment and process termination. That does not establish an
unfenced defect on 7.9.1 from a warning observed on 7.10.0, or certify a consumer's
deployment. The fence supplies no tenant policy, outbox or automatic retries.

## Lifecycle ownership

The application resolves runtime configuration, memoizes clients where appropriate, owns connection pools, registers shutdown, generates schemas and clients, and manages migrations. The package reads no globals or environment variables and never calls `$disconnect`.

The exact 7.10.1 client/adapter pair also has native and packed ESM/CommonJS
PostgreSQL 18 fence and Delivery qualification, with strict types and an offline
frozen reinstall. It preserves delayed-write rollback, connection closure,
quarantine, shutdown and original dispatch authority. This does not extend the
separate transaction-abort classifier beyond its documented 7.9.1 profile or
qualify every Prisma patch. Publish and pin the refreshed runtime artifact before
adopting this addition; a beta.4 version label alone does not identify its bytes.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/prisma-runtime@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/prisma-runtime` | `./dist/index.d.ts` |
| `@playstack/prisma-runtime/prisma-pg` | `./dist/prisma-pg.d.ts` |
| `@playstack/prisma-runtime/transaction-aborts` | `./dist/transaction-aborts.d.ts` |
| `@playstack/prisma-runtime/errors` | `./dist/errors.d.ts` |
| `@playstack/prisma-runtime/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/rate-limit-prisma

Source: https://playstack.dev/docs/packages/events-operations/rate-limit-prisma.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/rate-limit-prisma` implements the atomic `RateLimitStore` contract in Postgres for applications that prefer their existing relational database over another service.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/rate-limit-prisma@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the store

```ts
import { PrismaRateLimitStore } from '@playstack/rate-limit-prisma'

const store = new PrismaRateLimitStore(prisma)
const limiter = createRateLimiter({ definitions, store, clock, keyHasher, environment })
```

`store.client` is the exact structural Prisma client supplied by the application.

## Atomic behavior

For each consume call, the adapter:

1. Creates missing buckets at full capacity.
2. Locks every row in lexicographic key order to avoid inconsistent lock ordering.
3. Refills and evaluates the complete group using application time.
4. Writes every resulting state in one transaction only when allowed.

A denied group consumes no bucket. Database or response-shape failures become `RateLimitUnavailableError`, allowing the core definition’s explicit open/closed policy to decide behavior.

## Rolling-window store

`PrismaRollingWindowRateLimitStore` implements the separate rolling-expiry/cooldown
policy from `@playstack/rate-limit/rolling-window`. Select the
`rate-limit.rolling-window.prisma` artifact (`playstack-rate-limit-rolling.prisma`)
and migrate it with the application's normal process. It does not reuse
token-bucket rows.

The store serializes first writes, locks subjects in order and reads PostgreSQL
`clock_timestamp()` after locks in one READ COMMITTED transaction. Returned
`evaluatedAtMs` keeps retry headers on the same clock. Configure client statement/
transaction bounds; ambiguous commits do not trigger automatic retry/fallback.

The host schedules `store.cleanupExpired(batchSize)`, default 1,000 and at most
10,000 rows, using database time and SKIP LOCKED. Active hit/cooldown state is
retained. This adds no package-owned timer or worker. Match the raw-query schema/
search path to the generated Prisma model.

The rolling adapter's disposable PostgreSQL evidence uses Prisma 6.12.0 and
PostgreSQL 17.11; it is not qualification of every Prisma driver/version.

## Prisma boundary

Compose the managed `playstack-rate-limit.prisma` artifact with `playstack database sync`, generate the application client, and run application-owned migrations. The structural client contract requires `$transaction`, bucket `createMany`/`update`, and parameterized raw query support.

The adapter does not generate or disconnect clients, select a Prisma runtime, own pools, or run migrations. Use [`@playstack/prisma-runtime`](/docs/packages/foundation/prisma-runtime) when an application explicitly selects among generated clients.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/rate-limit-prisma@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/rate-limit-prisma` | `./dist/index.d.ts` |
| `@playstack/rate-limit-prisma/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/rate-limit-prisma/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/search-next

Source: https://playstack.dev/docs/packages/content-intelligence/search-next.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/search-next` adapts both Next.js routers to the same `@playstack/search` source and protocol.

```ts
// app/api/search/route.ts
export const GET = createAppRouterSearchHandler(search, protocol)
```

```ts
// pages/api/search.ts
export default createPagesRouterSearchHandler(search, protocol)
```

Repeated Pages Router parameters remain repeated. App Router forwards its native abort signal, while Pages Router derives cancellation from request closure when available. Request-derived authentication, account context, route paths, and cache policy remain application-owned.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/search-next@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/search-next@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/search-next` | `./dist/index.d.ts` |
| `@playstack/search-next/app` | `./dist/app.d.ts` |
| `@playstack/search-next/pages` | `./dist/pages.d.ts` |
| `@playstack/search-next/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/search` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/skills

Source: https://playstack.dev/docs/packages/foundation/skills.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/skills` gives coding agents the architectural context needed to make judgement calls across Playstack packages. It is a versioned Markdown corpus—not a runtime agent framework, autonomous workflow, or replacement for deterministic lint and `playstack doctor` checks.

## Install the corpus

Install the Skills package and the CLI as development dependencies:

```sh
npm install --save-dev @playstack/skills @playstack/cli
npx playstack init
npx playstack skills sync
```

The default target is `.agents/skills`. Installed files remain in the repository so developers and agents can review the exact guidance in use.

## Configure targets and overrides

```json
{
  "$schema": "./node_modules/@playstack/cli/playstack.schema.json",
  "skills": {
    "targets": [".agents/skills"],
    "overrides": {
      "event-design": ".playstack-overrides/event-design"
    }
  }
}
```

| Property | Type | Purpose |
| --- | --- | --- |
| `skills.targets` | `string[]` | One or more repository-relative directories that receive the corpus. |
| `skills.overrides` | `Record<skillId, path>?` | User-owned skill directories that shadow a packaged skill by stable ID. |

An override must stay inside the repository, include its own `SKILL.md`, and use an ID declared by the installed package. It shadows the managed source without modifying it.

## Synchronize and verify

```sh
npx playstack skills sync
npx playstack skills sync --diff
npx playstack skills sync --check
npx playstack doctor --check
```

The CLI validates the package manifest and content hashes before copying anything. It records the package version, skill ID, source, file hashes, and aggregate content hash in `.playstack/skills.json`.

A clean second sync makes no changes. If a managed copy was edited locally, sync reports an unsafe conflict and refuses to overwrite it. Use an explicit override for repository-specific guidance; reserve `--force` for deliberately replacing a managed copy.

## Included skills

### `playstack-conventions`

Reviews integration boundaries for portable contracts, explicit dependencies, trace context, escape hatches, and standard adoption.

### `playstack-escalation`

Guides the progression from configuration, to replacing a seam, to ejecting package-owned source only when the earlier choices do not fit.

### `scope-review`

Examines multi-tenant ownership and lifecycle boundaries that cannot be proven by checking for a scope filter alone.

### `event-design`

Helps choose useful event names and granularity, including the boundary between transactional domain events and observational analytics events.

### `chakra-composition`

Reviews Chakra prop usage, recipe extraction, and whether a repeated pattern belongs locally, in a component, or in a shared theme.

### `extension-review`

Reviews browser-extension permissions, execution contexts, service-worker lifecycle, bundle weight, and common store-review traps.

### `structured-data`

Aligns JSON-LD, metadata, feeds, webmentions, and oEmbed representations around one application-owned content model.

## Manifest contract

`playstack.skills.json` is the package's public distribution contract. Schema version 1 declares:

| Property | Meaning |
| --- | --- |
| `packageVersion` | The exact corpus version being synchronized. |
| `skills[].id` | A stable lowercase kebab-case skill identifier. |
| `skills[].path` | The confined package-relative skill directory. |
| `skills[].files` | Ordered relative paths mapped to lowercase SHA-256 digests. |

The package has no runtime state, events, environment access, or application bridge. Installation and drift reporting belong to [`@playstack/cli`](/docs/cli).

## Boundary

Skills carry judgement that depends on product context. Deterministic violations—invalid wire values, missing trace metadata, undeclared events, unsafe extension permissions, or unscoped persistence—belong in lint rules, tests, and `doctor`. Keeping that split explicit prevents always-on guidance from spending context on checks the toolchain can enforce every time.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/skills@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/skills` | `./dist/index.d.ts` |
| `@playstack/skills/playstack.skills.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/skills/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Application UI

Source: https://playstack.dev/docs/packages/ui.md

The three UI packages are independently composable and do not install an authentication system, routes or provider clients.

| Package | Implemented surface |
| --- | --- |
| [Chakra](/docs/packages/ui/chakra) | Semantic tokens, neutral/warm presets and system composition. |
| [App UI](/docs/packages/ui/app-ui) | Shells, navigation, settings, collections, commands, Kanban and split layouts. |
| [Admin UI](/docs/packages/ui/admin-ui) | Account-admin shell, scoped members section and member table. |

These are preview compositions: they ship today, work with any Next application, and will keep growing. Prebuilt and custom routes sit side by side in ordinary Next routes, while your application keeps sessions, data fetching and authorization.

See [Application interfaces](/features/application-ui) and [Chakra integration](/integrations/chakra-ui).

## Architecture

Source: https://playstack.dev/docs/architecture.md

Playstack uses an inside-out package model. Portable contracts sit at the center; framework and infrastructure choices are attached at application-owned boundaries.

For example, your application can use the Storage package to save a file without putting S3-specific calls throughout its code. You choose and configure the storage provider in one place. A Nest adapter is only needed if you want that storage service supplied through Nest's dependency injection.

## Terms used in these guides

| Term | What it means |
| --- | --- |
| Domain package | The package that defines a capability, such as storage or accounts, without tying your application to a particular framework. |
| Adapter or provider | An implementation that connects a contract to a framework, database, or external service. |
| Composition root | The place in your application where you create services and connect their dependencies. |
| Operation context | Information about an operation, such as who initiated it and which request it belongs to. |
| Transactional event | An event whose handlers participate in the producing operation; failures can prevent that operation from completing. |
| Observational event | An event handled separately from the producing operation. Durable delivery requires explicit persistence and retry handling. |

You do not need every layer to get started. The [first storage example](/docs/getting-started) uses a domain package and an in-memory provider, with no framework or database setup.

## Dependency direction

```text
UI / HTTP / jobs
      |
framework adapters
      |
application composition
      |
domain packages + events
      |
database / queues / vendors
```

Dependencies point inward toward contracts. Runtime calls can flow outward through injected interfaces, but a domain package should not import the application, a framework adapter, or an infrastructure client.

## Event bridges

Event bridges are the standard seam for connecting domain activity to other capabilities. A bridge can publish to one or more sinks, transform an envelope for a broker, or remain in memory for tests. Events preserve operation context so downstream work can retain correlation and causation.

Keep the bridge in the application composition root. This makes fan-out, retries, observability, and delivery guarantees visible rather than implicit.

## Configuration

Adapters accept explicit configuration objects. Prefer an asynchronous module factory when configuration depends on application services or secrets. Pass schema names, client instances, table prefixes, queue names, and other deployment choices through those boundaries instead of relying on package globals.

## Testing

Test domain policy with in-memory ports. Test each adapter against its real boundary contract. Add a smaller number of application composition tests to prove that events, operation context, and configured clients cross package seams correctly.

This split keeps most tests fast while still exercising the places where packages meet.

## Composition references

- [Native and multi-account authentication](/docs/native-authentication): separate secure storage from device-wide refresh ownership and recovery.
- [Durable security decisions](/docs/durable-security-projections): commit revocation with audit intent, then repair downstream projections.
- [Data-only background sync hints](/docs/background-sync-hints): combine Devices and durable Delivery without an inbox or document payloads.

Each reference names what packages provide, what the host must own, and what its fixtures do not qualify.

## @playstack/lint

Source: https://playstack.dev/docs/packages/foundation/lint.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/lint` catches integration-boundary mistakes that can be identified statically. It complements repository conventions, type-level validation, and `playstack doctor`; it does not replace them.

## Install and configure

The plugin supports ESLint `^9.0.0 || ^10.0.0`, all six rules and the four presets from both ESM and CommonJS configs. Use a Node version supported by the ESLint major you select; the broader Playstack engine range does not override a tool's own engine requirements.

```sh
npm install --save-dev @playstack/lint eslint
```

```js
import { configs } from '@playstack/lint'

export default [
  configs.recommended,
  {
    ...configs.extension,
    files: ['apps/extension/src/background/**/*.{ts,tsx}'],
  },
  {
    ...configs.chakra,
    files: ['packages/playstack-ui/src/**/*.{ts,tsx}'],
    rules: {
      ...configs.chakra.rules,
      '@playstack/semantic-tokens-only': ['error', { tokens: ['fg.default', 'bg.canvas', 'border.default'] }],
    },
  },
]
```

`extension` and `chakra` deliberately omit `files`; consumers must scope them to the surfaces where those contracts apply. Use `configs.all` only when every source file belongs to all three domains.

## Presets and rules

| Preset        | Rules                                                              | Boundary                                                                             |
| ------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `recommended` | `no-process-env-in-core`, `no-consumer-internal-handler`           | Keeps portable core configuration explicit and transactional handlers package-owned. |
| `extension`   | `no-worker-memory-state`, `no-worker-long-timer`, `no-remote-code` | Enforces disposable MV3 worker state and bundled executable code.                    |
| `chakra`      | `semantic-tokens-only`                                             | Restricts selected style properties to an explicit semantic-token vocabulary.        |
| `all`         | All six rules                                                      | Combines the presets without assigning file scopes.                                  |

`no-worker-long-timer` reports dynamic delays and literal delays over 10 seconds by default. Configure `maxDelayMs` only when the extension has a documented lifecycle reason; longer work should use persisted intent and the alarms API.

`semantic-tokens-only` accepts `tokens` and `properties` allowlists. This lets an application own its theme vocabulary rather than inheriting hardcoded visual values from Playstack.

## CLI composition

```sh
npx playstack lint init --diff
npx playstack lint init
```

The [Playstack CLI](/docs/cli/lint-skills) can materialize a managed flat-config fragment from `playstack.json`. Direct ESLint composition remains supported when the application needs a custom file layout.

Rules expose no fixes unless a transformation can preserve semantics; the initial rule set reports only.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/lint@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/lint` | `./dist/index.d.ts` |
| `@playstack/lint/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `eslint` | `^9.0.0 \|\| ^10.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/mfa

Source: https://playstack.dev/docs/packages/identity/mfa.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/mfa` provides passkey-first step-up authentication, TOTP, recovery codes, recent-auth proofs, trusted-device decisions, and account policy without taking session ownership from `@playstack/auth`.

## Install

```sh
npm install @playstack/mfa @playstack/core @playstack/crypto @playstack/events
```

Install `@simplewebauthn/server` only when using the optional Node adapter.
Install `@playstack/devices` only when selecting its remembered-device bridge;
it is not required for passkeys, TOTP or recovery.

## Compose the service

```ts
import { createMfa } from '@playstack/mfa'
import { simpleWebAuthnPasskeys } from '@playstack/mfa/webauthn'

export const mfa = createMfa({
  persistence,
  crypto: applicationCrypto,
  webCrypto: globalThis.crypto,
  recoveryHasher: passwordGradeRecoveryHasher,
  passkeys: simpleWebAuthnPasskeys(),
  limiter: mfaAttemptLimiter,
  events,
  clock,
  ids,
  issuer: 'Example App',
  rpId: 'app.example.com',
  rpName: 'Example App',
  origins: ['https://app.example.com'],
  // Optional: trustedDevices: devices,
})
```

## Configuration reference

| `MfaOptions` property | Required | Purpose |
| --- | --- | --- |
| `persistence` | Yes | Transactional credentials, challenges, recovery sets, and replay locks. |
| `crypto` | Yes | Encryption plus purpose-bound recent-auth signing and verification. |
| `webCrypto` | Yes | TOTP HMAC and secure random bytes. |
| `recoveryHasher` | Yes | Password-grade recovery-code hash and verify bridge. |
| `passkeys` | Yes | WebAuthn options and ceremony verification bridge. |
| `limiter` | Yes | Fail-closed attempt limiter by method and user. |
| `events`, `clock`, `ids` | Yes | Typed security events and deterministic primitives. |
| `issuer` | Yes | TOTP issuer, maximum 200 characters. |
| `rpId`, `rpName` | Yes | WebAuthn relying-party ID and display name. |
| `origins` | Yes | One or more exact HTTPS WebAuthn origins. |
| `trustedDevices` | No | Device trust lookup and grant bridge. |
| `challengeTtlMs` | No | Positive WebAuthn challenge lifetime; defaults to five minutes. |
| `recentAuthTtl` | No | Signed proof lifetime; defaults to `5m`. |
| `recoveryCodeCount` | No | Codes per set; defaults to 10 and cannot exceed 50. |
| `maxTrustedDeviceTtlMs` | No | Trust-grant ceiling; defaults to 30 days. |
| `preparationTtlMs` | No | One-attempt preparation lifetime, 1–300 seconds; defaults to 60 seconds. |
| `authorizeOperation` | No | Read-only current product authorization on the identity transaction before MFA child locks. |

## Passkey bridge

`PasskeyAdapter` builds registration/authentication options and verifies both ceremonies against the service-supplied challenge, exact origins, RP ID, credential public key, and sign count. The service owns challenge lifetime and single use, credential uniqueness, discovery, and persisted sign counts.

The included `simpleWebAuthnPasskeys` adapter defaults user verification to `required`, prefers discoverable credentials, requests no attestation, hashes internal user IDs into fixed-size handles, and omits the allowlist for discoverable sign-in. Use `userVerification: 'preferred'` only if another factor provides verification.

## Other bridge contracts

| Contract | Key members |
| --- | --- |
| `MfaPersistence` | Transactional credentials, single-use challenges, recovery sets, and locked recovery-code consumption. |
| `MfaCrypto` | Random tokens, encryption, and purpose-bound sign/verify; compatible with `@playstack/crypto`. |
| `RecoveryCodeHasher` | `hash(code)` and `verify(hash, code)` using a password-grade algorithm. |
| `MfaAttemptLimiter` | `consume(method, userId)` returning `allowed` and optional `retryAt`. |
| `TrustedDeviceManager` | `isTrusted` and `trust`; implemented structurally by `@playstack/devices`. |

## Security behavior

- TOTP uses HMAC-SHA1, a 30-second period, six digits, a one-step window, encrypted secrets, and counter replay prevention.
- Recovery codes are displayed once, activated only after acknowledgement, consumed atomically, and retained after use.
- Regenerating codes does not invalidate the previous set until the replacement is acknowledged.
- Sign-count regressions are persisted for detection but do not hard-lock synced passkeys.

`MfaService` implements auth’s `StepUpVerifier`. Register auth’s MFA-change handler for both enrollment and disable events so credential changes revoke existing sessions atomically. Route `mfa.verification.locked` to security notifications as a transactional event that bypasses preferences.

## Transaction-bound workflows

Credential, challenge and recovery mutations accept a caller transaction as the
last argument, after an optional operation context where present. They join that
live handle; they do not commit an independent MFA unit of work. Publish results
only after the outer transaction commits, and propagate failures for rollback.

For expensive verification, call `prepareVerifyTotp`,
`prepareVerifyRecoveryCode`, `prepareFinishPasskeyAuthentication`,
`prepareConfirmTotpEnrollment`, `prepareFinishPasskeyRegistration` or
`prepareRegenerateRecoveryCodes` **before opening** the protected transaction.
The returned server-only capability exposes `commit(transaction)` and permits
one attempt, including rollback. Short snapshot reads finish before expensive
hash/passkey work. Commit rechecks current credentials, challenge/set/code
identity, replay state and original deadlines under identity-first locks.
A preparation's TTL cannot extend a challenge, TOTP window or recent-auth proof.

Proof signing and factor consumption share the commit transaction; signing or
required evidence failure restores replay authority. Enrollment/disable events
can revoke Auth sessions on that same handle through the existing MFA-change
handler. Configure `authorizeOperation` for current application identity/policy
checks or perform an active-user check on the caller transaction. MFA does not
own account or suspension policy.

Standalone failed passkey ceremonies retain challenge burning. Prepared
validation consumes no challenge until commit. Attempt budgets and denied-attempt
evidence remain preparation admission effects. Direct caller methods may do
expensive work while the caller holds locks; prepare first when that matters.
Existing recent-auth proofs remain stateless and reusable until expiry. A proof
leaked before outer rollback is not cryptographically revoked, so keep results
private until commit. External remembered-device grants are not implicitly atomic
with MFA, and no host routes or step-up flow are enabled by this API.

## Persistence and testing

`@playstack/mfa/prisma` provides locked PostgreSQL persistence. The required
`lockUser` port serializes the identity before MFA rows; native adapters reject
root/autocommitting handles. Custom adapters must implement equivalent locking.
The memory adapter has no identity directory and uses its single-writer profile.
No schema change or migration is required for caller composition. Its managed,
security-critical fragment is non-ejectable and declares its reverse `User`
relations as model extensions. `playstack database sync` composes those fields
into the selected managed auth artifact and validates the complete Prisma
relation graph before writing. If an application owns the `User` model, it must
supply the extension target explicitly. Applications still own migrations.
`@playstack/mfa/testing` provides in-memory persistence and deterministic bridge
fakes.

## Boundary

MFA does not identify devices, issue sessions, choose account policy, or send lockout notifications. The Workers-safe core accepts any passkey adapter; Node-only WebAuthn support remains an optional subpath.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/mfa@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/mfa` | `./dist/index.d.ts` |
| `@playstack/mfa/errors` | `./dist/errors.d.ts` |
| `@playstack/mfa/prisma` | `./dist/prisma.d.ts` |
| `@playstack/mfa/testing` | `./dist/testing.d.ts` |
| `@playstack/mfa/webauthn` | `./dist/webauthn.node.d.ts` |
| `@playstack/mfa/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/mfa/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/crypto` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/devices` | `0.1.0-beta.4` | Optional; only for the entry points that use it. |
| `@simplewebauthn/server` | `^13.3.2` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-fulfillment

Source: https://playstack.dev/docs/packages/commerce/nest-fulfillment.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-fulfillment` exposes authorized administration and worker services without choosing routes, queues, or a dashboard.

`PlaystackFulfillmentAdminService` redacts encrypted address storage from ordinary reads and requires a distinct `fulfillment:address:read` permission before decryption. Write operations carry resolved staff context into the portable transition record.

`PlaystackFulfillmentWorker.process(job, context)` handles start, ship, deliver, fail, and cancel commands. Call it from `@playstack/queues`, BullMQ, Inngest, or another worker without coupling the domain to a transport.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-fulfillment@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-fulfillment@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-fulfillment` | `./dist/index.d.ts` |
| `@playstack/nest-fulfillment/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/fulfillment` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-search

Source: https://playstack.dev/docs/packages/content-intelligence/nest-search.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-search` registers named search sources and protocols while leaving route ownership, guards, authorization, and rate limits with the application.

```ts
PlaystackSearchModule.forRoot({
  searches: {
    docs: {
      source: docsSearchSource,
      protocol: docsSearchProtocol,
      health: () => searchIndex.check(),
    },
  },
})
```

An injected endpoint accepts Nest's parsed query and returns portable status, headers, and JSON. The application controller applies that result through Express or Fastify. No public route is registered implicitly.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-search@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-search@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-search` | `./dist/index.d.ts` |
| `@playstack/nest-search/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/search` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-storage

Source: https://playstack.dev/docs/packages/infrastructure/nest-storage.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-storage` registers multiple named `@playstack/storage` drivers without choosing their providers, credentials, buckets, or shutdown policy.

```ts
PlaystackStorageModule.forRoot({
  stores: {
    publicFonts: { driver: publicFonts, prefix: 'fonts' },
    privateFiles: { driver: privateFiles, prefix: 'private' },
    packages: { driver: packageStorage },
  },
})
```

Inject a driver with `@InjectStorage('packages')`. Each resolved store retains its portable capabilities and exact native client. Async registration declares stable store names before its factory runs so Nest injection tokens remain deterministic.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-storage@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-storage@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-storage` | `./dist/index.d.ts` |
| `@playstack/nest-storage/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/storage` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/rate-limit-do

Source: https://playstack.dev/docs/packages/events-operations/rate-limit-do.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/rate-limit-do` maps the atomic rate-limit storage contract onto Cloudflare Durable Objects while keeping policy portable.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/rate-limit-do@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Bind and compose

Export `PlaystackRateLimitDurableObject` from the Worker, bind its namespace, then construct the client-side store:

```ts
import {
  DurableObjectRateLimitStore,
  PlaystackRateLimitDurableObject,
} from '@playstack/rate-limit-do'

export { PlaystackRateLimitDurableObject }

const store = new DurableObjectRateLimitStore({
  namespace: env.PLAYSTACK_RATE_LIMIT,
  routeBucket: (bucket) => `account-group:${resolveGroup(bucket.key)}`,
})
```

## Store options

| Property | Contract |
| --- | --- |
| `namespace` | Structural `idFromName(name)` and `get(id).fetch(...)` Durable Object namespace. |
| `routeBucket` | Maps each compiled bucket to a non-empty deterministic object name. |

Every bucket in a multi-subject definition must route to the same object. The adapter checks this before any storage request. Single-bucket definitions may route directly from the bucket key; multi-bucket policies need an application-defined common group.

The Durable Object evaluates and writes the entire group within one storage transaction and uses application time because its storage has no Redis-style server-time command.

## Routing considerations

Object naming determines both atomicity and load distribution. Review common-group routes for hot spots, and never route related buckets to separate objects merely for distribution—the operation would no longer be atomic.

## Boundary

The application owns Worker bindings, object names, environment configuration, deployment, and observability. The package supplies the class and structural client adapter, not Cloudflare project configuration.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/rate-limit-do@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/rate-limit-do` | `./dist/index.d.ts` |
| `@playstack/rate-limit-do/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/webhooks

Source: https://playstack.dev/docs/packages/communication/webhooks.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/webhooks` verifies exact request bytes, claims a provider event once, and inserts its normalized event into an application queue in the same transaction. It also exposes a small outbound dispatch contract for observational event routes. It does not provide event-store replay or provider business logic.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/webhooks@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose

```ts
import { createWebhooks, githubWebhookProvider, stripeWebhookProvider } from '@playstack/webhooks'
import { prismaWebhookPersistence } from '@playstack/webhooks/prisma'

const webhooks = createWebhooks({
  providers: [stripeWebhookProvider({ secret, crypto, clock }), githubWebhookProvider({ secret: githubSecret, crypto, clock })],
  persistence: prismaWebhookPersistence(prisma),
  queue: applicationWebhookOutbox,
})

const result = await webhooks.receive(provider, {
  headers: request.headers,
  body: request.rawBody,
})
```

## Bridge contracts

| Contract              | Boundary                                                                  |
| --------------------- | ------------------------------------------------------------------------- |
| `WebhookProvider`     | Verifies and parses exact raw bytes into a normalized provider event.     |
| `WebhookPersistence`  | Transactionally claims the provider and event ID once.                    |
| `WebhookQueue`        | Inserts the verified event using the same transaction kind.               |
| `WebhookHmacVerifier` | Narrow SHA-256 verification contract compatible with `@playstack/crypto`. |

The built-in Stripe and GitHub providers reject duplicate or ambiguous signature headers and malformed fields, including extra `=` separators. They verify and parse the same captured request bytes. Custom providers can retain the normalized payload and raw request as escape hatches. Unknown event types belong to application handlers and do not fail receipt.

`@playstack/webhooks/prisma` supplies a non-ejectable, security-critical receipt artifact and a structurally typed persistence adapter. Queue storage remains application-owned because it must share the application's transactional outbox.

See the [Stripe](/integrations/stripe) and [GitHub](/integrations/github) integration guides for provider-specific composition.

## Send outbound webhooks

Use `createOutboundWebhookHandler()` to map an accepted Playstack event to one or more configured endpoints. Endpoint URLs, authorization headers, and signing keys stay in live application configuration rather than domain events or queued payloads.

| Dispatcher                      | Use                                                                                                        |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `@playstack/webhooks/direct`    | One-shot HTTPS delivery for development or small application hooks, with optional exact-body signing.      |
| `@playstack/webhooks/durable`   | Self-hosted delivery engine with injected atomic persistence, ordered endpoint leases, bounded retry, and history queries. |
| `@playstack/webhooks/relaypath` | Relaypath-managed signing, retries, fan-out, endpoint health, and delivery history.                        |

The direct adapter rejects redirects and does not persist, retry, or record delivery. The durable adapter claims at most one ordered record per endpoint and classifies permanent versus retryable failures. Relaypath receives the source event ID as its idempotency key and only a managed endpoint ID—not the direct URL or secret headers.

The current direct contract projects a bounded `Retry-After` into a validated
absolute `WebhooksError.retryAt`; provider bodies and arbitrary headers are not
exposed. Durable retries add bounded local jitter without shortening that provider
deadline. `maxAttempts` defaults to 10 durable claims, including crash recovery;
`maxAgeMs` defaults to 24 hours from admission. Persistence must atomically
increment the claim count and fence every completion with an unexpired lease.
No new send starts at/after expiry or beyond the budget. In-flight sends can still
finish later, and uncertain remote acknowledgments can duplicate delivery.

`PrismaOutboundWebhookPersistence` from `@playstack/webhooks/durable/prisma`
provides namespaced, immutable event-bound PostgreSQL outbound storage. Select and
migrate the optional `webhooks.outbound.prisma` artifact. It serializes endpoint
FIFO, fences completion by unexpired database-clock leases and supplies bounded,
scope/filter-bound history cursors. `compactTerminal({ completedBefore, limit,
endpointId? })` removes payloads from eligible terminal rows in atomic bounded
batches, retaining immutable binding digests and identifiers for replay refusal.
Compacted history uses the payload-free `OutboundWebhookCompactedDelivery`
variant; replay never restores payloads or sends again. Apply the current reviewed
artifact migration before activating compaction. Retained metadata/gates are not
complete erasure or bounded metadata retention. Optional shared admission and
authorized recovery are described below; neither makes an uncertain send safe to
repeat. The inbound Prisma receipt adapter is **not** outbound delivery storage. An
injected test store or a passing transport test does not qualify production Slack
or Discord notifications; supply and verify actual persistence and provider
composition before enabling them.

An audience confirmation can therefore notify Slack or Discord by routing `audiences.subscription.confirmed` to a formatter and dispatcher without coupling the Audiences package to either service.

### Slack and Discord profiles

Root exports `createSlackIncomingWebhookDispatcher` and
`createDiscordWebhookDispatcher` take immutable `{ endpoints, fetch, formatText }`
configuration. `endpoints` maps opaque IDs to server-owned secret URLs; per-send
URL/header overrides are rejected. The host formatter explicitly projects only
permitted text, such as aggregate counts or opaque IDs. Slack uses an escaped
literal profile (up to 4,000 UTF-16 units before/after escaping); Discord limits
content to 2,000 units, suppresses mentions and forces `wait=true`.

Use native `createNodeWebhookFetch` with exact provider host/443 policy. These
profiles accept only HTTP 200 and propagate bounded `Retry-After` deadlines to
durable retries. Missing/invalid 429 metadata refuses rather than guessing an
early retry. The generic transport discards bodies and supplies no provider
message receipt or body acknowledgment.

For Discord body-only retry metadata, explicitly inject
`createNodeDiscordWebhookFetch` from `@playstack/webhooks/node`. It accepts only
canonical Discord HTTPS webhook URLs and reads at most 4 KiB of uncompressed
429 JSON under the whole-operation deadline. It validates `retry_after` and
global-limit hints without exposing response bodies or bucket identifiers.
Competing valid deadlines use the later minimum; malformed or incomplete
metadata refuses rather than falling back to an earlier retry. Slack and the
generic transport retain their body-discard behavior. Qualify live provider and
runtime behavior before activation; none of these profiles guarantees exactly-once delivery.

### Shared route and global admission

`createCoordinatedWebhookDispatcher` from `@playstack/webhooks/coordination`
wraps a dispatcher with shared route/global permits and persistent 429
cooldowns. Compose `PrismaOutboundWebhookCoordination` from
`@playstack/webhooks/coordination/prisma` and explicitly migrate
`webhooks.coordination.prisma`. Use trusted opaque group IDs shared by every
participating worker; map actual provider credentials/resources and egress
identity, not just application list or tenant boundaries.

Admission acquires short database locks, not a transaction spanning provider I/O.
Native Direct and pinned Node transports recheck the permit before submission;
custom transports must honor `context.beforeSubmission`. Cooldowns survive
restarts only after their write commits. A crash or uncertain database write
after observing a provider deadline requires an operational pause and reviewed
reconciliation; permit expiry cannot reconstruct a lost deadline. Configure
bounded database timeouts and a lease longer than the qualified send deadline.
This coordinates participating senders, not unrelated clients or discovered
provider quotas, and does not recall an already submitted request.

### Authorized inspection and recovery

`PrismaOutboundWebhookRecovery` from `@playstack/webhooks/recovery/prisma`
supplies masked inspection, history and revision-bound reconciliation. Select
`webhooks.outbound.prisma` and `webhooks.outbound-recovery.prisma`, and supply
transaction-bound operator authorization, independent evidence verification and
safe Audit/Events recording. No recovery HTTP controller is installed for you.

`retain` parks an unknown outcome without asserting non-delivery; `confirm`
requires verified delivered evidence. Neither sends or resets the original
attempt/age budget. Optional `retry` additionally requires current destination
authorization, independent non-delivery evidence and fencing of every prior
sender. The package supplies no Slack/Discord evidence source establishing those
conditions: keep administrative resends unavailable without a qualified one.
Timeouts, absent receipts and operator assertions are not non-delivery evidence.
Retained journals, identifiers and tombstones need a separate reviewed lifecycle;
compaction and reconciliation are not complete erasure.

### Editable settings and durable routing

`WebhookIntegrationSettings` from `@playstack/webhooks/settings` and
`PrismaWebhookIntegrationSettingsPersistence` from
`@playstack/webhooks/settings/prisma` provide encrypted Slack/Discord settings,
masked reads, scoped event routing and expected-revision rotation/enable/disable/
revocation. Explicitly compose and migrate `webhooks.settings.prisma`; durable
dispatch also needs `webhooks.outbound.prisma`. Supply native Crypto and mandatory
operator/worker authority and safe Audit callbacks joining the original transaction.
Opaque IDs are not credentials; expose no secret-resolution endpoint to browsers.

`route(listId, fullEvent, workerPrincipal)` retains the original fan-out and full
event digest, including empty fan-out. Replays cannot expand to newly enabled or
replaced destinations. Private resolution requires that receipt and current
authority/state/revision. The root `createWebhookIntegrationTargetResolver`
composes this with native provider dispatchers, an explicit approved text formatter,
trusted event-to-list projection and injected pinned transport. Select exactly one
of static `targets` or dynamic `targetResolver` in the durable engine.

Lookup is bounded and repeated before dispatch. Config changes invalidate old
queued revisions; a snapshot already resolved may still send to its old URL, never
the replacement. Route receipts commit separately from outbound enqueue: recover
the same event through a durable source outbox. Retained IDs/digests/tombstones are
not complete erasure or anonymous data. Operator recovery and live provider
qualification remain separate work.
Webhook failure must remain independent of consent signup/mail.

Optional `@playstack/webhooks/settings/nest` provides
`WebhookIntegrationSettingsModule.forRoot` with mandatory operator guard, trusted
principal, exact Origin/session-bound CSRF and native mutation-rate admission.
Strict path/body scope, masked responses and fixed errors do not replace host
authentication; no browser secret-resolution route is exposed.

Native `admitTestSend` requires explicit bounded admission and an original-handle
Events/outbox enqueue. Authority/current revision, quota, routing receipt, outbox
intent and safe Audit commit together. Tests use fixed server text and a five-minute
profile, not a caller-supplied URL/payload. HTTP accepted is not delivered. Workers
recover the original event/key and reauthorize before sending; repeat requests are
new rate-admitted intents. Complete erasure, administrative recovery and deployed
provider qualification remain separate gates.

## Customer-provided outbound URLs

HTTPS and redirect rejection alone are not an SSRF boundary. For Node public-endpoint delivery, inject `createNodeWebhookFetch` from `@playstack/webhooks/node` into `createDirectWebhookDispatcher` from `@playstack/webhooks/direct`. Configure the dispatcher's timeout; direct transport use must supply a deadline signal.

The transport validates all DNS answers, rejects non-public/special-use addresses, pins an approved address to the TLS lookup and preserves hostname/certificate verification. It resolves each attempt afresh, uses no shared agent/proxy, rejects redirects and defaults to port 443. Optional `allowedHosts`/`allowedPorts` never bypass address validation. Request bodies default to 1 MiB, response headers are capped at 16 KiB and response bodies are discarded.

Keep your infrastructure egress controls and deployment DNS/TLS tests in place; the transport's address checks complement them rather than replace them. Private-network or proxied delivery needs an explicitly owned transport.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/webhooks@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/webhooks` | `./dist/index.d.ts` |
| `@playstack/webhooks/errors` | `./dist/errors.d.ts` |
| `@playstack/webhooks/prisma` | `./dist/prisma.d.ts` |
| `@playstack/webhooks/direct` | `./dist/direct.d.ts` |
| `@playstack/webhooks/node` | `./dist/node.d.ts` |
| `@playstack/webhooks/durable` | `./dist/durable.d.ts` |
| `@playstack/webhooks/durable/prisma` | `./dist/outbound-prisma.d.ts` |
| `@playstack/webhooks/coordination` | `./dist/coordination.d.ts` |
| `@playstack/webhooks/coordination/prisma` | `./dist/coordination-prisma.d.ts` |
| `@playstack/webhooks/recovery` | `./dist/recovery.d.ts` |
| `@playstack/webhooks/recovery/prisma` | `./dist/recovery-prisma.d.ts` |
| `@playstack/webhooks/settings` | `./dist/settings.d.ts` |
| `@playstack/webhooks/settings/prisma` | `./dist/settings-prisma.d.ts` |
| `@playstack/webhooks/settings/nest` | `./dist/settings-nest.d.ts` |
| `@playstack/webhooks/relaypath` | `./dist/relaypath.d.ts` |
| `@playstack/webhooks/testing` | `./dist/testing.d.ts` |
| `@playstack/webhooks/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Optional; only for the entry points that use it. |
| `rxjs` | `^7.0.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Compatibility and support

Source: https://playstack.dev/docs/compatibility.md

Playstack packages share one release version. The ranges below summarize compatibility targets, not evidence that every version/provider combination has been tested. Check the installed package's peer requirements and adapter-specific qualification notes before choosing a deployment stack.

## Supported versions

| Surface                          | Supported                                                     |
| -------------------------------- | ------------------------------------------------------------- |
| Node.js                          | 20, 22 and 24; new server projects should start on 22 or newer |
| NestJS bindings (`nest-*`)       | Nest 10 and 11                                                |
| React bindings (`*-react`)       | React 18.2 and 19                                             |
| Next.js bindings (`*-next`)      | Next 14.1 through 16; Cache Components need Next 16           |
| Chakra compositions              | Chakra UI 3 with Emotion 11, for React DOM                     |
| Managed Prisma schema fragments  | Prisma 6.7 through 7                                          |
| BullMQ queue adapter             | BullMQ 5.16 through 6                                         |
| ESLint plugin                    | ESLint 9 and 10                                               |

Provider SDKs (Stripe, Resend, Cloudflare, push transports and others) keep their own peer ranges, listed on each integration page. A tool's own engine requirement, such as the Node version an ESLint major needs, applies alongside the Playstack range.

## What "available" means

- **Available** means an implementation is present in this documentation snapshot, not that registry publication or production qualification is complete. Check [preview access](/docs/packages#access-policy) before installing.
- **Preview** means an initial composition ships with explicit limitations, typically a UI composition or a new adapter.
- **Planned** means a capability has a name and a direction but no published package yet.

The [status page](/status) lists every package and adapter with its current label.

## Validate your adoption

Playstack packages ship the runtime code, in-memory test doubles and adapter conformance helpers you need to prove a composition in your own application. Before cutover:

- Run the persistence conformance helper from each package's `testing` entry point against a disposable copy of your real database, so transaction handles, locking and rollback are exercised where they will run.
- Keep the required adapters and registration checks on the same physical transaction as the domain calls, not merely the same transaction label.
- Exercise provider integrations against a sandbox account. Package tests use transport doubles; your deployment's egress, DNS and TLS behavior is yours to verify.
- Never point a mutating conformance suite at production data.

If something in this table does not match what you run, [send feedback](/docs/cli#feedback) or ask on the pricing access list; compatibility gaps are prioritized ahead of new features.

## @playstack/api-keys

Source: https://playstack.dev/docs/packages/identity/api-keys.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/api-keys` authenticates customer and integration callers with opaque credentials that are shown once and stored only as indexed SHA-256 digests.

Issuance, verification, rotation, revocation and family revocation can join a
caller-owned transaction through their optional third argument, after operation
context. `list(owner, transaction?)` reads pending writes on that handle. This
lets application attribution, key changes and transactional audit/outbox evidence
commit together. Propagate failures to the outer transaction and expose a new
secret only after commit; product authorization and cross-database handoffs
remain application responsibilities. Custom persistence adapters must implement
`assertTransaction` and transaction-aware listing. No schema migration is needed.

## Install

```sh
npm install @playstack/api-keys @playstack/core @playstack/events
```

## Compose and issue

```ts
import { createApiKeys } from '@playstack/api-keys'

export const apiKeys = createApiKeys({
  persistence,
  events,
  crypto: globalThis.crypto,
  clock,
  ids,
  product: 'bm',
  scopes: ['spans:read', 'spans:write'],
  maxKeyRateLimitCapacity: 1_000,
})

const { secret, key } = await apiKeys.issue({
  owner: { type: 'account', id: accountId },
  name: 'Production ingest',
  environment: 'live',
  scopes: ['spans:write'],
  createdBy: userId,
  rateLimitOverride: { capacity: 500 },
})
```

Return `secret` once. The stored record contains only its product/environment prefix, hash, and last four hexadecimal characters.

## Service configuration

| `ApiKeysOptions` property | Required | Purpose                                                                |
| ------------------------- | -------- | ---------------------------------------------------------------------- |
| `persistence`             | Yes      | Transactional lookup, locking, listing, and retained revocation state. |
| `events`                  | Yes      | Emits typed issue, use, and revocation events.                         |
| `crypto`                  | Yes      | Web Crypto `subtle` and `getRandomValues`.                             |
| `clock`, `ids`            | Yes      | Application-owned time and identifiers.                                |
| `product`                 | Yes      | Lowercase 2–16 character key prefix.                                   |
| `scopes`                  | Yes      | Complete allowlist of canonical product scopes.                        |
| `maxKeyRateLimitCapacity` | Yes      | Ceiling for any per-key capacity override.                             |
| `lastUsedWriteIntervalMs` | No       | Usage-write throttle; defaults to one hour.                            |
| `format` | No | Secret byte length and complete owner/environment prefix table. |

By default, secrets have the form `<product>_<live|test>_<32 hex characters>`
and contain 128 bits of CSPRNG entropy. For a product requiring 32-byte secrets,
set `format.secretBytes: 32`; supported integer lengths are 16–64 bytes.

```ts
// Supply this as ApiKeysOptions.format alongside the required ports.
const format = {
  secretBytes: 32,
  prefixes: {
    live: { user: 'pmk_u', account: 'pmk_a', accountMember: 'pmk_m' },
    test: { user: 'pmk_test_u', account: 'pmk_test_a', accountMember: 'pmk_test_m' },
  },
}
```

Prefixes omit the separator before the random suffix and use 1–64 lowercase
alphanumeric characters with single internal underscores, beginning with a letter.
`product` remains the canonical namespace, independent of the displayed prefix.
Issue and rotate share the snapshotted format; verification checks exact length,
prefix and stored owner/environment metadata. A prefix never grants authority.
Changing formats does not automatically accept older credentials: use explicit
`legacyVerifiers` only for application-owned imports, retaining live policy checks.

## Issue and verification properties

| Property                     | Meaning                                                                   |
| ---------------------------- | ------------------------------------------------------------------------- |
| `owner`                      | `{ type: account \| accountMember \| user, id }`.                         |
| `name`                       | Human-readable key label.                                                 |
| `environment`                | `live` or `test`, also encoded in the prefix.                             |
| `scopes`                     | Non-empty subset of the configured scope allowlist.                       |
| `createdBy`                  | Opaque actor ID.                                                          |
| `expiresAt`                  | Optional absolute millisecond timestamp.                                  |
| `rateLimitOverride.capacity` | Optional per-key ceiling that cannot exceed the service maximum.          |
| `requiredScopes`             | Optional verification requirement; every requested scope must be present. |

## Persistence bridge

`ApiKeysPersistence` exposes a transaction boundary, locked lookup by hash, lookup by ID, owner-scoped listing, and save. Verification and revocation must lock the same record so a last-used update cannot race past revocation.

```ts
import { prismaApiKeysPersistence } from '@playstack/api-keys/prisma'

const persistence = prismaApiKeysPersistence(prisma)
```

The managed Prisma fragment is security-critical and non-ejectable. The application owns migrations.

## Rate-limit and Nest composition

The key’s optional override is only an inner ceiling. Compose [`@playstack/rate-limit`](/docs/packages/events-operations/rate-limit) so an account-plan bucket remains authoritative, then add [`@playstack/nest-api-keys`](/docs/packages/identity/nest-api-keys) at the HTTP boundary to verify scopes and consume both buckets atomically.

Revocation retains the record and emits a transactional audit event. Manual rotation can still issue a second key, deploy it, verify traffic, and revoke the first.

## Rotate with a bounded grace period

```ts
const rotation = await apiKeys.rotate({
  id: currentKeyId,
  rotatedBy: userId,
  overlapEndsAt: Date.now() + 60 * 60_000,
})
```

Atomic rotation links both generations and keeps the previous key valid only through the configured overlap. `maxRotationOverlapMs` defaults to seven days. Retrying a completed rotation returns `already_rotated` rather than creating another key, but cannot recover the copy-once replacement secret.

`revokeFamily()` traverses both sides of the lineage and revokes every generation in one transaction. Missing links and cycles fail closed. Named legacy verifiers can validate an application-owned key format and optionally rehash the managed record during a gradual migration.

## Boundary

API keys are not JWTs, OAuth tokens, third-party credentials, offline licences, or a complete authorization policy. Application code still authorizes the verified owner and scopes against the requested resource.

## Live authorization policy

Optional `authorization.assertAllowed(input, transaction)` runs for issue, verify and rotate, including already-rotated retries. Verification checks policy on every use, not only last-used writes. Use the transaction to recheck membership, issuer ceilings and resource ownership; requested resource context is not trusted tenancy.

List/revoke management routes need separate authorization. Rotation preserves the original creator while recording the rotator; map replacement credentials to an application-owned stable grant actor. No generic machine-principal or vault-policy package is implied.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/api-keys@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/api-keys` | `./dist/index.d.ts` |
| `@playstack/api-keys/errors` | `./dist/errors.d.ts` |
| `@playstack/api-keys/prisma` | `./dist/prisma.d.ts` |
| `@playstack/api-keys/testing` | `./dist/testing.d.ts` |
| `@playstack/api-keys/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/api-keys/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/i18n

Source: https://playstack.dev/docs/packages/foundation/i18n.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

Localization is an implemented preview. Its subscription tier is not assigned in this catalog. It provides locale and catalog contracts, not a translation service, locale router or ICU compiler.

```sh
playstack add i18n --dry-run
```

Select `--web` for the [React binding](/docs/packages/foundation/i18n-react) or `--api` for [Nest](/docs/packages/foundation/nest-i18n). Integration manifests declare decisions and required tests; these recipes do not generate application wiring or select a database/ICU runtime.

```ts
import { createCatalogStore, createLocalePolicy, createLocaleFormatters } from '@playstack/i18n'

const policy = createLocalePolicy({
  locales: ['en', 'fr'],
  source: 'en',
  directions: { en: 'ltr', fr: 'ltr' },
})
const catalogs = createCatalogStore({
  policy,
  source: { common: { files: '{count, plural, one {# file} other {# files}}' } },
  translations: { fr: { common: { files: '{count, plural, one {# fichier} other {# fichiers}}' } } },
})
const locale = policy.resolveWeb({ cookieLocale: 'fr' })
const message = catalogs.get(locale, 'common', 'files')
// Format message.message with message.locale using your chosen ICU runtime.
const formatters = createLocaleFormatters(locale, { timeZone: 'UTC' })
formatters.currencyMinor(1299, 'USD')
```

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/i18n@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Resolution and ownership

| Context  | Precedence                                                            |
| -------- | --------------------------------------------------------------------- |
| Web      | URL locale, cookie, user preference, Accept-Language, source default. |
| API      | Accept-Language, authorized account preference, source default.       |
| Delivery | Recipient preference, source default; reload it in the worker.        |

Policies and catalogs are copied, with no mutable global request locale. Language-header negotiation is bounded and honors weights/exclusions. `negotiate()` can return no acceptable match; convenience resolvers fall back, so applications own any 406 policy.

Catalog IDs are inferred from the source object. Dots are literal IDs; partial translations fall back, empty translations are valid, and unknown translation IDs fail. Each resolved message retains its effective locale: use that locale for fallback plural rules. Native Intl formatters require an explicit time zone; currency formatting is display, not accounting or conversion.

ICU parsing, parameter-type generation, extraction, file loading and native catalog compilation are not implemented. Keep complete messages intact, validate grammar with the selected ICU tooling, and never interpolate through naive string replacement or HTML injection. Verify rendering in the browsers and native runtimes your product targets.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/i18n@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/i18n` | `./dist/index.d.ts` |
| `@playstack/i18n/errors` | `./dist/errors.d.ts` |
| `@playstack/i18n/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-commerce

Source: https://playstack.dev/docs/packages/commerce/nest-commerce.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-commerce` gives Nest applications injectable checkout, order-access, cancellation, and payment-projection boundaries.

The browser supplies offer IDs, quantities, redirect URLs, and an idempotency key. The application resolves the buyer and configured payment-provider instance. Guest checkout requires an explicit short-lived access-token issuer; order number plus email is never authorization.

`PlaystackCommerceWorker` accepts normalized payment projections only after the payments and webhooks pipeline verifies provider facts. The package installs no controllers, storefront, webhook verifier, or queue transport.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-commerce@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-commerce@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-commerce` | `./dist/index.d.ts` |
| `@playstack/nest-commerce/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/commerce` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-rate-limit

Source: https://playstack.dev/docs/packages/events-operations/nest-rate-limit.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-rate-limit` attaches portable rate-limit policy to NestJS routes after trustworthy request identity has been established.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-rate-limit@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose module and route

```ts
PlaystackRateLimitModule.forRoot({
  limiter,
  clock,
  trustedProxyCidrs: ['10.0.0.0/8'],
})

@UseGuards(PlaystackAuthGuard, PlaystackAccountGuard, PlaystackRateLimitGuard)
@UseRateLimit({
  name: 'api.request',
  subjects: (request, ip) => ({
    ip,
    account: request.playstackAccount.accountId,
  }),
  cost: (request) => request.body.items.length,
  headers: true,
})
@Post('/batch')
createBatch() {}
```

## Module configuration

| `NestRateLimitOptions` property | Required | Purpose |
| --- | --- | --- |
| `limiter` | Yes | Structural core limiter exposing `consume`. |
| `clock` | Yes | Formats retry response timing. |
| `trustedProxyCidrs` | No | Immediate peers allowed to contribute forwarded client-IP headers. |

Use `forRootAsync({ imports, inject, useFactory })` for DI-based composition.

## Route declaration properties

| Property | Required | Purpose |
| --- | --- | --- |
| `name` | Yes | Configured core policy name. |
| `subjects(request, ip)` | Yes | Resolves all policy subjects from trusted request state. |
| `cost` | No | Static or request-derived positive operation cost. |
| `headers` | No | Emits `RateLimit-*`, and on denial `Retry-After`, headers. |

Forwarded headers are ignored unless the immediate peer matches a trusted CIDR. The resolver walks from the nearest hop, normalizes IPv4-mapped IPv6, and canonicalizes native IPv6.

Denied operations return HTTP 429 and the stable `playstack/rate_limited` body. A fail-closed store failure returns 503 because the request was not proven over limit.

## Boundary

The adapter chooses no persistence, installs no global guard, and does not trust forwarded headers by default. Applications supply the core limiter and place the guard after authentication/account resolution when those subjects are required.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-rate-limit@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-rate-limit` | `./dist/index.d.ts` |
| `@playstack/nest-rate-limit/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/rate-limit` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Native and multi-account authentication

Source: https://playstack.dev/docs/native-authentication.md

Use the injectable [React auth transport](/docs/packages/identity/auth-react) with a product-owned native coordinator. `createExpoTokenStorage` stores credentials; it is not a refresh coordinator or a complete Expo/Tauri transport. The browser bearer transport's refresh cookie is not a native token-pair implementation.

## Server authority

Choose strict refresh replay explicitly when required; bounded grace remains the default. Authorization-code exchange binds the selected account, client, redirect URI, PKCE challenge, scopes, and expiry. It locks and revalidates the code, runs live authorization, issues the session, and records consumption in the same transaction, returning credentials only after commit. Qualify that physical transaction with your adapters; equal transaction-kind strings are insufficient.

An ambiguous commit does not establish that the code remains unused. Require a reviewed recovery flow or a new login instead of automatically repeating exchange. Account selection never bypasses live membership or an account-bound credential's scope. Switching between accounts for one user need not rotate credentials; switching identities requires separate credential slots and state generations.

## One refresh owner per credential family

Coordinate every window, process, and background task that can use the family. A mutex in one hook or JavaScript runtime is not device-wide ownership. Desktop applications can designate a native/main-process owner reached by IPC; mobile applications must qualify foreground/background coordination and secure storage.

| Event | Host behavior |
| --- | --- |
| Login or callback | Bind state, PKCE, redirect, and pending-login generation; canceled or superseded work cannot install a late token pair. |
| Concurrent refresh | One owner submits; waiters share its result. Do not replay ambiguous network outcomes. |
| Refresh succeeds | Validate and persist the complete new pair before publishing success, only for the current generation. |
| Storage write fails | Gate further refresh with the old token; rotation may already have committed. Require recovery. |
| Identity switch | Fence in-flight UI results by generation and preserve other identities' credential slots. |
| Logout | Stop admission, advance the generation, coordinate outstanding work, and perform revocation/deletion. A late response cannot restore the slot. |
| Deletion fails or app restarts | Report failure and recover pending journal state before admitting more refresh. Empty UI state is not proof of revocation. |

The secure-storage adapter has no journal, multi-key transaction, cross-process lock, or automatic network retry. Several IPC calls do not form one transaction. Keep secrets out of SSR props, logs, and synchronization notices; use opaque slot identifiers instead of emails or tokens.

## Qualification boundary

Package adapter tests cover slot isolation and malformed/read/write/delete failures with injected storage. Provider race tests cover instance-local ownership, not native lifecycle coordination. Before deployment, test login/cancel/restart/logout, delayed refresh, two windows, background suspension, account switching, callback replay, storage failures, and ambiguous network outcomes on the actual runtimes. This is a composition contract, not a claim that a packaged device-wide coordinator ships with Playstack.

## @playstack/analytics

Source: https://playstack.dev/docs/packages/events-operations/analytics.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/analytics` provides runtime-neutral, fire-and-forget page, track, identify, and group calls with consent gating, PII scrubbing, isolated fan-out, and native provider access.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/analytics@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose

```ts
import { configureAnalytics } from '@playstack/analytics'
import { fathom } from '@playstack/analytics/fathom'
import { plausible } from '@playstack/analytics/plausible'

export const analytics = configureAnalytics({
  drivers: [fathom(fathomClient), plausible(plausibleClient)],
  consent: restoredConsent,
  development: import.meta.env.DEV,
  logger: console,
  onError: ({ method, driverId, error }) => {
    errors.captureError(error, { tags: { method, driverId: driverId ?? 'input' } })
  },
})

analytics.page({ url: '/pricing', properties: { source: 'navigation' } })
analytics.track('signup_completed', { plan: 'pro', value: 4900 })
```

Calls validate and scrub once before fan-out, return synchronously, and never throw. Sync and async provider failures are isolated through `onError`.

## Reporter configuration

| `ConfigureAnalyticsOptions` property | Required | Purpose                                                   |
| ------------------------------------ | -------- | --------------------------------------------------------- |
| `drivers`                            | Yes      | Uniquely named structural provider bridges.               |
| `consent`                            | No       | `unknown`, `granted`, or `denied`; defaults to `unknown`. |
| `development`                        | No       | Warn once for unsupported driver methods.                 |
| `logger`                             | No       | Receives development capability warnings.                 |
| `onError`                            | No       | Isolated input or driver failure observer.                |

Consent-required drivers receive events only while state is `granted`. Drivers marked `not-required` continue under unknown or denied consent. Dropped calls are never replayed.

## Provider bridge

```ts
interface AnalyticsProvider<TClient = unknown> {
  id: string
  client: TClient
  capabilities: {
    page: boolean
    track: false | 'goals' | 'events'
    identify: boolean
    group: false | 'partial' | 'full'
  }
  consent: 'required' | 'not-required'
  page?(page: AnalyticsPage): void | Promise<void>
  track?(event: string, properties: AnalyticsProperties): void | Promise<void>
  identify?(userId: string, traits: AnalyticsProperties): void | Promise<void>
  group?(groupId: string, traits: AnalyticsProperties): void | Promise<void>
}
```

`analytics.driver(id).client` returns the native SDK client. Capabilities document actual behavior rather than simulating missing features.

## Privacy contract

Properties are limited to 30 shallow string, number, boolean, or null values. PII-shaped keys and values are omitted before any provider sees them. `normalizeAnalyticsTrack` from `@playstack/analytics/validation` applies the same contract before a queue or framework adapter persists server events.

## Included adapters

- Fathom maps page views and named events; numeric `value` becomes `_value`.
- Plausible maps page views, events, and non-null custom properties.
- Basemarks supports `track()` with string-valued properties, not page/identify/group. Non-string properties require explicit `mapProperties` conversion or the event is rejected. Consent defaults to required; this gate does not control SDK auto-instrumentation. See the [Basemarks integration](/integrations/basemarks).

## Testing and boundary

`@playstack/analytics/testing` provides capture/no-op drivers and a driver contract suite. The core does not bundle or initialize SDKs, persist consent, reconcile identities, or decide whether browser and server events represent one logical event.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/analytics@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/analytics` | `./dist/index.d.ts` |
| `@playstack/analytics/errors` | `./dist/errors.d.ts` |
| `@playstack/analytics/types` | `./dist/types.d.ts` |
| `@playstack/analytics/validation` | `./dist/validation.d.ts` |
| `@playstack/analytics/testing` | `./dist/testing.d.ts` |
| `@playstack/analytics/fathom` | `./dist/fathom.d.ts` |
| `@playstack/analytics/plausible` | `./dist/plausible.d.ts` |
| `@playstack/analytics/basemarks` | `./dist/basemarks.d.ts` |
| `@playstack/analytics/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/i18n-react

Source: https://playstack.dev/docs/packages/foundation/i18n-react.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

This independently installable binding supports React 18.2/19 without Next, Chakra, server auth or an ICU runtime. It is classified Free under the Laravel localization baseline.

```ts
import { createI18nReact } from '@playstack/i18n-react'

// catalogs is an application-owned createCatalogStore() result.
// Create bindings once outside render.
export const { I18nProvider, useLocale, useMessage, useMessages, useFormatters } = createI18nReact(catalogs)
```

Wrap consumers in `I18nProvider` with `locale` and `timeZone`. Message hooks return resolved records, including fallback locale, rather than interpolated strings. The host chooses its ICU/rich-text renderer. Providers are isolated; hooks outside their matching provider fail explicitly.

For [App UI](/docs/packages/ui/app-ui) and [Admin UI](/docs/packages/ui/admin-ui), supply `formatMessage` to this provider, or use `MessageCatalogProvider` alongside an existing locale context. Include both package source namespaces. See [UI localization](/docs/frameworks/ui-localization) for a complete composition and editable artifacts.

The shared package-message boundary returns plain text for visible and accessible labels. It rejects non-string output, preserves explicit component overrides and retains English defaults when no provider is installed. It is not an HTML renderer.

Locale routing, document language/direction, cookies, preferences and consistent SSR/client locale/time-zone inputs remain application-owned. Ship only client-safe catalogs. Packed localized Next and React fixtures do not replace real translation, RTL, accessibility or native acceptance tests.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/i18n-react@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/i18n-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/i18n-react` | `./dist/index.d.ts` |
| `@playstack/i18n-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/i18n` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-api-keys

Source: https://playstack.dev/docs/packages/identity/nest-api-keys.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-api-keys` translates API-key verification, scope requirements, and atomic rate limits into explicit NestJS route declarations.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-api-keys@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose

```ts
PlaystackApiKeysModule.forRoot({
  apiKeys,
  limiter,
  rateLimitName: 'api-keys.request',
  resolveAccountId: (key) => key.owner.id,
})

@UseGuards(PlaystackApiKeysGuard)
@RequireApiKey('spans:write')
@Post('/spans')
ingest(@Req() request: ApiKeyRequest) {
  return service.ingest(request.apiKey!)
}
```

## Configuration reference

| `NestApiKeysOptions` property | Required | Purpose |
| --- | --- | --- |
| `apiKeys` | Yes | Structural verifier exposing `verify({ secret, requiredScopes })`. |
| `limiter` | Yes | Consumes the API-key and account buckets atomically. |
| `rateLimitName` | Yes | Configured shared rate-limit policy name. |
| `headerName` | No | Credential header; defaults to Authorization Bearer. |
| `resolveAccountId` | Yes | Maps the verified key and request to the authoritative account bucket ID. |

The guard reads the credential, passes `@RequireApiKey` scopes to verification, resolves account context, and calls the limiter once with `{ apiKey, account }` subjects plus the key’s optional capacity override. Storage failure returns 503; a denied bucket returns 429. The verified record is attached as `request.apiKey`.

Use `forRootAsync({ imports, inject, useFactory })` for DI-based configuration.

## Boundary

The adapter does not issue keys, own persistence, infer account tenancy, or install a global guard. Provider-specific clients and application authorization remain accessible through the supplied callbacks and services.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-api-keys@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-api-keys` | `./dist/index.d.ts` |
| `@playstack/nest-api-keys/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/api-keys` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/rate-limit` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Durable security decisions and projections

Source: https://playstack.dev/docs/durable-security-projections.md

A downstream audit/read-model failure must not undo a security decision that already committed. Compose [API keys](/docs/packages/identity/api-keys), Audit, and Events with explicit transaction ownership; do not replace required handlers with no-ops or silently change their event semantics.

## Source transaction

Commit these together through the **same physical transaction handle**:

1. The native credential decision, such as `apiKeys.revoke`.
2. A canonical, IDs-only audit record through `bindAuditRegistry` and `PrismaAuditPersistence`.
3. The immutable observational envelope through `PrismaEventOutboxWriter`.

Canonical audit or intent persistence failure rolls back the decision. This does not permit unjournaled revocation; creation and onboarding retain the same required-evidence rule. Only after commit does `PrismaOutboxPump` dispatch the durable intent. Projection failure leaves it retryable and the credential revoked.

## Recoverable projection

In a separate post-commit transaction, atomically claim `(stableHandlerName, originalEventId)` through the audit receipt seam and write the projection. A failed effect must roll back its receipt. Concurrent repair and lost acknowledgments then produce one committed database effect. Remote I/O needs its own idempotency mechanism or recovery protocol; a local receipt cannot make it atomic.

Preserve the original actor, occurrence time, event ID, and immutable credential-generation ID, including impersonation attribution where relevant. Never substitute the repair operator, repair time, or a current mutable email. Secrets, hashes, and credential envelopes do not belong in audit intent.

Keep handler identities and receipts for the supported replay horizon. Do not prune pending outbox rows or receipts with projection rows. The outbox is temporary transport state, not indefinite replay history; retain canonical audit under the product's reviewed retention policy. Bound worker leases, retries, backoff, and shutdown, and limit each pump to owned event names.

## Evidence and limits

The repository's `scripts/test-security-audit-projection-postgres.mjs` fixture exercises native adapters on an owned disposable PostgreSQL cluster: late audit/intent failures, failed projection, competing repairs, and lost acknowledgment/replay. Recorded runtime qualification is Prisma 6.12.0 / PostgreSQL 17.11, not every Prisma adapter or a deployed worker.

Qualify your exact database/client versions and physical transaction before adoption. This recipe does not establish that remote Audit drivers participate in the transaction or that ambiguous connection/commit failures are safe to retry. If canonical audit-chain work cannot fit the source transaction, a separately reviewed minimal durable journal is needed.

## @playstack/analytics-react

Source: https://playstack.dev/docs/packages/events-operations/analytics-react.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/analytics-react` supplies an existing analytics instance through React context, makes consent reactive, and offers explicit Pages and App Router navigation adapters.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/analytics-react@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the provider

```tsx
import { PlaystackAnalyticsProvider, useAnalytics, useAnalyticsConsent } from '@playstack/analytics-react'

function ApplicationProviders({ children }) {
  return (
    <PlaystackAnalyticsProvider analytics={analytics}>
      {children}
    </PlaystackAnalyticsProvider>
  )
}

function UpgradeButton() {
  const analytics = useAnalytics()
  return <button onClick={() => analytics.track('upgrade_selected')}>Upgrade</button>
}
```

The provider accepts only `analytics` and children. `useAnalytics()` returns that exact core instance, including native clients. `useAnalyticsConsent()` returns reactive `{ state, setConsent }` controls.

## Consent ownership

Restore durable consent on the core instance before rendering, then persist changes made through the hook in application settings or compliance storage. This package owns only React reactivity; it does not store evidence.

## Next.js Pages Router bridge

```tsx
import { usePagesRouterAnalytics } from '@playstack/analytics-react/next-pages'

usePagesRouterAnalytics(useRouter(), {
  enabled: true,
  trackShallow: false,
  shouldTrack: ({ url }) => !url.startsWith('/account/private'),
  resolvePage: ({ url }) => ({
    title: document.title,
    properties: { section: url.split('/')[1] || 'home' },
  }),
  onError: (error) => errors.captureError(error),
})
```

| Option         | Purpose                                              |
| -------------- | ---------------------------------------------------- |
| `enabled`      | Disables initial and subsequent tracking when false. |
| `trackShallow` | Treats query-only navigation as a distinct page.     |
| `shouldTrack`  | Product-specific route exclusion policy.             |
| `resolvePage`  | Supplies title and properties for the resolved URL.  |
| `onError`      | Isolated policy/metadata error observer.             |

The structural router bridge records the initial ready URL and completed navigation only, skips duplicate URLs, and has no `next` dependency.

## Next.js App Router bridge

```tsx
'use client'

import { Suspense } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'

import { useAppRouterAnalytics } from '@playstack/analytics-react/next-app'

function AnalyticsRoutes() {
  useAppRouterAnalytics(usePathname(), useSearchParams(), {
    shouldTrack: ({ pathname }) => !pathname.startsWith('/account/private'),
  })
  return null
}

export function AnalyticsNavigation() {
  return (
    <Suspense fallback={null}>
      <AnalyticsRoutes />
    </Suspense>
  )
}
```

The App Router effect observes committed pathname and query values, records the first available URL, and skips duplicates. The application owns the Suspense boundary required by `useSearchParams()` in statically rendered routes.

## Boundary

The package does not initialize SDKs, persist consent, emit domain events, publish artifacts, or impose a router. Those choices remain application inputs.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/analytics-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/analytics-react` | `./dist/index.d.ts` |
| `@playstack/analytics-react/errors` | `./dist/errors.d.ts` |
| `@playstack/analytics-react/next-pages` | `./dist/next-pages.d.ts` |
| `@playstack/analytics-react/next-app` | `./dist/next-app.d.ts` |
| `@playstack/analytics-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/analytics` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/connections

Source: https://playstack.dev/docs/packages/identity/connections.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/connections` manages encrypted, refreshable credentials for acting against third-party APIs on behalf of a user, account member, or account.

## Install

```sh
npm install @playstack/connections @playstack/core @playstack/crypto @playstack/events
```

## Compose providers and storage

```ts
import { createConnections } from '@playstack/connections'
import { githubConnectionProvider } from '@playstack/connections/providers/github'

const github = githubConnectionProvider({
  clientId: process.env.GITHUB_CLIENT_ID!,
  clientSecret: process.env.GITHUB_CLIENT_SECRET!,
})

export const connections = createConnections({
  persistence,
  crypto: applicationCrypto,
  events,
  clock,
  ids,
  providers: [github],
  scope: 'account',
  redirectUri: 'https://app.example/api/connections/callback',
  returnUrlOrigins: ['https://app.example'],
})

const { authorizationUrl } = await connections.begin({
  provider: 'github',
  scope: { type: 'account', id: accountId },
  connectedByUserId: userId,
  returnUrl: 'https://app.example/settings/connections',
  scopes: ['repo:read'],
})
```

OAuth uses PKCE plus a purpose-bound, short-lived, single-use state token. Access and refresh tokens are encrypted before persistence.

## Service configuration

| `ConnectionsOptions` property | Required | Purpose                                                          |
| ----------------------------- | -------- | ---------------------------------------------------------------- |
| `persistence`                 | Yes      | Transactional encrypted connection state and refresh locking.    |
| `crypto`                      | Yes      | Random tokens, digests, encryption, and signed single-use state. |
| `events`, `clock`, `ids`      | Yes      | Typed lifecycle events and deterministic primitives.             |
| `providers`                   | Yes      | Unique provider bridges keyed by provider ID.                    |
| `scope`                       | Yes      | Exactly one of `user`, `accountMember`, or `account`.            |
| `redirectUri`                 | Yes      | Exact OAuth callback URI.                                        |
| `returnUrlOrigins`            | Yes      | Allowlist for post-authorization return URLs.                    |
| `stateTtl`                    | No       | Signed OAuth state lifetime; defaults to `5m`.                   |
| `refreshAheadMs`              | No       | Refresh before expiry window; defaults to five minutes.          |

The configured `scope` must match the selected Prisma artifact variant and persistence adapter option.

## Provider bridge

```ts
interface ConnectionProvider {
  id: string
  capabilities: {
    incrementalScopes: boolean
    refresh: boolean
    revoke: boolean
    rotatingRefreshTokens?: boolean
  }
  authorizationUrl(context: OAuthAuthorizationContext): string
  exchangeCode(code: string, context: OAuthExchangeContext): Promise<TokenSet>
  identify(tokens: TokenSet): Promise<ProviderIdentity>
  refresh?(refreshToken: string): Promise<TokenSet>
  revoke?(tokens: TokenSet): Promise<void>
  validateManual?(credential: string): Promise<ProviderIdentity>
}
```

Capabilities must describe real provider behavior. The service handles connection state, encryption, scope comparison, refresh concurrency, and lifecycle events; provider drivers own protocol-specific URLs and token exchange.

## Provider options

The included driver accepts required `clientId` and `clientSecret`, plus optional GitHub Enterprise `webBaseUrl` and `apiBaseUrl`, REST `apiVersion`, `userAgent`, `allowSignup`, `prompt`, injected `fetch`, and test clock. It supports OAuth App authorization with PKCE and personal access tokens as manual credentials.

`@playstack/connections/providers/oauth2` supplies a standards-based authorization-code, PKCE, refresh, client-authentication, and optional RFC 7009 revocation factory. Named drivers configure GitHub, LinkedIn, Mastodon, and X while preserving the native provider instance and injected Fetch boundary.

AT Protocol is intentionally separate. [`@playstack/atproto`](/docs/packages/identity/atproto) retains official discovery, PAR, DPoP, nonce, refresh, and session-store behavior instead of copying its credentials into a conventional OAuth token record.

## Crypto and persistence bridges

`ConnectionCrypto` is structurally compatible with `@playstack/crypto`. Its `sign` and `verify` methods must preserve purpose and single-use semantics. `ConnectionsPersistence` provides transactional lookup/save and must lock a connection during refresh.

```ts
import { prismaConnectionsPersistence } from '@playstack/connections/prisma'

const persistence = prismaConnectionsPersistence(prisma, { scope: 'account' })
```

Select the matching managed schema variant in `playstack.json`; it is security-critical and non-ejectable. The CLI composes the matching reverse model extension into a managed owner artifact. When an established schema differs physically, use an application-owned persistence adapter and contract tests rather than importing parallel User or Account models.

## Boundary

Connections returns a valid access token; feature code uses the provider’s native SDK directly. The package does not implement login, provider API clients, posting, syndication, or provider-specific UI. Secret sources and provider configuration remain application-owned.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/connections@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/connections` | `./dist/index.d.ts` |
| `@playstack/connections/errors` | `./dist/errors.d.ts` |
| `@playstack/connections/prisma` | `./dist/prisma.d.ts` |
| `@playstack/connections/providers/github` | `./dist/providers/github.d.ts` |
| `@playstack/connections/providers/oauth2` | `./dist/providers/oauth2.d.ts` |
| `@playstack/connections/providers/transport` | `./dist/providers/transport.d.ts` |
| `@playstack/connections/providers/linkedin` | `./dist/providers/linkedin.d.ts` |
| `@playstack/connections/providers/mastodon` | `./dist/providers/mastodon.d.ts` |
| `@playstack/connections/providers/x` | `./dist/providers/x.d.ts` |
| `@playstack/connections/testing` | `./dist/testing.d.ts` |
| `@playstack/connections/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/connections/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/connections/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/crypto` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-i18n

Source: https://playstack.dev/docs/packages/foundation/nest-i18n.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

The Nest 10/11 binding registers a non-global locale policy and `PlaystackLocaleService`. It is classified Free under the Laravel localization baseline. Supply the declared Nest, RxJS and metadata peers; no database or translation provider is installed.

```ts
import { Module } from '@nestjs/common'
import { PlaystackI18nModule } from '@playstack/nest-i18n'

@Module({
  imports: [
    PlaystackI18nModule.forRoot({
      locales: ['en', 'fr'],
      source: 'en',
      directions: { en: 'ltr', fr: 'ltr' },
    }),
  ],
})
export class AppModule {}
```

Inject `PlaystackLocaleService` and call `resolveRequest(request, { accountLocale })` with an already-authorized account preference. It resolves Accept-Language, account preference, then source locale without mutating requests or singleton state. `resolveRecipient(recipient.locale)` deliberately has no HTTP context and is appropriate for delivery workers.

`forRootAsync` supports normal Nest imports/inject/useFactory composition. `PLAYSTACK_LOCALE_POLICY` and `@InjectLocalePolicy()` expose the underlying policy. The application owns authentication, controllers, `Content-Language`, `Vary`, cache separation and any 406 response policy. Locale is never authorization context.

The binding supports Nest 10 and 11. Registration and per-request isolation are covered by the package; how your controllers set `Content-Language` and cache headers remains yours to test.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-i18n@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-i18n@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-i18n` | `./dist/index.d.ts` |
| `@playstack/nest-i18n/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/i18n` | `0.1.0-beta.4` | Required by the package. |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/registry

Source: https://playstack.dev/docs/packages/infrastructure/registry.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/registry` is the portable private-registry core: immutable release registration, fenced publication claims, canonical manifests, exact reads, optimistic tag and visibility changes, and host-supplied release eligibility, with an optional read-only npm HTTP entry point, PostgreSQL persistence, bounded tarball inspection, and Playstack Storage composition. It serves `registry.playstack.dev`.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/registry@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose without adopting a billing or credential system

```ts
import { createRegistry, canAccessRelease } from '@playstack/registry'

const registry = createRegistry({
  persistence, // application-owned RegistryPersistence
  clock,
  ids, // unpredictable claim IDs and tokens
  authorize, // verified principal + operation + optional exact release + transaction
  verifyArtifact, // bytes, size, hashes, canonical manifest, immutable storage
})
```

`authorize` is required on reads and mutations, including completed retries. The host authenticates before calling the service and owns live scope and resource policy. Account-owned packages require the principal's account to match the owner; shared packages have `ownerAccountId: null`, which is not anonymous authorization.

`canAccessRelease(accountId, packageRecord, release, snapshot)` evaluates a host-owned snapshot: an explicit package set with inclusive `releasedThrough` cutoffs plus exact-version administrative exceptions. That is how a subscription reaches the registry: the host projects the packages a plan earns through "now" while the subscription is active, freezes the cutoffs at lapse, and advances them on renewal. The core never loads billing projections, calculates grace, or infers tier names.

## Reads and downloads

The npm entry point answers packuments, exact versions, and tarball downloads with private `no-store` headers, ETags, and a signed-redirect or streamed download policy chosen by the host. Withdrawn versions are denied whatever tags or exceptions say; rollback changes pointers, never bytes.

## Boundary

Namespace allocation, credentials, subscriptions, and public routes belong to the host. [`@playstack/nest-registry`](/docs/packages/infrastructure/nest-registry) supplies the Nest HTTP binding and [`@playstack/registry-swift`](/docs/packages/infrastructure/registry-swift) the Swift Package Manager read protocol over the same core.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/registry@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/registry` | `./dist/index.d.ts` |
| `@playstack/registry/errors` | `./dist/errors.d.ts` |
| `@playstack/registry/npm` | `./dist/npm.d.ts` |
| `@playstack/registry/prisma` | `./dist/prisma.d.ts` |
| `@playstack/registry/node` | `./dist/node.d.ts` |
| `@playstack/registry/storage` | `./dist/storage.d.ts` |
| `@playstack/registry/packer` | `./dist/packer.d.ts` |
| `@playstack/registry/mirror` | `./dist/mirror.d.ts` |
| `@playstack/registry/release` | `./dist/release.d.ts` |
| `@playstack/registry/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/registry/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/registry/testing` | `./dist/testing.d.ts` |
| `@playstack/registry/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/storage` | `0.1.0-beta.4` | Optional; only for the entry points that use it. |
| `tar-stream` | `^3.2.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## Data-only background sync hints

Source: https://playstack.dev/docs/background-sync-hints.md

A sync hint means “ask the authenticated sync API for changes,” not “apply this document.” Compose Devices with the [durable delivery worker](/docs/packages/communication/delivery#durable-attempts-and-replay), without requiring a notification inbox. Enrollment, scheduling, coalescing, and the sync protocol remain application-owned.

## Durable handoff

In the product transaction, select eligible recipients and exclude the origin device. Reserve a host-owned coalescing batch with a unique tenant/recipient/target/revision key. Closing the batch freezes its content and recipient scope. Persist the protected immutable reference, register the native attempt, and append queue outbox intent on the same physical transaction.

Use only a hint such as `{ kind: 'sync-required', version: '1' }`: no document text, names, paths, access tokens, or previews. Reuse the closed batch/target identity on enqueue retry and redelivery. A genuinely new batch gets a new revision; an uncertain send must not receive a new key to bypass quarantine. Set finite attempts and a freshness deadline. Collapse IDs and provider TTLs are optimizations, not durable identity.

## Live recipient checks

The worker's transactional `prepare` resolves immutable intent, recomputes its request hash, and checks current membership, enrollment, preferences, suppression, expiry, and origin-device exclusion. Unavailable authority throws; confirmed denial returns a denied decision.

Wrap Devices' encrypted target resolver with a product `PushTargetSource`. `createDevicesPushTargetSource` alone resolves the user/app/token; it does not authorize product enrollment or exclude the origin. Recheck in `resolve` immediately before provider I/O, return null for revoked/expired/origin targets, and delegate permanent feedback to `devices.recordPushFeedback`. Bind provider slot/app identity to the intent. Never log decrypted credentials.

The worker commits submission before dispatch and uses snapshotted portable data. Keep SDK clients and credentials in configured closures, not queue payloads. Revocation after the final check cannot recall an accepted push; the sync API independently authorizes every content read.

## Data-only transport and recovery

APNs requires `aps: { 'content-available': 1 }` plus background headers and the correct topic on the injected client. Expo uses `data` and `contentAvailable` with a compatible SDK. FCM uses `data`, with the appropriate APNs background configuration for Apple devices. Omit alert/title/body/notification fields. Web Push can transport data, but browser policies may require a visible notification; this does not promise silent browser sync or headless execution on any OS.

Permanent destination rejection invalidates only the selected token. Malformed receipts and lost responses are non-retryable uncertainty, not grounds to invalidate a valid token. The worker conservatively quarantines thrown dispatch errors; verified refusal evidence can permit a host reconciler to use native `reconcile/retry` within the original budget and deadline. Ordinary queue retry grants no new send. Failed outcome persistence requires receipt repair, not resubmission.

Provider acceptance is not device execution. Review SDK retries/deadlines separately; one adapter invocation is not proof of one network submission. Foreground authenticated sync remains the recovery path for lost hints.

## Qualification boundary

Native provider/slot unit fixtures cover recipient rechecks, permanent rejection, malformed outcomes, and concurrent/replayed work without resending accepted/unknown attempts. They do not qualify real devices, SDK network retries, a durable host coalescer, or your application's physical transaction. Exercise those boundaries before enabling a production worker.

## Localizing App UI and Admin UI

Source: https://playstack.dev/docs/frameworks/ui-localization.md

Package UI uses the shared [React localization binding](/docs/packages/foundation/i18n-react). No provider is required for existing English usage. The host owns translated copy and its ICU dependency.

```tsx
'use client'
import { IntlMessageFormat } from 'intl-messageformat' // application dependency
import { appUICatalog } from '@playstack/app-ui/messages'
import { adminUICatalog } from '@playstack/admin-ui/messages'
import { createCatalogStore, createLocalePolicy } from '@playstack/i18n'
import { createI18nReact, type FormatCatalogMessage } from '@playstack/i18n-react'

const catalogs = createCatalogStore({
  policy: createLocalePolicy({ locales: ['en', 'fr'], source: 'en' }),
  source: { ...appUICatalog, ...adminUICatalog },
  translations: {
    fr: {
      'app-ui': { 'search.clear': 'Effacer la recherche' },
      'admin-ui': { 'members.title': 'Membres' },
    },
  },
})
export const { I18nProvider } = createI18nReact(catalogs)
export const formatMessage: FormatCatalogMessage = (message, values) => {
  const result = new IntlMessageFormat(message.message, message.locale).format(values)
  if (typeof result !== 'string') throw new Error('UI messages must be plain text')
  return result
}
// Within your Chakra composition root:
// <I18nProvider locale="fr" timeZone="UTC" formatMessage={formatMessage}>
//   {children}
// </I18nProvider>
```

Use `MessageCatalogProvider` directly if another library already owns the locale context. Always format using the resolved message locale, not the requested locale. Partial translations fall back; configured missing namespaces/IDs and formatter failures remain explicit. Component props/message overrides win over catalog values.

## Editable source artifacts

Both UI packages export data-only `./messages` and `./locales/en.json` entry points. Admin UI also consumes App UI, so compose both namespaces. Configure their artifact destination in `playstack.json`:

```json
{ "i18n": { "target": "libs/i18n", "packages": ["@playstack/app-ui", "@playstack/admin-ui"] } }
```

```sh
playstack i18n init --dry-run
playstack i18n init
playstack i18n init --check
```

The `app-ui.messages.en` and `admin-ui.messages.en` artifacts target `en/app-ui.json` and `en/admin-ui.json`. This copies English catalogs, not translations or extracted application strings. Keep translations application-owned; review ejection/merge before customizing managed English. Do not force-sync over reviewed edits.

Account names, role labels, navigation and business content remain host-owned. Set consistent document `lang`/`dir`, Chakra direction, explicit directional control props and SSR locale inputs. Localization must never alter membership permissions, account scope or mutation ordering. Verify right-to-left layout and screen-reader behavior in your own product.

## @playstack/atproto

Source: https://playstack.dev/docs/packages/identity/atproto.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/atproto` wraps the stable lifecycle of the official AT Protocol OAuth client. Discovery, PAR, PKCE, DPoP keys, nonces, refresh rotation, and session persistence remain with that client.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/atproto@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose without weakening the protocol

```ts
import { createAtprotoConnectionClient } from '@playstack/atproto'

const atproto = createAtprotoConnectionClient({ client: oauthClient })
const authorizationUrl = await atproto.begin({
  identifier: 'person.bsky.social',
  state: signedApplicationState,
})
const { session, identity } = await atproto.complete(requestQuery)
```

The stable DID can identify an application connection, but bearer tokens and DPoP private keys are not copied into `@playstack/connections`. Authenticated XRPC continues through the restored official session.

Node 20 uses `@atproto/oauth-client-node` 0.3.x. Node 22 and newer support 0.3.x through 0.5.x; the CLI selects the verified range for the active runtime.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/atproto@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/atproto` | `./dist/index.d.ts` |
| `@playstack/atproto/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/connections` | `0.1.0-beta.4` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/audit

Source: https://playstack.dev/docs/packages/identity/audit.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/audit` records security- and administration-relevant activity in append-only, per-scope tamper-evident chains for Node and Workers.

## Install

```sh
npm install @playstack/audit @playstack/core @playstack/events
```

## Compose event-driven audit

```ts
import {
  bindAuditRegistry,
  createAuditService,
  Sha256AuditHash,
} from '@playstack/audit'

const audit = createAuditService({
  persistence,
  hash: new Sha256AuditHash(applicationCrypto),
  clock,
  ids,
  mapper: {
    map: (event, payload) => ({
      scopeType: event.context.scope?.type ?? 'system',
      scopeId: event.context.scope?.id ?? 'global',
      actorType: event.context.actor?.type ?? 'system',
      actorId: event.context.actor?.id,
      resourceType: payload.resourceType as string,
      resourceId: payload.resourceId as string,
    }),
  },
  retention: {
    mode: 'calendar-months',
    months: 84,
    policyReference: 'docs/policies/audit-retention.md',
  },
})

const dispose = bindAuditRegistry(events, registry, audit)
events.assertReady()
```

The event definition chooses the audit tier and projected fields. The application mapper supplies scope, actor, resource, and impersonation facts because those cannot be inferred safely from arbitrary payload names. The recorded `action` defaults to the event name without its first (package) segment, so `accounts.member.joined` is audited as `member.joined`; a mapper may return `action` to override that, for example to keep the full event name when two packages share a suffix.

## Configuration reference

| `AuditServiceOptions` property | Required | Purpose |
| --- | --- | --- |
| `persistence` | Yes | Transactional append, locked chain-tail lookup, query, and integrity reads. |
| `hash` | Yes | `digest(value)` for canonical entry hashing. |
| `clock`, `ids` | Yes | Application-owned timestamps and entry identifiers. |
| `mapper` | Yes | Maps a typed event and projected payload to scope, actor, and resource facts. |
| `retention.mode` | Yes | `calendar-months`, `duration`, or `until-deletion`; select one explicit policy. |
| `retention.months` | For `calendar-months` | Positive whole calendar months, aligned to the UTC month boundary. |
| `retention.durationMs` | For `duration` | Positive safe-integer milliseconds; for example, `30 * 24 * 60 * 60 * 1000` for exactly 30 days. |
| `retention.policyReference` | Yes | Application-owned policy document that justifies the selected policy. |

The validated retention policy is exposed as `audit.retention` so storage, scheduled partition maintenance, and framework adapters use the same decision.

`prune(scopeType, scopeId, limit)` preserves a verified chain checkpoint while
removing a bounded expired prefix. Duration policies use the exact current clock
minus `durationMs`; calendar policies use the first day of the current UTC month
minus `months`. Entries exactly at the cutoff remain. `until-deletion` disables
age pruning and does not load the history merely to return zero.

Retention does not install a deletion worker. For any selected mode, compose
`scopeLifecycle.isOpen(scope, transaction)` with the authoritative user/account
lifecycle and invoke `erase(scopeType, scopeId, transaction)` only after that scope
is closed, on the same physical transaction. Unavailable authority must throw,
and scope IDs must never be reused. `until-deletion` expresses the absence of an
age cutoff; the application still owns the deletion trigger and qualification.

## Audit entry properties

| Group | Properties |
| --- | --- |
| Identity | `id`, `at`, `scopeType`, `scopeId`. |
| Actor | `actorType`, optional `actorId`, `onBehalfOfId`, and `apiKeyId`. |
| Action | `action`, `resourceType`, `resourceId`. |
| Detail | Optional `changes` and `context`; secret- and PII-shaped keys are recursively redacted. |
| Chain | Optional `prevHash` and required `hash`. |

Queries always require `scopeType` and `scopeId`, with optional actor, resource, action, time range, cursor, and limit filters.

Built-in actor types are `user`, `apiKey`, `staff`, and `system`. The string
contract is intentionally extensible, so applications can record actors such
as `partner`, `service`, or another authenticated principal without converting
them into a user. Actor identifiers remain optional only for system activity.

## Persistence bridge

`AuditPersistence` must lock the `scopeType/scopeId` chain while reading its tail and appending the next record—even when the chain is empty. Transactional event handlers receive the emitting transaction and fail the mutation if audit storage fails. Queued handlers open their own transaction after outbox delivery.

`@playstack/audit/prisma` provides PostgreSQL advisory locking, append-only inserts, and stable time/ID cursor pagination. The managed Prisma fragment is composed by the CLI; the application owns migrations plus monthly partition creation and removal.

## Direct application routes

Package events are the authoritative audit path for security-critical domain operations. Use [`@playstack/nest-audit`](/docs/packages/identity/nest-audit) for explicitly declared application routes whose successful responses should be recorded on a best-effort basis.

## Boundary

Audit is not the general event store and does not record every event. The application owns retention policy, storage access control, operator workflows, partition operations, and the decision about which application-specific events are auditable.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/audit@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/audit` | `./dist/index.d.ts` |
| `@playstack/audit/errors` | `./dist/errors.d.ts` |
| `@playstack/audit/prisma` | `./dist/prisma.d.ts` |
| `@playstack/audit/testing` | `./dist/testing.d.ts` |
| `@playstack/audit/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/audit/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-analytics

Source: https://playstack.dev/docs/packages/events-operations/nest-analytics.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-analytics` keeps provider I/O outside request handlers by validating server-originated analytics and dispatching one idempotent queue job per eligible provider.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-analytics@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the module

```ts
const core = configureAnalytics({ drivers: [serverPostHogProvider] })
const analyticsQueue = createQueue('analytics', {
  app: 'billing',
  environment: 'production',
  driver: queueDriver,
})

PlaystackAnalyticsModule.forRoot({
  queue: analyticsQueue,
  drivers: [asServerAnalyticsDriver(core.driver('posthog'))],
  onError: (failure) => errors.captureError(failure.error),
})
```

`asServerAnalyticsDriver` is an explicit assertion that the injected client supports server ingestion. Do not mark a browser-only driver merely because it has a structural `track` method.

## Module and driver properties

| Surface | Contract |
| --- | --- |
| `queue` | Application-owned `@playstack/queues` queue. |
| `drivers` | Unique server drivers with `id`, native `client`, `runtime: 'server'`, consent requirement, and `track`. |
| `onError` | Isolated observer for validation, enqueue, or delivery failures. |

Use `forRootAsync({ imports, inject, useFactory })` for DI-resolved composition.

## Dispatch events

```ts
analytics.track({
  id: `renewal-${invoice.id}`,
  event: 'subscription_renewed',
  properties: { account_id: account.id, plan: 'pro', value: invoice.total },
  consent: account.analyticsConsent,
})
```

IDs are required URL-safe strings up to 48 characters. They become portable `event_id` and per-driver queue keys `analytics-<eventId>-<driverId>`, deduplicating each provider independently. Because `event_id` occupies one of 30 property slots, supply at most 29 additional properties.

Consent-required drivers are skipped unless the event carries `granted`; skipped calls are not replayed. `track` returns synchronously and never fails the originating request.

## Worker bridge

Register `PlaystackAnalyticsDeliveryJob` with the application queue processor using the Nest-managed instance. Each provider gets a separate job with five exponential-backoff attempts. Invalid payloads and unknown drivers are non-retryable; provider failures are retryable and exhausted delivery reaches `onError`.

## Boundary

The module emits no domain events and owns no queue lifecycle. It does not persist consent or make browser and server calls equivalent automatically. To deduplicate the same logical event across runtimes, propagate a provider-supported common event ID or choose one authoritative path.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-analytics@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-analytics` | `./dist/index.d.ts` |
| `@playstack/nest-analytics/errors` | `./dist/errors.d.ts` |
| `@playstack/nest-analytics/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/analytics` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/queues` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/registry-swift

Source: https://playstack.dev/docs/packages/infrastructure/registry-swift.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/registry-swift` implements the Swift Package Manager registry v1 read protocol over [`@playstack/registry`](/docs/packages/infrastructure/registry). It is a TypeScript server library, not a Swift client and not an npm-to-Swift proxy, and it adds no account, billing, or persistence authority.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/registry-swift@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Composition

```ts
import { createSwiftRegistryHandler } from '@playstack/registry-swift'
import { verifySwiftArchive } from '@playstack/registry-swift/node'
import { createRegistryStorage } from '@playstack/registry/storage'

const artifacts = createRegistryStorage({
  storage: privateStorageDriver,
  verifyTarball: (body, release, key, signal) => verifySwiftArchive(body, release, key, { signal }),
})

const handler = createSwiftRegistryHandler({
  registry,
  baseUrl: 'https://registry.playstack.dev/-/v1/swift',
  authenticate: authenticateAccountKey,
  resolvePackage: async (identity, principal) => resolveOwnedSwiftPackage(identity, principal),
  download: artifacts.download,
})
```

Use a separate namespace and route from npm, for example key `{ namespace: 'swift', name: 'playstack.licensingkit' }` for the Swift identity `playstack.licensingkit`. The resolver maps identities to host-owned keys; it does not grant access. The registry authorizes each catalog, version, manifest, and archive read with the current principal, filters frozen releases, and denies withdrawn versions.

## Protocol surface

GET and HEAD release lists, exact release metadata, canonical `Package.swift`, toolchain variant manifests, source ZIP downloads, optional authorized source-URL identifier lookup, Bearer and Basic account credentials, v1 headers, and problem details. URLs derive only from the configured `baseUrl`, never from incoming Host headers, and HTTPS is required outside explicit local development.

## Boundary

Namespace allocation, identity-to-plan mapping, and membership policy remain host-owned, as does publishing: this package reads.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/registry-swift@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/registry-swift` | `./dist/index.d.ts` |
| `@playstack/registry-swift/node` | `./dist/node.d.ts` |
| `@playstack/registry-swift/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `yauzl` | `^3.2.0` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/feedback

Source: https://playstack.dev/docs/packages/events-operations/feedback.md

{/* package-access:start */}

> **Paid.** Covered by the Playstack Pro License; access and tier assignment are not yet announced. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/feedback` defines the schema-version-1 report that `playstack feedback` sends, a framework-neutral intake policy with validation, per-address limiting and address hashing, the `FeedbackStore` port with memory and Prisma implementations, and the `feedback.report.received` event. It is not ticketing and not telemetry: nothing is sent without an explicit client action, the intake never reads reports back, and it never sends mail.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/feedback@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose the intake

```ts
import { createFeedbackIntake } from '@playstack/feedback'
import { PrismaFeedbackStore } from '@playstack/feedback/prisma'
import { PrismaEventOutboxWriter } from '@playstack/events-prisma'

const intake = createFeedbackIntake({
  store: new PrismaFeedbackStore(prisma, { outbox: new PrismaEventOutboxWriter() }),
  limiter: { consume: ({ ip }) => limiter.consume('feedback.ip', { subjects: { ip } }) },
  hashAddress: (ip) => keyedHash(ip),
})

const decision = await intake.limit(ip) // before reading any body
const { id, acceptance } = await intake.accept(body, { ip }) // 'created' | 'duplicate'
```

`accept` throws `FeedbackError('invalid_report')` with path detail for schema failures and never echoes the submitted message. Duplicate ids are acknowledged without a second row or event, so client retries are safe. The report carries a category (`bug`, `idea`, `question`, `other`), the message, optional contact, CLI build identity, runtime facts, and the redacted failure record `playstack feedback --last` attaches.

## Persistence and events

`publish/playstack-feedback.prisma` is the managed database artifact, materialized by `playstack database sync`. `PrismaFeedbackStore` is structurally typed against a generated client and takes the application's outbox writer, so the store and the `feedback.report.received` event commit together. Notification and triage workers consume that event; the intake itself stays write-only.

## Boundary

HTTP lives in [`@playstack/nest-feedback`](/docs/packages/events-operations/nest-feedback). Reading reports back, replying, and closing them are application workflows outside this package.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/feedback@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/feedback` | `./dist/index.d.ts` |
| `@playstack/feedback/errors` | `./dist/errors.d.ts` |
| `@playstack/feedback/prisma` | `./dist/prisma.d.ts` |
| `@playstack/feedback/testing` | `./dist/testing.d.ts` |
| `@playstack/feedback/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/feedback/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-audit

Source: https://playstack.dev/docs/packages/identity/nest-audit.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-audit` makes route-level audit intent visible in NestJS metadata while reusing an existing `@playstack/audit` recorder and retention policy.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-audit@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose explicit route audit

```ts
PlaystackAuditModule.forRoot({
  audit,
  onFailure: (error) => errors.captureError(error),
})

@UseInterceptors(PlaystackAuditInterceptor)
@Audited({
  map: (request, response) => ({
    scopeType: 'account',
    scopeId: request.playstackAccount.accountId,
    actorType: 'user',
    actorId: request.playstackSession.userId,
    action: 'project.created',
    resourceType: 'project',
    resourceId: response.id,
  }),
})
@Post('/projects')
create() {}
```

## Configuration and bridge properties

| Surface | Contract |
| --- | --- |
| `audit` | Recorder exposing validated `retention` and `record(input)`. |
| `onFailure` | Optional isolated sync/async observer for mapping or recording failures. |
| `AuditRouteDeclaration.map` | Receives the framework request and successful response; returns an `AuditEntryInput`. |

Module construction validates that the recorder carries an immutable `AuditRetentionPolicy`, failing before startup when it is absent or malformed. `forRootAsync({ imports, inject, useFactory })` supports DI-based composition.

The interceptor records successful responses only. It is queued/best effort: failures reach `onFailure` and never turn an already-committed mutation into an HTTP failure.

## Boundary

Use transactional `@playstack/events` audit handlers for security-critical package operations. This adapter does not infer important operations, replace domain events, install itself globally, or own persistence; application routes opt in explicitly.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-audit@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-audit` | `./dist/index.d.ts` |
| `@playstack/nest-audit/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/audit` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-registry

Source: https://playstack.dev/docs/packages/infrastructure/nest-registry.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-registry` supplies sync and async DI registration for the read-only npm registry protocol, a Node HTTP streaming boundary, and an ejectable Express controller. It does not create credentials, persistence, buckets, subscriptions, or public routes.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-registry@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Register

```ts
import { PlaystackRegistryModule, type NestRegistryOptions } from '@playstack/nest-registry'

PlaystackRegistryModule.forRootAsync({
  imports: [ApplicationRegistryModule],
  inject: [APPLICATION_REGISTRY_OPTIONS],
  useFactory: (options) => options as NestRegistryOptions,
})
```

Supply `NestRegistryOptions`: the core npm options (`registry`, trusted `baseUrl`, `authenticate`, `resolvePackage`, `download`) and optional bounds and reporting. Eject `templates/registry.controller.ts.template`, register it in the module that imports `PlaystackRegistryModule`, and review its path against `baseUrl`, including any Nest global prefix. The module is not global and registers no controller itself.

## Transport rules

The controller owns exactly one response per request and never reads bodies: writes are unsupported and answer 501. `handleNode` preserves the original encoded request path, rejects normalized or absolute-form targets, takes the public origin only from `baseUrl`, and passes raw duplicate Authorization headers to the core's strict parser. Core ETags, private `no-store` headers, status codes, and the signed-redirect policy are preserved; nothing buffers a whole tarball. Defaults are an 8 KiB request-target limit and a 60-second whole-response deadline; disconnects abort the download stream.

## Boundary

Keep the application's rate limits, proxy policy, and header and body limits in front of this route. Do not add interceptors that serialize or compress tarball bytes or guards that redirect to a browser login. Fastify is not qualified.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-registry@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-registry` | `./dist/index.d.ts` |
| `@playstack/nest-registry/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/nest-registry/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/registry` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/auth-authjs

Source: https://playstack.dev/docs/packages/identity/auth-authjs.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/auth-authjs` connects successful Auth.js provider callbacks to application-owned Playstack authentication. It does not configure providers, replace Auth.js routing, or create a second identity database.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/auth-authjs@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Configure the bridge

```ts
import { createAuthJsBridge } from '@playstack/auth-authjs'

const bridge = createAuthJsBridge({
  normalizeProfile: ({ provider, providerAccountId, profile }) => ({
    providerId: provider,
    providerUserId: providerAccountId,
    email: profile.email,
    emailVerified: profile.email_verified === true,
    raw: profile,
  }),
  authenticate: (profile) => auth.authenticateProfile(profile),
  resolveAccount: (userId) => activeAccountFor(userId),
  onAuthenticated: (user) => accounts.ensurePersonalAccount(user.id),
})
```

Attach `bridge.callbacks` to either an Auth.js Pages Router configuration or App Router factory. The bridge requires JWT sessions because its callback owns the encrypted Playstack principal.

`CredentialService.authenticateProfile()` is the canonical destination for
the normalized result. Use `authenticateProvider()` only when Playstack also
owns the authorization-code exchange. Both paths share the same verified-email,
identity-linking, reservation, transaction, and event rules.

## Boundary

Provider normalization and verified-email policy remain explicit. The session callback adds only `session.playstack`, an exact `AuthSessionView` without provider tokens or raw profile data. Provider handlers, cookies, middleware, and application module augmentation remain application-owned.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/auth-authjs@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/auth-authjs` | `./dist/index.d.ts` |
| `@playstack/auth-authjs/errors` | `./dist/errors.d.ts` |
| `@playstack/auth-authjs/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/auth` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `next-auth` | `>=4.24.0 <6` | Optional; only for the entry points that use it. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-feedback

Source: https://playstack.dev/docs/packages/events-operations/nest-feedback.md

{/* package-access:start */}

> **Paid.** Covered by the Playstack Pro License; access and tier assignment are not yet announced. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-feedback` mounts `POST /v1/feedback`, the anonymous intake route `playstack feedback` posts to, and exports the intake for hosts that need it elsewhere.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-feedback@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Configure

```ts
import { Module } from '@nestjs/common'
import { PlaystackFeedbackModule } from '@playstack/nest-feedback'
import { PrismaFeedbackStore } from '@playstack/feedback/prisma'

@Module({
  imports: [
    PlaystackFeedbackModule.forRoot({
      store: new PrismaFeedbackStore(prisma, { outbox }),
      limiter: { consume: ({ ip }) => limiter.consume('feedback.ip', { subjects: { ip } }) },
      hashAddress: (ip) => keyedHash(ip),
      // The client address per your reviewed proxy policy; without it the
      // socket address is used and forwarded headers are never trusted.
      clientAddress: (request) => trustedClientAddress(request),
    }),
  ],
})
export class ApplicationModule {}
```

`forRootAsync({ imports, inject, useFactory })` resolves the same options from application providers. The module is not global and exports `NEST_FEEDBACK_OPTIONS` and `PLAYSTACK_FEEDBACK_INTAKE`.

## HTTP contract

| Outcome | Response |
| --- | --- |
| stored | `202 { id, status: 'accepted' }` |
| known id | `202 { id, status: 'duplicate' }`, nothing written |
| malformed or empty body | `400 { error: 'malformed_json' }` |
| schema failure | `400 { error: 'invalid_report', detail }` |
| body over `maxBodyBytes` (64 KiB default) | `413` |
| address limited | `429` with `Retry-After` and `RateLimit-*` headers |
| storage or limiter failure | `503` |

The route reads the client address before the body, so a limited address never costs a parse. The address is hashed with the host's keyed hash before it is stored.

## Boundary

The package chooses no proxy policy: supply `clientAddress` from the host's reviewed number of trusted hops. Authentication is deliberately absent; the CLI submits anonymously and the report's contact field is the only identity.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-feedback@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-feedback` | `./dist/index.d.ts` |
| `@playstack/nest-feedback/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/feedback` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/billing

Source: https://playstack.dev/docs/packages/identity/billing.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/billing` keeps a rebuildable local projection of provider-owned customers and subscriptions. Provider adapters own checkout, customer portals, and normalized webhook translation; entitlements remain the application-facing authorization boundary.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/billing@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Define plans and compose

```ts
import { createBilling, definePlans } from '@playstack/billing'
import Stripe from 'stripe'
import { createStripeBillingClient, stripeBilling } from '@playstack/billing/stripe'

const stripe = createStripeBillingClient(Stripe, {
  apiKey: stripeSecretKey, clock,
  httpClient: Stripe.createNodeHttpClient(),
  configuration: { maxNetworkRetries: 2, timeout: 20_000 },
})
const billing = createBilling({
  provider: stripeBilling(stripe, { instance: 'subscriptions', clock }),
  persistence,
  entitlements,
  entitlementConsistency: 'transactional',
  events,
  clock,
  plans: definePlans({
    pro: {
      prices: {
        monthly: {
          provider: 'stripe',
          instance: 'subscriptions',
          id: 'price_pro_monthly',
        },
      },
      entitlements: [{ entitlement: 'packages.pro', value: true }],
      trial: {
        durationMs: 14 * 24 * 60 * 60_000,
        graceMs: 7 * 24 * 60 * 60_000,
        graceEntitlements: [{ entitlement: 'workspace.read', value: true }],
      },
    },
  }),
})
```

The provider requires this dedicated client construction. It preserves the actual
SDK instance and normal non-Checkout behavior; native Checkout uses a fresh guarded
SDK per submission, with one POST admission bound to its original key and replay
window. An ordinary SDK client is rejected. Pass the same trusted clock throughout.
Custom transports must not retry or follow redirects internally. Native escape-hatch
calls remain explicitly unmanaged.

Create checkout and portal sessions only from authenticated routes. Pass only verified, normalized provider events from `@playstack/webhooks` to `handleWebhook()`.

## Configuration

| Property                 | Required | Purpose                                                                                      |
| ------------------------ | -------- | -------------------------------------------------------------------------------------------- |
| `provider`               | Yes      | One provider instance and its native-client escape hatch.                                    |
| `plans`                  | Yes      | Code-defined prices, grants, legacy plans, and optional local trials.                        |
| `persistence`            | Yes      | Customers, current subscriptions, immutable revisions, and trial eligibility.                |
| `entitlements`           | Yes      | Replaces independently scoped subscription and trial grant sources.                          |
| `entitlementConsistency` | Yes      | Selects `transactional` for a shared store or `eventual` for durable cross-store projection. |
| `events`, `clock`        | Yes      | Provider-neutral lifecycle events and deterministic time.                                    |
| `pastDueGraceMs`         | No       | Access grace applied to past-due subscriptions; defaults to seven days.                      |

Every provider reference includes `{ provider, instance }`. Compose separate
billing services for multiple Stripe accounts or other providers. Customer,
price, subscription, checkout, webhook, and entitlement-source identities stay
isolated by that instance.

## Subscription state and repair

```ts
const current = await billing.getSubscription(accountId)
const subscriptions = await billing.listSubscriptions(accountId)
const revisions = await billing.listSubscriptionHistory(accountId)

await billing.reconcileSubscription(accountId, current.id)
```

Each accepted projection stores the provider event ID and occurrence time.
Exact retries and older events are no-ops, while every accepted state is added
to immutable history. Scheduled provider transitions appear as `pendingPlan`,
`pendingProviderPriceId`, and `pendingChangeAt`; the current plan remains
authoritative until a later provider event applies the transition.

Reconciliation fetches the provider’s current subscription and sends it through
the same ordering, history, event, and entitlement path. The application cannot
write subscription state directly.

## Adopt an existing provider projection

```ts
const customer = await billing.previewCustomerAdoption({
  accountId,
  providerCustomerId,
})
const subscription = await billing.previewSubscription(accountId, providerSubscriptionId)

await billing.adoptCustomer({ accountId, providerCustomerId })
await billing.importSubscription(accountId, providerSubscriptionId)
```

Preview operations are report-only and include field-level drift. Import retrieves provider-owned state and runs it through the ordinary ordering, history, events, and entitlement path; callers cannot inject a status or plan. Running previews beside an existing projection is the supported shadow phase, and retaining the old tables until reports remain clean provides the rollback boundary.

## Period-end subscription commands

`createBillingSubscriptionCommands()` composes an optional durable journal with
the current Billing persistence and required account-user authority checks.
`prepare()` binds the exact actor/account/provider/subscription generation and
provider request before submission. `submit()` also recovers the original request
and key; `inspect()` returns authorized evidence. The Stripe adapter lives at
`@playstack/billing/subscription-commands/stripe`, with Prisma and memory journals
under the matching `/prisma` and `/testing` subpaths. Construct its dedicated
client with `createStripeSubscriptionCommandClient(Stripe, { apiKey,
httpClient: Stripe.createNodeHttpClient(), configuration: { timeout: 10_000 },
clock })` and pass the same clock to the adapter. Ordinary SDK clients are
rejected: Stripe's zero-retry setting alone still permits some connection retries.
Each explicit submission gets a fresh guard that checks the original key, path
and deadline and admits one POST to the base transport, including after body
failures. The Node HTTP transport is locally qualified; custom SDK constructors
and transports must be trusted and must not retry or follow redirects internally.

The cancellation profile supports `cancel-at-period-end` and
`withdraw-cancellation` for qualified classic single-item subscriptions. With the
server `plans` catalog, `change-plan` also supports immediate same-interval changes
that preserve quantity and anchor, explicitly selecting `always_invoice` and
`error_if_incomplete`. Prices resolve from that catalog; callers cannot supply
price IDs. Authorization receives the full resolved target. Provider qualification
requires an active uncanceled source, paid latest invoice and positive flat
licensed prices in the same currency/interval. Paid zero-dollar discounts remain
valid. Item identity and proration time are retained in exact-request recovery.

Apply the optional journal and `BillingSubscriptionCommandScope` fragment through
a reviewed application migration and update all writers together. Its accepted
generation prevents preparation from crossing another native completion while
projection lags; permanent keys and scope generations survive cleanup. New
reservation checks the captured generation under scope-before-journal locking.
Unknown outcomes keep an account/provider barrier and the original key. Replay
ends at the earlier of 23 hours and the captured period end; source changes need
review. A separate operator gate receives the exact record/key/decision to bind
reviewed evidence, then an expected revision guards `reconcile()`.

Command acknowledgment never updates grants or subscription facts. Verified
webhooks/authoritative reconciliation remain responsible for those. Provider GET
state, elapsed time and arbitrary SDK errors do not prove safe rejection.
Configure bounded SDK timeouts and shutdown separately. Native locks do not
serialize Portal/Dashboard writers or provide provider-side compare-and-swap;
live Stripe qualification remains separate from the local checks. Cross-interval,
pending-payment/SCA, quantity-changing and other unsupported financial workflows
retain the qualified Portal/provider path. Existing
provider/Portal compositions remain supported.

## No-card trials

```ts
const trial = await billing.startTrial({ accountId, plan: 'pro' })
const currentTrial = await billing.getTrial(accountId)
await billing.extendTrial({ accountId, expectedEndsAt: trial.endsAt, endsAt: reviewedSupportDeadline })
await billing.cancelTrial(accountId)
```

The trial’s full grants expire at `endsAt`; explicitly smaller grace grants
start then and expire at `graceEndsAt`. Time-bounded entitlements move the
account from active to grace to expired without a timer. Trial eligibility is
create-once per account, and an active paid subscription cancels its trial
sources. Support extension uses an absolute deadline and expected current
deadline, preserving plan, original start and grace duration. Exact-target replay
does no extra work; stale targets conflict. Canceled trials cannot be revived.
An expired uncanceled trial can be deliberately extended into the future;
support authorization and limits remain application policy.

Explicit `startTrialInTransaction`, `getTrialInTransaction`,
`extendTrialInTransaction` and `cancelTrialInTransaction` methods join a caller
physical transaction. Mutations require transactional or disabled entitlement
projection, so state, events and local grants share the caller's commit. Return
values remain provisional until that commit; errors must abort it. Standalone
methods retain the existing eventual-projection option and its host-owned
reconciliation requirement.

## Lifecycle events

Billing emits provider-neutral events for subscription updates and terminal
cancellation, cancellation scheduling and resumption, scheduled and applied
plan changes, invoice payment, payment failure, provider trial ending, and
local trial start/extension/cancellation and durable subscription-command evidence.
Notification, audit, and reconciliation code
therefore does not need Stripe payload knowledge.

## Boundary

The provider remains the subscription source of truth, while product code checks stable entitlements rather than plan or price names. The application owns raw-body webhook routing and verification, redirects, migrations, reconciliation scheduling, and product-specific downgrade behavior. `billing.provider.client` exposes the exact native billing client.

## Durable checkout and usage

Checkout now requires an explicit, globally unique `idempotencyKey`. Persistence atomically reserves immutable request identity before provider I/O and records completed or unknown outcomes. `getCheckoutAttempt()` supports reconciliation. Retry ambiguous outcomes with the same key; use a new key only for an intentional new checkout, not to bypass an unknown result.

Trusted server code can include `terms: { metadata, expiresAt }` in a checkout
request. Terms are copied before asynchronous work, persisted and compared on
exact retries. Stripe applies metadata to both Checkout and the resulting
subscription; `accountId` and `plan` are reserved. Use opaque lifecycle/deployment
bindings, never bearer proofs, secrets or personal data. Host customer/checkout
mappings and current account/lifecycle authorization remain required.

`expiresAt` is an absolute Unix-millisecond deadline with whole-second precision.
Stripe requires 30 minutes–24 hours remaining at creation. When injecting a clock,
pass the same one to Billing and `stripeBilling(client, { clock })`. Retries and
stored-link reuse retain the original deadline, never a sliding TTL; elapsed
terms cannot admit a fresh checkout or an expired redirect. Prepared recovery can
reconcile the original provider operation within its original replay window;
it does not extend either deadline. Checkout expiry does not replace webhook lifecycle checks or
resolve an unknown provider outcome. These terms use the existing attempt JSON
field rather than requiring new columns.

`createBillingUsage()` records idempotent usage and claims bounded aggregates for provider reporting. Adopters supply the persistence/reporting seams and product allowance, overage and quota policies. A usage total is not an atomic quota reservation.

The Stripe v1 reporter requires `createStripeBillingUsageClient(Stripe, { apiKey,
httpClient: Stripe.createNodeHttpClient(), configuration: { timeout: 10_000 },
clock })`. Use the same trusted clock on the reporter. Each explicit report has
one guarded POST admission with the original identifier, key and replay window,
including after SDK connection/body failures. Ordinary SDK clients are rejected;
custom transports must not retry or follow redirects internally. Unknown outcomes
remain quarantined for reviewed reconciliation, and submission receipts do not
prove asynchronous invoice inclusion.

Subscription projection holds a customer lock across ordering checks, history and transactional entitlement projection. Stripe period normalization supports subscription-level and item-level period fields; the earliest item boundary is the conservative aggregate end. Refund/dispute policy and resource-license grants are separate from subscription billing. Test shadow/import/reconciliation and rollback against real application data before cutover.

## Customer and promotion settings

The Stripe provider's checkout input selects either an existing `customerId` or
an authorized `customerEmail` for Checkout-owned customer creation. The latter
preserves Stripe's `customer_email` behavior without inventing a customer ID.
These profiles are exclusive. The higher-level Billing service retains its
`ensureCustomer({ email })` profile and creates/adopts the customer before checkout.
Applications that already own canonical customer/checkout mappings can use the
provider profile with their existing durable journal and reconciliation policy.

`terms.allowPromotionCodes` explicitly preserves the chosen promotion-code
behavior in the prepared Stripe payload. Omitted, `false` and `true` remain
distinct immutable inputs. Fixed coupon selection and an explicit promotion-code
flag cannot be combined in the supported Stripe profile.

Load the original journal entry before applying fresh-request defaults. The
provider's pure `assertCheckoutReplay(prepared, input)` checks the original
commitment, key, customer selection, email, promotion settings and complete
canonical request before submission. It performs no provider I/O and does not
apply creation-time expiry rules to an existing request. Recovery submits the
original prepared bytes; changed same-key input requires refusal rather than a
new provider operation.

The provider does not own a key registry or authorize retries. The host journal
must serialize reservation, recheck live account/lifecycle authority, retain
permanently closed keys and reconcile uncertain/expired operations. Treat the
email-bearing prepared body as private retained material: erase it when eligible
while preserving its commitment and closed tombstone. Native closed-key rejection
and URL redaction remain part of the Billing service's mapped-customer profile.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/billing@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/billing` | `./dist/index.d.ts` |
| `@playstack/billing/errors` | `./dist/errors.d.ts` |
| `@playstack/billing/prisma` | `./dist/prisma.d.ts` |
| `@playstack/billing/testing` | `./dist/testing.d.ts` |
| `@playstack/billing/subscription-commands/prisma` | `./dist/subscription-command-prisma.d.ts` |
| `@playstack/billing/subscription-commands/testing` | `./dist/subscription-command-testing.d.ts` |
| `@playstack/billing/subscription-commands/stripe` | `./dist/subscription-command-stripe.d.ts` |
| `@playstack/billing/usage/prisma` | `./dist/usage-prisma.d.ts` |
| `@playstack/billing/usage/stripe` | `./dist/usage-stripe.d.ts` |
| `@playstack/billing/usage/testing` | `./dist/usage-testing.d.ts` |
| `@playstack/billing/stripe` | `./dist/stripe.d.ts` |
| `@playstack/billing/webhooks` | `./dist/webhooks.d.ts` |
| `@playstack/billing/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/entitlements

Source: https://playstack.dev/docs/packages/identity/entitlements.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/entitlements` projects grants from billing, licensing, registration, staff, or another authority into stable capabilities and limits. It does not own plans, prices, authentication, feature flags, or provider side effects.

```ts
const registry = defineEntitlements({
  'packages.pro': { kind: 'boolean' },
  'tokens.maximum': { kind: 'number' },
})

const entitlements = createEntitlements({
  registry,
  persistence,
  events,
  clock,
  ids,
})

await entitlements.replaceSource({
  subject: { type: 'account', id: accountId },
  source: { type: 'stripe', id: subscriptionId },
  grants: [{ entitlement: 'packages.pro', value: true }],
})

const decisions = await entitlements.resolveMany(
  { type: 'account', id: accountId },
  ['packages.pro', 'tokens.maximum'],
)
const snapshot = await entitlements.snapshot({
  type: 'account',
  id: accountId,
})
```

Boolean grants combine with OR and numeric grants by maximum. Replacing one source affects only that projection; replacing it with no grants revokes it. The package emits transactional `entitlements.grants.replaced` events and publishes an ejectable Prisma fragment plus `@playstack/entitlements/prisma`.

Both bulk reads use one persistence lookup rather than querying once per
entitlement. `replaceSourceInTransaction()` lets Billing or another
same-database authority update grants on an existing transaction. It verifies
the transaction kind and emits the ordinary grants-replaced event on that unit
of work; separate stores use the normal idempotent source replacement path.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/entitlements@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/entitlements@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/entitlements` | `./dist/index.d.ts` |
| `@playstack/entitlements/errors` | `./dist/errors.d.ts` |
| `@playstack/entitlements/prisma` | `./dist/prisma.d.ts` |
| `@playstack/entitlements/testing` | `./dist/testing.d.ts` |
| `@playstack/entitlements/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/github-app

Source: https://playstack.dev/docs/packages/identity/github-app.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/github-app` creates, scopes, caches, and refreshes short-lived GitHub App installation tokens. It does not wrap Octokit, persist installations, receive raw webhooks, or decide which repositories a subscriber may access.

```ts
const githubApp = createGitHubApp({
  appId: env.GITHUB_APP_ID,
  privateKey: env.GITHUB_APP_PRIVATE_KEY,
  crypto: githubJwtSigner,
  client: githubInstallationClient,
  cache,
  clock,
})

const access = await githubApp.getInstallationToken({
  installationId,
  repositoryIds,
  permissions: { contents: 'read' },
})
```

The service backdates and bounds App JWTs, refreshes installation tokens before expiry, and keys cached access by installation plus requested scope. Verified installation deletion or suspension events from `@playstack/webhooks` invalidate related cached credentials. Repository policy and reconciliation remain application-owned.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/github-app@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/github-app@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/github-app` | `./dist/index.d.ts` |
| `@playstack/github-app/errors` | `./dist/errors.d.ts` |
| `@playstack/github-app/testing` | `./dist/testing.d.ts` |
| `@playstack/github-app/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/client-auth

Source: https://playstack.dev/docs/packages/identity/client-auth.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/client-auth` is the runtime-neutral public-client half of
Playstack’s authorization-code profile. It never embeds a client secret or owns
the application’s login, MFA, SSO, or consent interface.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/client-auth@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose a client

```ts
import {
  createClientAuth,
  createFetchTokenTransport,
} from '@playstack/client-auth'

const auth = createClientAuth({
  clientId: 'desktop-app',
  authorizationEndpoint: 'https://app.example.com/oauth/authorize',
  redirectUri,
  scopes: ['profile', 'devices'],
  transport: createFetchTokenTransport({
    tokenEndpoint: 'https://api.example.com/oauth/token',
    revocationEndpoint: 'https://api.example.com/oauth/revoke',
  }),
  storage: platformCredentialStorage,
  launcher: systemBrowserLauncher,
})

await auth.authorize()
const response = await auth.request(request, apiTransport)
await auth.signOut()
```

Every attempt generates S256 PKCE material and callback state. Completion
verifies the returned state before exchanging the code. Access credentials
refresh before expiry, concurrent refreshes share one operation inside the
configured coordinator, and an authenticated request retries once after an
unauthorized response. A failed refresh clears stored credentials; sign-out
attempts remote revocation and always clears local state.

## Configuration and bridges

| Property | Required | Purpose |
| --- | --- | --- |
| `clientId`, `authorizationEndpoint`, `redirectUri`, `scopes` | Yes | Registered public-client identity and requested authorization. |
| `transport` | Yes | Code exchange, refresh, and optional revocation. |
| `storage` | Yes | Platform-appropriate durable credential storage. |
| `launcher` | For `authorize()` | Opens the system or extension browser and returns the callback URL. |
| `refreshCoordinator` | No | Cross-context or cross-process rotation lock; defaults to one JS process. |
| `pkceCrypto`, `now`, `expirySkewMs` | No | Runtime crypto and deterministic policy overrides. |

`@playstack/client-auth/loopback` is Node-only. It binds `127.0.0.1` on an
ephemeral port, accepts one callback, times out, and closes immediately. Browser
extensions should use `@playstack/extension` for their launcher, credential
storage, and cross-context refresh lock.

## Boundary

The package does not register clients, authorize a user, host an OAuth route,
choose a keychain, or host device authorization endpoints. Those server boundaries
live in `@playstack/auth` and `@playstack/nest-auth`; the host supplies secure
storage and browser-launch behavior.

## Device authorization is available

`@playstack/client-auth/device` exports `beginDeviceAuthorization()`: supply a reviewed transport, display the returned user code/verification URL, then explicitly call `waitForTokens()`. Polling observes slow-down, expiry, cancellation and classified connection timeouts. It does not expose the device code/verifier or introduce a second refresh algorithm.

Server grants use `@playstack/auth/device`; the transport validates token responses and forwards AbortSignal. Successful issuance remains one-time. A lost committed response requires new consent. OS storage and multi-process refresh coordination are yours to verify in the application.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/client-auth@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/client-auth` | `./dist/index.d.ts` |
| `@playstack/client-auth/loopback` | `./dist/loopback.d.ts` |
| `@playstack/client-auth/device` | `./dist/device.d.ts` |
| `@playstack/client-auth/testing` | `./dist/testing.d.ts` |
| `@playstack/client-auth/vectors.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/client-auth/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-billing

Source: https://playstack.dev/docs/packages/identity/nest-billing.md

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-billing` binds an existing `@playstack/billing` service to request-scoped account resolution. It installs no routes, authentication guards, webhook verification, or redirect policy.

```ts
PlaystackBillingModule.forRoot({
  billing,
  resolveAccount: (request) => requireBillingAccount(request),
})
```

Inject `PlaystackBillingService` into authenticated controllers. Checkout and portal calls resolve the current account before delegating to the core service; webhook handlers accept only events already verified by `@playstack/webhooks`. `forRootAsync` supports Nest-managed dependencies while keeping the module non-global.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-billing@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-billing@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-billing` | `./dist/index.d.ts` |
| `@playstack/nest-billing/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/billing` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/extension

Source: https://playstack.dev/docs/packages/identity/extension.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/extension` supplies runtime primitives for a Playstack-connected
Manifest V3 extension. WXT owns scaffolding, entrypoints, manifests, bundling,
and store builds; Playstack owns boundaries that must survive a disposable
service worker.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/extension@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Compose authentication

```ts
import { createClientAuth } from '@playstack/client-auth'
import {
  createExtensionAuthorizationLauncher,
  createExtensionTokenStorage,
  createWebLockRefreshCoordinator,
} from '@playstack/extension'

const auth = createClientAuth({
  clientId: 'browser-extension',
  authorizationEndpoint,
  redirectUri: browser.identity.getRedirectURL(),
  scopes: ['profile'],
  transport,
  launcher: createExtensionAuthorizationLauncher(browser.identity),
  storage: createExtensionTokenStorage({
    session: browser.storage.session,
    local: browser.storage.local,
  }),
  refreshCoordinator: createWebLockRefreshCoordinator(navigator.locks),
})
```

Short-lived access credentials live in session storage. Refresh credentials
live in local storage, while a Web Lock serializes rotation across popup,
content-script, and service-worker contexts. The extension opens the
application’s existing browser authorization flow and never receives a user’s
password.

## Messaging, storage, and permissions

`defineMessages()` validates request and response payloads around typed runtime
handlers. `defineExtensionStorage()` and `createExtensionStorage()` validate
values every time they cross browser storage. `ensureExtensionPermissions()`
checks existing optional permissions before prompting and fails explicitly when
the user declines.

All APIs are structural: pass the browser facade supplied by WXT or another
extension runtime without importing that framework into Playstack.

## Boundary

Module memory and timers are never durable MV3 state. Register browser event
handlers at module evaluation, reconstruct state on wake, and use alarms for
long delays. `@playstack/lint` supplies explicit extension presets for remote
code, long worker timers, and mutable worker-memory state.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/extension@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/extension` | `./dist/index.d.ts` |
| `@playstack/extension/auth` | `./dist/auth.d.ts` |
| `@playstack/extension/messaging` | `./dist/messaging.d.ts` |
| `@playstack/extension/permissions` | `./dist/permissions.d.ts` |
| `@playstack/extension/storage` | `./dist/storage.d.ts` |
| `@playstack/extension/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/extension/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-entitlements

Source: https://playstack.dev/docs/packages/identity/nest-entitlements.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-entitlements` resolves a request subject and enforces named capabilities through NestJS guards. It does not authenticate requests or infer account context from route parameters.

```ts
PlaystackEntitlementsModule.forRoot({
  entitlements,
  resolveSubject: (request) => ({
    type: 'account',
    id: request.auth.account.id,
  }),
})

@UseGuards(PlaystackEntitlementsGuard)
@RequireEntitlements('packages.pro')
getProDownload() {}
```

Successful resolution is attached to `request.entitlementSubject`. Authentication, account scope, module ordering, and application-specific denial handling remain explicit composition concerns.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-entitlements@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-entitlements@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-entitlements` | `./dist/index.d.ts` |
| `@playstack/nest-entitlements/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/entitlements` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/extension-react

Source: https://playstack.dev/docs/packages/identity/extension-react.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/extension-react` exposes client-only authorization state for an
extension popup or options page without persisting React state or owning the
product interface.

```tsx
import {
  PlaystackExtensionAuthProvider,
  useExtensionAuth,
} from '@playstack/extension-react'

function Popup() {
  const { status, tokens, authorize, restore, signOut } = useExtensionAuth()
  // Render application-owned loading, signed-out, error, and account states.
}

<PlaystackExtensionAuthProvider client={clientAuth} onError={reportError}>
  <Popup />
</PlaystackExtensionAuthProvider>
```

The provider restores through `@playstack/client-auth`, so expired access
credentials rotate through the extension’s cross-context coordinator. It
exposes `loading`, `anonymous`, `authenticated`, and `error` states plus
`authorize()`, `restore()`, and `signOut()` actions. The application owns
navigation, consent copy, error presentation, and every visual component.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/extension-react@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/extension-react@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/extension-react` | `./dist/index.d.ts` |
| `@playstack/extension-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/client-auth` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/extension` | `0.1.0-beta.4` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-github-app

Source: https://playstack.dev/docs/packages/identity/nest-github-app.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-github-app` registers an existing `@playstack/github-app` service behind a stable NestJS injection token. It adds no routes, webhook verification, persistence, repository authorization, or global module.

```ts
@Module({
  imports: [PlaystackGitHubAppModule.forRoot(githubApp)],
})
export class GitHubModule {}

@Injectable()
export class RepositoryAccess {
  constructor(
    @Inject(GITHUB_APP_SERVICE)
    readonly github: GitHubAppService,
  ) {}
}
```

`forRootAsync` supports services assembled from Nest-managed configuration. The injected service retains its client, crypto, cache, and verified-webhook escape hatches.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-github-app@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-github-app@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-github-app` | `./dist/index.d.ts` |
| `@playstack/nest-github-app/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/github-app` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/licensing

Source: https://playstack.dev/docs/packages/identity/licensing.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/licensing` verifies Playstack license credentials without issuing licenses, storing private signing keys, processing payments, or validating App Store receipts.

```ts
import {
  RECOMMENDED_OFFLINE_GRACE_SECONDS,
  WebCryptoLicenseVerifier,
  createLicensing,
} from '@playstack/licensing'

const licensing = createLicensing(
  new WebCryptoLicenseVerifier({
    subtle: crypto.subtle,
    keys: { 'production-2026': publicKeyBytes },
  }),
)

const result = await licensing.validate({
  token,
  product: 'quickzones',
  deviceId,
  now: Math.floor(Date.now() / 1_000),
  highestTrustedTime,
  offlineGraceSeconds: RECOMMENDED_OFFLINE_GRACE_SECONDS,
  isRevoked: (licenseId) => revocations.has(licenseId),
})
```

Grace policy is explicit; omitting it means zero grace. `LicenseSignatureVerifier` supports native key stores, and `@playstack/licensing/vectors` publishes deterministic cases for TypeScript, Swift, and Rust implementations.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/licensing@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/licensing@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/licensing` | `./dist/index.d.ts` |
| `@playstack/licensing/errors` | `./dist/errors.d.ts` |
| `@playstack/licensing/testing` | `./dist/testing.d.ts` |
| `@playstack/licensing/vectors` | `./dist/vectors.generated.d.ts` |
| `@playstack/licensing/vectors.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/licensing/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/licensing-issuer

Source: https://playstack.dev/docs/packages/identity/licensing-issuer.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/licensing-issuer` turns application-owned purchase policy into signed, distributable licenses. It does not process payments, expose routes, send mail, or decide product terms.

```ts
import { createLicensingIssuer } from '@playstack/licensing-issuer'

const issuer = createLicensingIssuer({
  persistence,
  authorizer: applicationLicensePolicy,
  distribution: transactionalDeliveryOutbox,
  signer: kmsBackedEd25519Signer,
  secrets,
  responseVault,
  events,
  clock,
  ids,
})
```

Activation codes are returned once and stored only as keyed digests. Activation and refresh responses are sealed for exact idempotent retries. Private key custody remains behind `LicenseSignatureSigner`; `LicenseResponseVault` must encrypt response material at rest.

Use `@playstack/licensing-issuer/prisma` for the managed PostgreSQL contract. The application owns migrations, entitlement-to-license policy, HTTP routes, distribution delivery, and key management.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/licensing-issuer@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/licensing-issuer@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/licensing-issuer` | `./dist/index.d.ts` |
| `@playstack/licensing-issuer/errors` | `./dist/errors.d.ts` |
| `@playstack/licensing-issuer/testing` | `./dist/testing.d.ts` |
| `@playstack/licensing-issuer/prisma` | `./dist/prisma.d.ts` |
| `@playstack/licensing-issuer/playstack.artifacts.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/licensing-issuer/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}

## @playstack/nest-licensing

Source: https://playstack.dev/docs/packages/identity/nest-licensing.md

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-licensing` binds an existing `@playstack/licensing-issuer` service to Nest without selecting authentication, payment policy, persistence, or routes.

```ts
PlaystackLicensingModule.forRoot({
  licensing: issuer,
  authorizeAdministration: (request, operation, resourceId) =>
    licensingPolicy.authorize(request, operation, resourceId),
  resolveContext: (request) => operationContext(request),
})
```

`PlaystackLicensingClientService` exposes activation, refresh, and deactivation for application controllers. `PlaystackLicensingAdminService` exposes authorized issuance, revocation, and seat inspection. Use `forRootAsync()` when issuer composition depends on Nest-managed providers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-licensing@0.1.0-beta.4
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-licensing@0.1.0-beta.4`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-licensing` | `./dist/index.d.ts` |
| `@playstack/nest-licensing/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.4` | Required by the package. |
| `@playstack/licensing-issuer` | `0.1.0-beta.4` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}
