Composable identity and registry access

This batch comes from dogfooding: the Playstack dashboard and API were built by composing the packages, and every place that needed an app-level patch became a package fix instead.

Split-origin sessions

@playstack/auth-react now documents and supports the bearer contract end to end. The transport accepts a plain-http loopback baseUrl for local development, useAuth() exposes refresh(), and the split-origin guidance no longer expects a CSRF header: a cross-origin page cannot read a cookie set on the API origin, so the API checks Origin and Sec-Fetch-Site instead.

@playstack/accounts-react accepts a sessionParser on the provider and any callback as onSessionChange, so an account switch with the bearer transport is one line: pass refresh and the transport adopts the rotated refresh cookie.

Smaller edges

@playstack/auth reads users without opening a transaction. The WebCrypto interfaces of @playstack/crypto and @playstack/api-keys are satisfied by globalThis.crypto everywhere without casts. CaptureMailProvider in @playstack/mail/testing gains rawMessages() and templateMessages(). @playstack/nest-feedback takes a clientAddress policy for hosts behind a proxy. Revocation in @playstack/api-keys is documented as idempotent, and @playstack/events and @playstack/audit document when an outbox row is written and how audit actions are named.

Feedback from the CLI

playstack feedback sends a bug report, idea, or question, and after a failed command playstack feedback --last attaches the redacted failure record. @playstack/feedback owns the report contract and durable intake; @playstack/nest-feedback serves it at POST /v1/feedback.

Registry access and licensing

Free packages are MIT and install from public npm. Paid packages are served from the Playstack registry under the Playstack Pro License, with account registry tokens issued from the dashboard, and access to releases published while a subscription was active is retained after it ends.

One shared memory transaction

@playstack/core/testing now owns the in-memory unit of work every package's memory persistence adapter joins. A transaction opened through one package's adapter can be passed to another package's adapter and both commit or roll back together, so composed flows can be tested without a database. Every memory adapter exposes state(transaction?).

Registered devices by injection

@playstack/devices now satisfies the DeviceSessionValidator contract of @playstack/auth directly through assertActive, so deviceValidator: devices needs no adapter. @playstack/nest-auth accepts the service through Nest injection: provide it under NEST_DEVICE_SESSION_VALIDATOR or as the devices option, and pass sessions as a factory that receives it. The auth guard answers a validation failure thrown by the strategy, such as a revoked device, with 401, and resolves its Reflector explicitly so builds without emitted decorator metadata keep working.

Bearer clients on split-origin routes

@playstack/nest-auth gains PlaystackOriginGuard, the defence for the split-origin profile where no CSRF cookie can be read: unsafe browser requests must present an allowed Origin and non-cross-site Fetch Metadata, while a request with no Origin, no Fetch Metadata and no cookie carries no ambient credential and passes. CLIs, mobile apps and servers can therefore call the same identity, account and member routes with a bearer token, and isBrowserRequest lets a host choose a transport per client, for example returning the whole token pair in the body to a client that cannot hold a cookie. The four other Nest bindings that read route metadata now inject Reflector explicitly, so they work in builds without decorator metadata.

Guards composed by the application

@playstack/nest-auth adds PlaystackGuardStack: name a default composition per transport profile (guards: 'cookie' or 'bearer') or list your own ordering of package guards, other Playstack bindings and application guards, then place the stack on a controller or install it globally with globalGuards: true and @Public() opt-outs. Each guard is resolved once from the application's providers, evaluated in order, and the first denial ends the request; a bad composition fails at bootstrap. The individual guards stay exported, so hand-placed layering keeps working and no package installs a guard on its own.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software