Skip to content

Latest commit

 

History

History
1510 lines (1123 loc) · 123 KB

CHANGELOG.md

File metadata and controls

1510 lines (1123 loc) · 123 KB

seamless-cli

0.30.0

Minor Changes

  • 7b2d4ea: Add seamless webhooks, seamless hooks, seamless claims and seamless audit-sinks to manage signed webhooks and their deliveries, post_login and pre_token login hooks, static custom claims, and audit log streaming to OTLP and syslog (syslog needs auth server v0.23.1, which v0.23.0 refuses). Signing secrets are printed once, when they are issued. hooks set and audit-sinks update change only the settings a flag names, and claims set asks before removing a claim. Every command takes --json. They need auth server v0.23 or later, and an older instance gets a message saying so.
  • 612b37b: New projects scaffold Seamless Auth API v0.23.0, the admin dashboard v0.14.0 and the starter templates v0.22.0, so a fresh project has roles and permissions in its tokens, the starters' permission guard demo, and the console's Roles and Integrations pages. Existing projects keep the images they were scaffolded with.

0.29.0

Minor Changes

  • 2eb878e: Add seamless permissions and seamless roles to manage the permission catalogue, organization roles, and the permissions each instance or organization role grants. roles set replaces a role's whole mapping and asks before taking a permission away. Every command takes --json. They need auth server v0.23 or later, and an older instance gets a message saying so.

Patch Changes

  • 5c60a5f: seamless init scaffolds admin dashboard v0.13.0, which adds the Roles page for permissions and organization roles, pinned to the commit and image digest its tag names.

0.28.0

Minor Changes

  • 5e62f4a: seamless profile login signs in through the OpenID Provider, which brings passkey sign-in to the CLI. It opens the system browser with a loopback redirect, and --device uses the device grant for a machine with no browser. Either way the CLI exchanges the sign-in for a first-party session of its own at POST /sessions/exchange and stores that in the OS keychain, keeping no token from the provider. Needs auth server v0.23 and the provider edge.

    The CLI reads the provider from oidcProvider on GET /system-config/public when the instance advertises it. On one that does not, pass --oidc-issuer and --client-id once, to seamless profile login or seamless profile add. Flags win over what the profile saved, and that wins over the advertisement, so an instance that stops advertising cannot downgrade a profile. With no provider anywhere the email code stays, and --email-code asks for it.

    Every provider sign-in prints the issuer it uses. The CLI asks before using an advertised issuer on another origin than the instance, and without a terminal it stops and asks for the flags. It reads the advertisement only over https or on loopback, so an instance on plain http needs the flags.

    Every sign-in now checks the OS keychain before it starts, including seamless login and the email code, and fails early on a machine with none, rather than after a browser approval or an emailed code.

    seamless clients create and update gain --session-exchange and --no-session-exchange, and get shows the setting.

    A browser session can step up with a passkey as well as the authenticator app. A device session uses the authenticator app only, and needs a step-up before admin and organization commands, reads included.

Patch Changes

  • 3aea9df: The adapter conformance suite covers permission guards, under the pending contract permission-guards. It holds a reference app's new /api/permissions routes to one behavior: exact matching, any-of and all-of, organization scope bound to the route, removal from an organization reaching the guard at the next token, and the live read when a token omits its permissions. The cases are skipped unless a run opts in with SEAMLESS_VERIFY_CONTRACTS, because they need the auth API at v0.23. The Express and Fastify reference apps add the routes only when the installed adapter has requirePermission.

0.27.2

Patch Changes

  • 3890db9: The adapter conformance suite now sends x-seamless-auth-transport: cookie on its cookie-transport requests, as @seamless-auth/client does, so body-less POST and DELETE calls pass the content-type gate server adapters add in v0.23 (fells-code/seamless-auth-api#558). Two new cases hold adapters to the request guards: a cross-site unsafe request answers 403 cross_site_request_blocked and leaves the session alone, and a body-less form-encoded POST /auth/recovery-codes without the transport header answers 415 unsupported_media_type. The 415 case is a pending contract: it is skipped unless SEAMLESS_VERIFY_CONTRACTS=content-type-gate (or all) is set, so an adapter that has not released the gate yet keeps passing. verify/CONFORMANCE.md lists pending contracts. The conformance grid no longer counts a skipped case as a failure.

    Requests that ask for external delivery (x-seamless-auth-delivery-mode: external) now carry a service token with the seamless-auth-external-delivery subject, as the server adapters send it, so the suite keeps passing once the API requires that subject (fells-code/seamless-auth-api#556).

  • ba479f2: Depend on @seamless-auth/types ^0.35.0, which adds the OpenID Provider session binding contract.

0.27.1

Patch Changes

  • 3c94d99: seamless init scaffolds templates v0.21.2, whose API and full-stack starters declare the v0.21 server adapters: @seamless-auth/express 0.23, @seamless-auth/fastify 0.14, @seamless-auth/nextjs 0.7, and seamless-auth 0.4 for Go, Rust and Python.

0.27.0

Minor Changes

  • 0413554: seamless clients shows whether an application requires PKCE and the nonce, and create and update accept --pkce/--no-pkce and --nonce/--no-nonce. Only a confidential client can turn PKCE off, and the nonce can be turned off only once PKCE is off. An application with both off relies only on state and client authentication against authorization code injection, so list, get, create and update warn about it. Pins @seamless-auth/types 0.34.0.

  • d6304a1: Set hosted page branding from the CLI (#320). seamless config hosted-ui get|set|reset (alias branding) reads the instance's hosted_ui system config, merges the fields you pass (--app-name, --logo, --font-family, and the six color flags) into the current branding, validates the result with HostedUiSchema from @seamless-auth/types, and writes the whole object back, since the instance replaces it rather than merging. reset removes named fields or, after a confirmation (--force to skip), clears all branding. hosted_ui is also writable as a whole value with config set hosted_ui '<json>' or config set hosted_ui null. The logo must be an https URL where the image is hosted: the instance does not store uploaded files, so a local path is refused with that message. Needs auth server v0.21 or later; an older instance reports that it does not support branding. Bumps @seamless-auth/types to 0.34.0 and lists the four new OpenID Provider keys as not yet writable (#332).

  • 8aea8aa: seamless migrate assess <csv|entra-id|auth0|cognito> [source args] [--report <path>] [--json] writes a readiness report from the source alone, with no Seamless Auth instance and no login. It reuses each source's parsing and validation and reports users found, how many would import and why the rest would not (including duplicate emails), contact coverage (verified email, phone only, neither), the roles and organization memberships that would be granted and the fields left behind, source features Seamless Auth does not cover (such as Auth0 enterprise connections or Cognito SAML providers, plus what an export cannot show, such as Cognito identity pools) with a link to the matching guide, and a link to the docs cost calculator with the user count filled in. The Markdown report holds counts and names of fields, roles and organizations, never a user's email, phone or id. --json prints the same assessment.

  • 6b8105e: seamless migrate auth0 <export.ndjson> and seamless migrate cognito <list-users.json>... read an Auth0 user export and Amazon Cognito list-users output directly, so the migration guides no longer need a jq conversion to CSV. Both share the dry run, --apply, --report and --json of migrate csv, and read files only (no AWS or Auth0 SDK or credentials).

    • Auth0: user_id becomes the external id, with email and phone_number. A gzip-compressed export is read as downloaded. Roles and organizations come from app_metadata or user_metadata only through --map.
    • Cognito: the sub attribute becomes the external id, with email and phone_number. --groups turns the guide's username-to-groups map into roles (ignoring Cognito's per-provider groups), and --map can read an attribute such as custom:tenant into organizations. One response, the Users array, an array of responses, concatenated responses, and several chunk files are all accepted.

    Blocked or disabled users, users whose email the source marks unverified (--allow-unverified imports them) and unconfirmed Cognito sign-ups (--include-unconfirmed) are skipped and listed in the report. Users with no email, including phone-only users, are reported invalid. Unlike the guides' earlier jq recipes, unverified emails are no longer imported by default.

  • 471b05a: Configure an OAuth provider from its issuer. seamless config oauth-providers add and update take --issuer <url>, and a provider with an issuer can leave out authorizationUrl, tokenUrl, userInfoUrl, and jwksUri: the instance reads the missing ones from the issuer's /.well-known/openid-configuration. Without an issuer, add refuses before sending anything and names the missing endpoints. The new seamless config oauth-providers refresh <id> (alias discover) asks the instance to read the discovery document again and drop its cached signing keys, then prints the endpoints sign-in will use (--json for the provider object); like other provider writes it steps up when the instance asks. oauth-providers list shows a provider's issuer. Needs auth server v0.21 or later. Bumps @seamless-auth/types to 0.34.0, which also adds the oidc_account_notice_sent and oidc_account_notice_failed auth event types.

  • 411691e: Add seamless clients and seamless resources for the OpenID Provider. clients lists, creates, updates and deletes OIDC clients, rotates a shared secret (printed once), registers a key file or JWKS URI, and sets which resources and scopes a client may use. Public clients, loopback and private-use redirect URIs, the device grant and token TTLs are flags, and redirect URIs and cross-field rules are checked with the @seamless-auth/types schemas before the instance is asked. resources manages the APIs clients may get tokens for and their scopes. Writes use the existing step-up flow. seamless apps still means managed applications in the portal. Needs an auth API with the /admin/applications and /admin/resources routes.

  • 2e758a2: Write the OpenID Provider config keys (#332). oidc_provider_enabled, oidc_issuer, allow_client_secrets, and third_party_clients_enabled are now writable with seamless config set and config apply. config set checks their values before sending: the three switches take true or false, and oidc_issuer takes a URL, or null to clear it. seamless config --help documents them: the instance refuses to enable the provider until oidc_issuer is set, allow_client_secrets enables the weaker shared-secret client authentication (and seamless clients refuses shared-secret clients until it is on), and third-party clients need third_party_clients_enabled. Needs auth server v0.21.0 or later, whose strict patch schema is the first to accept these keys. Bumps @seamless-auth/types to 0.34.0.

Patch Changes

  • 9ce5813: Pin source downloads to commits. --auth=local now checks that the cloned seamless-auth-api tag is the commit this CLI pins and removes the clone, before anything in it runs, when it is not. --admin=source downloads the dashboard by its pinned commit and checks the archive's commit before unpacking. The auth server and dashboard images are referenced by digest as well as tag, and npm run check:pins checks every pin against its tag.
  • ff1736c: seamless init scaffolds auth API v0.21.0 and admin dashboard v0.12.0, pinned to the commits and image digests their tags name.
  • 877e90a: seamless init --auth=local and --auth=docker (and seamless add --local) now write a generated JWKS_KID such as local-3f9a0c1b2d4e instead of dev-main, so the scaffolded adapter no longer warns on every boot that its kid is a placeholder. Re-running init on an existing project keeps a kid it already wrote. The value is only the kid header on the service tokens the adapter signs with the service secret; nothing verifies against it.
  • 0bbdb1d: Scaffold templates v0.21.1, whose production images run as non-root and listen on port 8080, matching the compose file the CLI now generates.
  • 364bd55: Depend on @seamless-auth/types ^0.34.0, the stable release, instead of the 0.34.0 prerelease.
  • babb5bd: Map the web container to port 8080. The seamless-templates web and Next.js images now run as non-root and listen on 8080 instead of 80 (seamless-templates#133), so the generated compose file publishes 127.0.0.1:5173:8080 and health-checks /health on 8080, and the verify harness serves every web, reference, and Next.js app on 8080 (the nginx ones from nginxinc/nginx-unprivileged, as the templates do). This needs a templates release that carries the change: a scaffold from the templates pinned before it would map a port its web image no longer listens on.

0.26.0

Minor Changes

  • 02d9f0e: Admin writes step up with an authenticator app code, which the auth server requires from v0.20 (fells-code/seamless-auth-api#490). When an instance answers 403 step_up_required, the CLI asks for the code, verifies it with POST /totp/verify-mfa on the same session, and retries the write once. This covers config set, config apply, config oauth-providers add|update|remove, users delete, users prepare-device-replacement, the org and org members writes, and every batch of migrate. A wrong code can be re-entered up to three times; a lockout, a rate limit, and an account with no authenticator app enrolled each get their own message instead of the old "requires an admin role" one. Without a terminal, SEAMLESS_TOTP_CODE supplies the code for the first step-up of a run.

    Breaking: users prepare-device-replacement now requires --proofing <in_person|remote_exception> and --evidence-ref <ref> (with an optional --approver <name>), because the auth server rejects a device replacement that does not record how the user's identity was established.

  • 79590f0: New projects scaffold auth server v0.20.0, admin dashboard v0.11.0 and seamless-templates v0.21.0 (the v0.20 SDKs). These include the fixes for GHSA-qx82-28gf-w744, GHSA-9xg2-j8fg-427c, GHSA-v9q2-hcqj-ggqr, GHSA-v2q2-q246-8chm and GHSA-m5x9-cjv4-6q3w, first shipped in auth server v0.19.4 and seamless-templates v0.20.3.

    --auth=local now clones the auth server at the same v0.20.0 tag the Docker image is built from, instead of the tip of main.

  • d0f5f9c: config set and config apply now write recovery_codes and account_recovery, which auth server v0.20 accepts (#310). Both are objects, and a field left out takes its default, so send the whole object, for example config set account_recovery '{"enabled":true,"delay":"24h"}'. An instance older than v0.20 rejects a patch that carries either key.

  • 55a9fd2: seamless init keeps every secret out of the generated docker-compose.yml.

    • With --auth=docker, the auth server's environment is now written to auth/.env and loaded with env_file: ./auth/.env, the same file --auth=local already used. The compose file no longer has an inline environment: block for the auth service.
    • The api service (or the web service of a full-stack app) no longer sets SEAMLESS_SERVICE_TOKEN inline. init writes it to api/.env (or web/.env), which compose already loads.
    • init writes a root .gitignore covering .env, .env.* (keeping .env.example), keys/ and *.pem. An existing .gitignore keeps its lines and gains only the missing patterns.
    • seamless owner-code and the generated AGENTS.md point at auth/.env for OWNER_EMAIL in both auth modes. Projects scaffolded earlier, with the variable inline in compose, still work.

    The scaffold now also generates DECOY_SUBJECT_SECRET for the auth server, which v0.20 requires instead of falling back to the service token (OAUTH_STATE_SECRET is still generated whenever OAuth providers are configured).

    Every generated .env file (from init, add, template env and env merges) is written with mode 0600, and an existing file is tightened to 0600 when it is rewritten.

Patch Changes

  • 525ed2c: seamless verify steps up the owner's admin session with a software passkey before admin writes, which the auth server requires from v0.20 (fells-code/seamless-auth-api#490).
  • 3bd0e2d: Update @seamless-auth/types to 0.30.0.
  • 60ee807: Update @seamless-auth/types to 0.33.0.

0.25.1

Patch Changes

  • ec0ea10: New projects scaffold auth server v0.19.3, admin dashboard v0.10.1 and seamless-templates v0.20.2. The auth server binds the OAuth state to the client that started the sign-in, and the dashboard and every starter are on the SDKs that send that binding, so OAuth sign-in works end to end in a new project.

0.25.0

Minor Changes

  • e768ec0: New seamless migrate entra-id imports users straight from Microsoft Entra ID through Microsoft Graph, with the same dry run, batches and report as migrate csv. Each user's Entra object id becomes their externalId, so re-runs match people without a hand-edited export. It signs in as an app registration with app-only User.Read.All (and GroupMember.Read.All for groups), reading ENTRA_TENANT_ID, ENTRA_CLIENT_ID and ENTRA_CLIENT_SECRET from the environment only. --group imports one group's members, --groups maps groups to organizations and roles, disabled accounts and guests are skipped and reported, and --allow-upn uses the userPrincipalName for users with no mail.

    Migration reports gain a skipped count and status, for rows left out on purpose. Skipped rows do not make the command exit 1.

Patch Changes

  • ce96113: seamless init now creates the directories the generated compose file keeps in a volume (node_modules under each bind-mounted source tree, and .next for the Next.js full-stack layout) before Docker does. On a Linux host Docker created them as root, so npm install on the host in api/ or web/ failed with EACCES until they were removed with sudo. A directory that already exists keeps its owner.
  • 1ce71d7: seamless verify now forwards the binding that POST /oauth/:providerId/start returns in the body of its OAuth callbacks, so the OAuth specs pass against an auth API that ties the callback to the browser that started the flow. Older API images ignore the extra field.
  • bce0a9b: seamless init --auth=local now produces an auth service that starts. The generated compose bind-mounts ./auth over /app, which hid the image's chmod +x ./validateEnvs.sh (the script is mode 644 in the clone) and left the entrypoint looking for a dist/server.js that Dockerfile.dev never builds. The auth service now runs the same command as the auth server's own docker-compose.dev.yml: sync dependencies, run migrations (creating the database on first boot), then watch the TypeScript sources.
  • 888a781: Depend on @seamless-auth/types 0.29.0, which adds the OAuth state binding field.

0.24.0

Minor Changes

  • 9e63557: New seamless owner-code command prints a one-time code for the owner of a local stack, so the first admin finishes registering without reading container logs. Register in the browser with the owner address, and when the page asks for a code, run npx seamless-cli owner-code from the project directory. It waits for the auth server, reads OWNER_EMAIL from auth/.env or docker-compose.yml (or takes an address), and only talks to a local auth server.

    seamless init now ends with that step, and the generated AGENTS.md names it in its first admin section.

  • 59d583d: Applications now read their service token as SEAMLESS_SERVICE_TOKEN, the name the managed portal already uses and the auth server's own name for it since 0.19.0. The generated auth server environment uses it too.

    • seamless init: the generated docker-compose.yml gives the api service (or a full-stack web service) SEAMLESS_SERVICE_TOKEN, with a comment saying it is the same secret as the auth service's. Connecting a managed application writes SEAMLESS_SERVICE_TOKEN into api/.env, and the values printed when there is no api/ directory use the new name.
    • seamless add: the backend's .env gets SEAMLESS_SERVICE_TOKEN, and the printed Node, Go, Rust and Python code reads it.
    • seamless verify: the Express and Fastify reference apps read SEAMLESS_SERVICE_TOKEN, and the Next.js starter is given both names so starters from before the rename still run.

    Existing projects keep working. When a project's .env already has API_SERVICE_TOKEN and not SEAMLESS_SERVICE_TOKEN, seamless add and seamless init (connecting a managed application) update API_SERVICE_TOKEN in place, say so, and print code that reads that name, rather than adding a second variable the app does not read.

    The starters in seamless-templates that read SEAMLESS_SERVICE_TOKEN require this version.

Patch Changes

  • e781293: seamless org members add now says the user is already a member when the instance answers 409, instead of a bare Request failed (409). (an organization's creator is a member automatically, so adding yourself after org create hits this). A 404 on add no longer claims the organization is missing when it is the user. The other org and users commands name the action that failed and include the instance's own error message, for example Could not update member (400: Organization must keep at least one owner).

  • a672c49: New projects scaffold auth server v0.19.1, admin dashboard v0.10.0 and seamless-templates v0.20.0. The auth server includes the fixes for GHSA-9jg2-jjqq-3346 and GHSA-66gq-4pwg-33jq, and the starters are on the SDKs that work with its cross-device magic link confirmation.

    The generated auth server environment now holds the service token as SEAMLESS_SERVICE_TOKEN, the auth server's own name for it since 0.19.0, instead of API_SERVICE_TOKEN. The auth server's .env.example carries a placeholder under the new name, and the server refuses to start when the two names hold different values, so writing only the old name left seamless init --auth=local (which clones the auth server's main branch) unable to start. Re-running against an existing auth/.env reads either name.

0.23.1

Patch Changes

  • 7cdcaaa: seamless add --local now names the Compose project in seamless/docker-compose.yml after the repository (name: <repo>-seamless, lowercased and reduced to the characters Compose allows). Before, every repository's stack ran as project seamless, so two repositories on one machine shared containers, the seamless_pgdata database and the seamless_auth-keys signing keys. A re-run, with or without --force, keeps the name already in the file, including one you edited.

    Upgrading: a stack written by an earlier version keeps working until you re-run seamless add --local --force. The new stack starts empty under the new name, and add says so when it replaces a file without a name. Stop the old containers first with docker compose -p seamless -f seamless/docker-compose.yml down, since they hold port 5312. The old data stays in the seamless_pgdata and seamless_auth-keys volumes, which may hold another repository's data too; remove them with docker volume rm once no repository uses them.

  • 585f79f: seamless add fixes from writing its docs:

    • The backend origin (--api-url) now defaults by ecosystem when the backend's .env has no PORT: the port in the entry file's listen call when there is one, otherwise 3000 for Node, 8000 for FastAPI and Django (the uvicorn and runserver defaults), and 8080 for Go and Rust (the adapters' own examples). It was 3000 for every backend, which pointed FastAPI and Django apps, and the auth server's APP_ORIGINS, at the wrong port.
    • The "Found no backend" error, seamless help add and the README list the folders detection searches from the same list detection uses, so they now include app/ (and web/, client/ and frontend/ in the error).
    • Go, Axum and Django get working credentialed CORS for UI_ORIGINS, like Express, Fastify and FastAPI: net/http middleware for net/http and chi, a Gin middleware, Echo's CORS middleware, a tower-http CorsLayer (cargo add tower-http --features cors is now part of the install), and django-cors-headers (now installed with the Django adapter).
    • FastAPI and Django install python-dotenv, and the printed code calls load_dotenv() before reading the environment. The hint no longer suggests uvicorn --env-file .env, which needed python-dotenv without installing it.
    • React gets the bundled sign-in screens: BrowserRouter around AuthProvider, then AuthRoutes from @seamless-auth/react/routes with guards for signed-in and signed-out pages, in src/App when it exists. react-router-dom is installed when the app does not have it.
    • Removed the em dashes from seamless <command> --help headers, the top-level help, the verify summary and the admin dashboard prompt.
  • 99f7c3b: Describe JWKS_KID correctly. It is the kid header on the service tokens the adapter signs with API_SERVICE_TOKEN, and the auth server does not check it. The warning printed when a managed instance's key id cannot be read no longer says the adapter warns "until it matches", and seamless help add and the README call it the service token kid instead of the signing key id.

  • 0fc9b73: Security: new projects scaffold the starters from seamless-templates v0.19.1 (was v0.19.0). The Express, Fastify, and Next.js starters there depend on @seamless-auth/express ^0.20.0, @seamless-auth/fastify ^0.11.1, and @seamless-auth/nextjs ^0.4.0, on @seamless-auth/core 0.20.0. Earlier starters resolved core 0.19.1, whose cookie guard accepted the pre-auth cookie /login issues from an email address alone as a signed-in session for that account (fixed in fells-code/seamless-auth-server#211). Projects scaffolded by an earlier CLI should move to those adapter versions.

0.23.0

Minor Changes

  • 162c6f4: Scaffold from seamless-templates v0.19.0, which adds Angular, Vue and SvelteKit web starters (seamless init --angular, --vue or --svelte), each usable with any api template.

0.22.0

Minor Changes

  • 5f73b07: seamless init now writes a root AGENTS.md and a CLAUDE.md (See @AGENTS.md) beside docker-compose.yml and seamless.config.json. The guide is built from the choices init made: the directory and template each starter came from (linking its own AGENTS.md), how the auth server runs, the admin console, the ports, how to start the stack, the owner email that becomes the first admin, and the rules for agents (use the SDK and adapter, never your own JWT, password, session cookie or login endpoint code), plus a link to https://docs.seamlessauth.com/llms.txt.
  • 4404708: seamless add now wires Go (net/http, Gin, chi, Echo), Rust (Axum) and Python (FastAPI, Django) backends on the seamless-auth-go, seamless-auth crate and seamless-auth PyPI adapters. It reads go.mod, Cargo.toml, pyproject.toml or requirements.txt, installs with go get, cargo add, uv add or poetry add (printing the pip install for a plain pip project), says how the language loads .env, and prints the code for that framework, including the development-only code logging and the admin console mount.
  • 18ce407: seamless add now wires Angular, Vue and SvelteKit web apps on the @seamless-auth/angular, @seamless-auth/vue and @seamless-auth/svelte bindings, alongside React. It installs the binding (and vue-router or @angular/router when the app has no router), writes VITE_API_URL for Vue and SvelteKit, and prints the code for each file: the provider or plugin, the bundled sign-in screens as routes, and guards for signed-in and signed-out pages. Angular, which does not read .env, gets the backend URL in the printed config. Nuxt and Svelte without SvelteKit are reported rather than wired.
  • e0a7ff2: Add seamless add, which adds Seamless Auth to an existing Express or Fastify backend and React web app. It connects a local auth server (written to seamless/) or a managed application, writes the backend's .env and the web app's API URL while keeping existing values, installs the adapter and SDK with the project's package manager, and prints the code to add for TypeScript or JavaScript. It never edits source files. Commands now run without a shell outside Windows, which also stops Node 24's DEP0190 warning.
  • 8eb2380: seamless verify now drives an Angular app through the same browser specs as the React template. The angular and angular-dev layers run the reference app in verify/angular-app on @seamless-auth/angular (production build, and ng serve with --dev), or an Angular web template once the registry has one. SEAMLESS_ANGULAR_DIR points the run at another Angular app, and --local installs the locally built @seamless-auth/client and @seamless-auth/angular.
  • 2d7955a: seamless verify now drives a SvelteKit app through the same browser specs as the React template. The svelte and svelte-dev layers run the reference app in verify/svelte-app on @seamless-auth/svelte (SvelteKit 3 as a single-page app, as a production build and on vite dev with --dev), or a Svelte web template once the registry has one. SEAMLESS_SVELTE_DIR points the run at another Svelte app, and --local installs the locally built @seamless-auth/client and @seamless-auth/svelte.
  • b5fdefb: seamless verify now drives a Vue app through the same browser specs as the React template. The vue and vue-dev layers run the reference app in verify/vue-app on @seamless-auth/vue (a production build, and vite with --dev), or a Vue web template once the registry has one. SEAMLESS_VUE_DIR points the run at another Vue app, and --local installs the locally built @seamless-auth/client and @seamless-auth/vue.

Patch Changes

  • ac648ad: Scaffold the auth server at v0.18.0, whose OTP and magic-link send routes are POST, as the Go, Rust and Python adapters and the current @seamless-auth/client send them. v0.18.0 still serves the deprecated GET routes, so projects on the Express and Fastify starters keep working. The next steps after seamless init now give each api starter its own command (go run ., cargo run, uv run uvicorn ...) instead of npm install && npm run dev.
  • b52d886: New projects scaffold the starters from seamless-templates v0.18.0 (was v0.17.0). Every starter now ships an AGENTS.md and a CLAUDE.md, so the root guide seamless init writes links each starter's AGENTS.md instead of its README, and the Go (Gin), Rust (Axum) and Python (FastAPI) api starters are available as beta.
  • 59b9ef7: Spell the product name as "Seamless Auth" in the init auth server prompt, and describe the package as the CLI for Seamless Auth, open source passwordless auth.
  • fca70ae: seamless verify now waits for each browser service's healthcheck before running its specs. The vue-dev and svelte-dev services compile their app at startup and report healthy only once a compiled module and the prebundled SDK are served, so a cold Vite dev server no longer makes the first browser spec time out on a slow runner. The production web services' healthchecks now probe 127.0.0.1, since nginx listens on IPv4 only and they never passed on localhost. A service that never becomes healthy fails its own layer, with its recent logs, and the run continues.

0.21.0

Minor Changes

  • 64a9907: Add an adapter conformance suite that any server adapter can be held to (#246).

    • seamless verify --adapter-url=<url> starts Postgres and the auth API and runs the adapter specs against a reference app you started at that URL, for adapters this repository does not build (Go, Rust, Python). verify/CONFORMANCE.md documents what the reference app must serve.
    • The adapter specs, run against the Express and Fastify reference apps on every seamless verify, now cover cookie attributes and clearing, refresh rotation, concurrent refresh and refresh-token reuse, bearer transport, the adapter's own guard on an app route, error passthrough, the absence of tokens in cookie-transport bodies, and forwarding of the client's address and user agent to the auth API.
    • The reference apps trust one proxy hop, so each virtual user gets its own client address and rate-limit bucket on the auth API, and pin the current adapter releases (@seamless-auth/express 0.19, @seamless-auth/fastify 0.10).
  • b051bff: seamless config set and config apply accept prompt_passkey_enrollment and phishing_resistant_only, which need auth server v0.17.0 or later. prompt_passkey_enrollment makes email code, phone code and magic link sign-ins ask a user with no passkey to enroll one. phishing_resistant_only limits sign-in to passkeys. An older instance rejects a patch that includes either key.

Patch Changes

  • 4385906: seamless users and seamless org now read admin responses with the shared @seamless-auth/types schemas. users credentials names each passkey by the name its owner gave it (friendlyName), then by the device the auth server recorded, where it used to print "credential" for every one because it looked for fields the API never sends. A response missing a field the CLI needs now fails with a message naming the field, where it used to print placeholders such as (no id). Fields an instance sends that this CLI version does not know about are kept, so --json output still carries them.
  • f13e646: seamless login --local now works for a phone number. The auth server sends an SMS code as a number in the delivery block, and the CLI only accepted a string, so every local phone login failed saying the instance had not returned the code. Login responses are now read with the shared @seamless-auth/types schemas, and a login method the instance offers that this CLI version does not know no longer gets in the way of an OTP login. seamless profile add --identifier-type is validated with the same schema.
  • d3708b2: seamless sessions now reads the session list with the shared @seamless-auth/types schema. A session the instance sends malformed fails the command with a message naming the field, where it used to be dropped from the list without a word. sessions list --json now carries every field the API sends, including deviceName, ipAddress and userAgent as null when the API has none, where it used to leave them out.
  • 062630f: seamless config set and config apply now accept flow_rate_limits, which the auth server has read from system config since v0.14.0; config apply used to drop it from a config file without a word. config apply now tells read-only keys from unknown ones, and config diff flags a key no config has, which is usually a typo. The writable keys are checked against the shared @seamless-auth/types patch schema. config get and oauth-providers list still show exactly what the instance stores: they check the response's shape but never fill in schema defaults or reject a stored value.
  • 4569033: seamless init now downloads the starter templates by the commit their release tag pointed at when the CLI was published, rather than by the tag, so a moved tag cannot change what a scaffold extracts. Template extraction also fails with a clear error on an archive entry that would write outside the project, as the --admin=source dashboard download already did. SEAMLESS_TEMPLATES_REF and SEAMLESS_TEMPLATES_DIR still override the source as before.

0.20.0

Minor Changes

  • 25a267b: seamless init can now include the admin console with a full-stack template such as the Next.js starter.

    • --admin=api (the default, and the recommended prompt option) has the web app serve the dashboard at http://localhost:5173/console. The auth server gets SERVE_ADMIN_DASHBOARD=true, the app's .env gets SERVE_ADMIN_CONSOLE=true from the starter's {{serveAdminConsole}}, and ORIGINS stays the web origin alone, since the console shares it. seamless.config.json, seamless check, and the closing summary point at that URL.
    • --admin=none still leaves the console out. --admin=image and --admin=source are refused with an explanation: a dashboard container on :5174 would call the app's /auth cross-origin, which the Next.js route handler does not allow.
    • A starter from a templates release without the /console route is scaffolded without the console, with a warning, rather than with an auth server serving a dashboard nothing proxies. An explicit --admin=api for such a release is an error.
    • seamless verify checks the Next.js starter's console: /console serves the dashboard and its assets from the app's origin, and an admin signed in on the app loads the console's admin API calls through it.

Patch Changes

  • 63ac8f9: Map the standalone admin console (--admin=image and --admin=source) to container port 8080, where the dashboard's unprivileged nginx listens. The generated compose published 5174:80, so nothing answered on http://localhost:5174.
  • da190d7: seamless init --mobile with no value now selects the Expo template, whose registry alias is mobile. A trailing --mobile used to be dropped silently, and seamless init --mobile my-app took my-app as the template id and lost the project name; it now scaffolds my-app with Expo. --mobile=expo and --mobile expo are unchanged, and a bare --web or --api (neither is an alias) now says it needs a value instead of being ignored.
  • bb94d5b: New projects scaffold the auth server at v0.16.0 (was v0.13.1) and the admin dashboard at v0.9.1 (was v0.7.0). The auth server now links or creates an account through OAuth only when the provider asserts a verified email, and adds user import and OpenID Connect provider support. The dashboard's new Passkey enrollment page needs /admin/enrollment routes that ship in the next auth server release, so it reports an error against v0.16.0. adm-zip, which unpacks the downloaded templates and dashboard, moves to 0.6.1 to clear its security advisories.
  • 0f49e6b: New projects scaffold the auth server at v0.17.0 (was v0.16.0) and the starters from seamless-templates v0.17.0 (was v0.16.0). The auth server now serves the passkey enrollment, authentication coverage, store review account and audit integrity routes the bundled admin dashboard v0.9.1 calls, and the Express, Fastify and Next.js starters move to adapters (@seamless-auth/express ^0.19.1, @seamless-auth/fastify ^0.10.1, @seamless-auth/nextjs ^0.3.1) that pass them through, so the console's Enrollment, Coverage and Audit Trail pages work on a fresh local stack. The starters also read AUTH_SERVER_ISSUER, so a starter run on the host signs in against the Docker stack, and the Next.js starter serves the console at /console.

0.19.0

Minor Changes

  • 99fb8b2: Let a starter run on the host sign in against the local Docker stack, and keep the auth server's dev signing keys across a container recreate.

    • Templates can now use a {{authServerIssuer}} placeholder. On a local stack it resolves to http://auth:5312, the issuer the auth container signs with, so a starter run with npm run dev (which reaches the server at http://localhost:5312) can verify signed auth responses once it sets AUTH_SERVER_ISSUER from it. On a managed instance it resolves to the instance URL. The generated compose file also sets AUTH_SERVER_ISSUER on the app container.
    • The Docker auth service mounts a named auth-keys volume at /app/keys, so docker compose up after a config change no longer mints a new dev key pair under the same key id and breaks every adapter's cached key.
    • The generated docker-compose.yml no longer has runs of blank lines between services.
    • --api with a non-API template now says it "expects an API template".
  • 061f749: seamless verify covers what a developer sees on their first npm run dev, and the Next.js starter.

    • --dev runs every browser template a second time on its development server, where React Strict Mode runs every effect twice. The production pass alone could not see an effect that is not idempotent, such as the magic-link screen spending its single-use link twice (fells-code/seamless-auth-js#161). The conformance workflow passes it on every pull request (#225).
    • Full-stack templates are no longer skipped. The Next.js starter is built from its own Dockerfile's runtime target (and its dev target under --dev) and driven by specs written for its own screens: passkey enrollment and sign-in, an emailed code, a magic link, the server-rendered session page, and its role-gated route (#222).
    • The React magic-link spec fails when the link is verified more than once, and the JWKS spec no longer expects the signing key's kid to be dev-main.
    • The browser resolves localhost to 127.0.0.1, so a server of your own on [::1]:5173 no longer answers in place of the stack, and SEAMLESS_API_URL, SEAMLESS_ADAPTER_URL, and SEAMLESS_FASTIFY_ADAPTER_URL are honored when set.

Patch Changes

  • 01d9b3d: Support Node 22 and newer. The engines field now requires >=22 instead of >=24 <25, and CI runs on Node 22, 24, and the latest release (fells-code/seamless-auth-api#339).
  • 9a21318: Depend on @seamless-auth/types ^0.28.0. The new release adds time range schemas for the dashboard metrics and security anomalies; its new response fields are optional, so nothing the CLI reads changes.

0.18.0

Minor Changes

  • 306e23f: seamless init supports full-stack templates (kind: "fullstack" in the template registry), starting with the Next.js App Router starter: seamless init --nextjs, --web=nextjs, or pick it at the web prompt. A full-stack template is the web app and its own backend, so it fills the web layer and the project gets no api/. The backend question is skipped, and --api beside it is refused before anything is created.

    • The local stack runs it as the web service: the template's dev container on port 5173, with the source mounted for reload and the auth wiring the api service would have had. One-time codes and sign-in links print in docker compose logs web.
    • The admin console is not available for full-stack templates yet, since the Next.js adapter cannot host it. Only --admin=none applies, and other values are refused.
    • seamless.config.json records the web service as kind: "fullstack" with no api entry, and seamless check checks the web app's /health instead of the API.
    • seamless verify says it is skipping full-stack templates rather than dropping them silently.
  • 6c226be: The Microsoft preset now signs in with OpenID Connect: it sets issuer and jwksUri for the tenant so the auth server verifies the ID token, reads email verification from the xms_edov optional claim (add it to the app registration's token configuration), and links users imported with seamless migrate under source entra-id by their oid. The tenant prompt now takes the directory (tenant) id as a GUID only, because ID tokens name the tenant that way. Needs an auth server that supports the issuer, jwksUri and externalIdSource provider fields.

  • 4711f7d: The Microsoft OAuth preset is now scoped to one tenant. init --oauth asks for the directory (tenant) id, refuses common, organizations and consumers, and writes tenant-specific endpoints. Without a tenant, Microsoft is scaffolded disabled with a placeholder, like a missing client id. Projects scaffolded earlier still point at common; replace it with your tenant id in the auth server's OAUTH_PROVIDERS.

  • 0928a39: Add seamless migrate csv <file> to import users from a CSV export into an instance. It is a dry run unless --apply is passed, finds columns by header (or a --map file kept beside the export), validates each row locally with the shared @seamless-auth/types schema, sends rows in batches of 200, and writes a CSV and JSON report of every row's outcome. Exits 1 when any row is rejected or invalid. Needs an auth server with POST /admin/users/import.

  • b651ff2: Relicense from AGPL-3.0-only to the Apache License, Version 2.0 (fells-code/seamless-auth-api#335). The LICENSE file, the license field and the license header in source files now say Apache-2.0, and the AGPL summary in LICENSE.md is removed.

  • 37c8c8a: seamless verify covers the OAuth migration cutover path (fells-code/seamless-auth-api#337) at the API layer. The spec:

    1. Imports a user from a directory, then signs them in through that directory's OIDC provider. The link is made on the ID token's oid, not the email.
    2. Checks that the sign-in answers nextStep: 'enroll_passkey', enrolls a passkey, and checks the prompt stops.
    3. Retires the provider for the user's organization. It checks that the user's sessions are revoked and that the next sign-in is refused with oauth_provider_retired.
    4. Restores the provider and checks the user can sign in again.

    The mock OIDC provider now issues signed ID tokens, serves /jwks, and can sign in as a named user. The existing mock provider is unaffected.

    Needs an auth API that includes fells-code/seamless-auth-api#348 (session revocation on retirement).

Patch Changes

  • 80991a5: Correct the advice printed after connecting a project to a managed application. It told you to set the auth server URL on your frontend, which cannot sign in; the frontend points at your backend, which runs the adapter. It now also names JWKS_KID among the backend values.
  • 65e9668: Depend on @seamless-auth/types ^0.26.0 (was ^0.24.0), so the CLI's wire types track the current auth API contract.

0.17.0

Minor Changes

  • 2bcfa33: Scaffold from seamless-templates v0.15.0, the release that carries the Expo starter.

    0.16.0 taught init the mobile template kind but left the registry pin on v0.14.0, which has no such kind, so the question was never asked and --mobile=expo was refused as an unknown option. With the pin on v0.15.0 the registry lists the starter as expo (alias mobile, beta): init offers it after the backend, --mobile scaffolds it at mobile/ with EXPO_PUBLIC_API_URL filled from the manifest, and the success output says how to start it. The starter is a complete Expo Router app on @seamless-auth/react-native 0.1.0 (email code, sign-in link, passkeys, a protected call to the companion API), and its tools/associations/ generator writes the files native passkeys need. --yes still scaffolds without it.

    The same release moves the API starters to @seamless-auth/express 0.16.0 and @seamless-auth/fastify 0.7.0 and passes authServerUrl and audience into requireAuth, so a scaffolded API accepts the auth API's access token from the native app alongside browser cookies, and its /auth mount answers a bearer client with tokens in the body. The conformance harness's adapters take the same ^0.16.0 and ^0.7.0. The starters also keep their SSL options when DATABASE_URL carries sslmode (templates 0.15.0's patch note). The web starters stay on @seamless-auth/react 0.12.0, and the auth API and admin dashboard pins do not move.

0.16.0

Minor Changes

  • b9529c6: seamless init learns the mobile template kind.

    seamless-templates gains a third kind next to web and api, starting with an Expo (React Native) starter. The CLI treated an unknown kind as if it were not there: the alias resolver skipped it, the prompts never offered it, and verify filtered it out without a word.

    A mobile starter is optional. init asks "Mobile app" after the backend, defaulting to none; --mobile=<id|alias> (or the bare alias, --mobile / --expo) includes one; --yes scaffolds without, since a native app brings prerequisites (an associated domain for passkeys) an unattended run should not opt into; and a registry that predates the kind offers nothing, so there is no question to ask. A chosen starter lands at mobile/ with its .env filled from the manifest, is recorded as services.mobile in seamless.config.json, is checked by seamless check only when recorded, and is called out in the success output with how to start it and the Android emulator host. verify announces the mobile templates it cannot drive instead of filtering them silently. Template copying skips .expo, ios, and android.

    The templates registry pin moves to the release that carries the starter in a follow-up.

Patch Changes

  • 4bbf7ac: seamless verify --local packs @seamless-auth/client alongside @seamless-auth/react when the React SDK checkout is an npm workspace.

    The client SDK repo is becoming a workspace that publishes a framework-agnostic @seamless-auth/client next to @seamless-auth/react, and the react tarball depends on the client one. Packing only the root of such a checkout would produce the private root package, and installing the react tarball alone would go to the registry for a client version that is not published yet. The harness now reads workspaces from the checkout's package.json, packs both packages when it finds one, and the react image installs every tarball in one npm install so the dependency resolves from the sibling file. A checkout that predates the workspace is packed as before. The version line reported for @seamless-auth/react reads the react package's own manifest in a workspace.

0.15.0

Minor Changes

  • c99c759: Move the scaffold onto auth API v0.13.1, admin dashboard v0.7.0, and seamless-templates v0.14.0.

    v0.13.0 instruments the auth path. Every auth_events row now carries a deployment id, a device class folded from the user agent, the subject's mail provider (a provider name, never the domain), an owner flag, and the attempt the row belongs to (the ephemeral token's new jti). GET /internal/metrics/funnel reports time to registration, time to login and passkey adoption, and GET /internal/metrics/sign-ins reports sign-in outcomes per method, device class, mail provider and owner flag, with where attempts stop. The same release ships dashboard v0.7.0 inside the API image, which is what that dashboard release needs: its Overview gains a Passwordless Funnel section and a Sign-in Outcomes section that read those two routes. v0.13.1 is the same server on a release image that applies Debian security updates at build time, since v0.13.0 never published an image (its scan failed on a base-image pcre2 finding).

    The admin dashboard pin moves with it, so the standalone console (--admin=image and --admin=source) serves the same release the API image serves at /console. Against an older API the two new Overview sections report themselves unavailable in place and the rest of the screen is unaffected.

    Templates v0.14.0 carries @seamless-auth/express 0.15.0 and @seamless-auth/fastify 0.6.0 in the API starters, and the conformance harness's adapters take the same ^0.15.0 and ^0.6.0. Those adapters forward the browser's user agent to the API as x-seamless-client-user-agent beside the client address they already forward, and pass the sign-ins route through. The adapter is the only client the API sees, so a project on the older adapters has every audit row recorded as the adapter's own user agent and its breakdown by device reads unknown; the other dimensions do not depend on it. Nothing else in the starters moves: the manifest contract and the registry are byte-identical to v0.13.0, and the React starters stay on @seamless-auth/react 0.12.0, since the token claim the API added is one the client never reads.

0.14.0

Minor Changes

  • af41ba4: Move the scaffold onto auth API v0.12.0, admin dashboard v0.6.0, and seamless-templates v0.13.0.

    v0.11.0 puts passkey enrollment behind an access session. It is a breaking change to the wire contract: /webAuthn/register/start and /webAuthn/register/finish used to accept the ephemeral token the API mints from an email address alone, so anyone who knew an address could enroll a credential against that account and sign in as its owner. Both routes read the access session now, and enrollment no longer issues a session of its own, answering 200 with the credential instead. The same release declares and validates the window on GET /admin/users, and adds organization deletion along with a paged, searchable admin organization list. v0.12.0 ships dashboard v0.6.0 inside the API image, which is what that dashboard release needs: it pages and searches organizations on the server, gains a remove action, takes a date range on Overview and Security, and grows an Authenticator Policy section.

    The admin dashboard pin moves with it, so the standalone console (--admin=image and --admin=source) serves the same release the API image serves at /console.

    The enrollment change has no safe release order, so every side of it moves here at once. Templates v0.13.0 carries @seamless-auth/react 0.12.0, @seamless-auth/express 0.14.0 and @seamless-auth/fastify 0.5.0 in the starters, and the conformance harness's adapters take the same ^0.14.0 and ^0.5.0. The adapters forward the access session the API now requires; an older one sends what this API refuses, so a scaffold pinned to either half alone would answer 401 at enrollment. No shipped flow loses a step, because registration proves an address with an email OTP and verifying it issues the session before the passkey screen appears.

    The adapters also proxy DELETE /admin/organizations/:organizationId, and forward the query string on GET /admin/users and GET /internal/auth-events/login-stats, which both dropped it, so the dashboard's user search and its login statistics range now reach the API as sent.

  • 206e3cd: Move the scaffold onto the current Seamless ecosystem: auth API v0.10.0, admin dashboard v0.5.0, and seamless-templates v0.12.0.

    Three releases of auth API work land together. v0.8.0 gives WebAuthn challenges their own store with a five-minute expiry and one-time use, so a registration and a login can be outstanding at once instead of clobbering each other, and it adds AUTHENTICATOR_POLICY, which decides attachment, user verification, attestation, and whether a synced passkey may enrol. v0.9.0 puts the lockout policy and the per-IP and per-identity limiters on TOTP step-up, which had none of the three, and stops running refresh tokens through bcrypt. v0.10.0 drops the sessions.refreshTokenHash column the previous release stopped writing.

    A scaffold's compose file is built from the pinned release's .env.example, so a new project picks up AUTHENTICATOR_POLICY along with SESSION_IDLE_TTL, MAX_CONCURRENT_SESSIONS and a commented TRUST_PROXY, and REFRESH_TOKEN_TTL moves from 1h to 1d.

    The admin dashboard bump keeps the standalone console (--admin=image and --admin=source) in step with the one the API image now serves at /console, since v0.10.0 embeds dashboard v0.5.0 itself. That release names the acting administrator separately from the subject in the events table, and collects identity proofing before preparing a device replacement, which is what the API already records and requires.

    Templates v0.12.0 moves the starters onto @seamless-auth/react 0.11.0, @seamless-auth/express 0.13.0 and @seamless-auth/fastify 0.4.0. All three are 0.x minors, so the caret ranges the starters carried could never have resolved to them and a scaffolded project stayed on the older versions however long ago they were pinned. It also carries per-application auth cookie names read from AUTH_COOKIE_PREFIX, so two Seamless apps on one host stop overwriting each other's session. The manifest contract is unchanged, so nothing in init moves with it.

    The conformance harness's adapters follow the same SDKs the starters now install: @seamless-auth/express ^0.13.0 and @seamless-auth/fastify ^0.4.0. Both stop repeating the access and refresh tokens in the body of the response that sets them as httpOnly cookies, and both forward a magic link's redirectUri to the API.

  • ec85b59: Match the list windows the auth API now enforces, and let org list page.

    The API validates the window on its admin list routes: limit is 1 to 100 and offset is 0 or more. The CLI checked both flags against one range with a floor of zero, which was right for --offset and wrong for --limit, so users list --limit 0 and users list --limit 500 were sent and came back as a 400 naming neither the flag nor the bound. Each flag is checked against its own range now, and the message says which one was wrong and what it accepts.

    --limit 0 is therefore an error rather than a request for nothing. Asking the server for zero rows and reporting "No users." said there were none when the CLI had not looked, which is worse than saying the flag is out of range.

    org list gains --limit, --offset and --search. It sent no window at all, so once the API started defaulting to 50 it printed the first 50 organizations and then a count of every organization, claiming rows it had not shown. It now reports where the page sits, the way users list already did, and --search matches the name and slug server-side.

0.13.0

Minor Changes

  • 63ff4ff: Add seamless check --strict, which exits non-zero when a check fails.

    check is what you would reach for in a health-check script or a CI gate, and it always exited 0: an empty directory, a stack that is down, and a fully healthy project were indistinguishable to anything reading the exit status.

    --strict exits 1 if any check failed, reporting how many. Without it the exit status is unchanged, because this output has been parsed by scripts since before the flag existed.

    Every check still runs either way. A gate that stops at the first problem hides the rest, and the whole picture is the reason to run check at all.

Patch Changes

  • 79aa298: Clean dist before building, so a stale artifact cannot be packed.

    build was a bare tsc, which overwrites but never removes. A file deleted or renamed in src left its old output behind, and prepublishOnly runs the same script, so a local publish could ship something no longer in the source tree. The clean script already existed and is now wired in.

  • cfed9e6: Correct the documentation that had drifted from the rest of the ecosystem.

    • seamless check and seamless verify now have README sections. Verify is half of what the CLI does and had none.
    • The auth server's local-development steps named a script that does not exist (db:migrate); the auth API spells it migrate:up, and the Docker path runs it for you at container start.
    • init --profile was documented as selecting a profile. It has been accepted and ignored since managed connect moved to the portal session, so the help now says so.
    • The generated project layout listed a README.md the CLI does not write, and omitted seamless.config.json, admin/, and which pieces a managed project skips.
    • "No redirects or third-party auth providers" predated OAuth sign-in, which the CLI has configured since the provider prompts shipped.
    • The included-projects list now names all four repositories the CLI scaffolds from and conformance-tests against, including seamless-auth-server and seamless-auth-react.
    • config set help was missing session_idle_ttl from the string-typed keys.
  • d039e18: Add --json to seamless whoami and seamless sessions list.

    config get/roles, users list/credentials, and org list/get/members list all had it; these two, both natural scripting targets, did not, so reading an identity or a session id from a script meant parsing formatted output.

    Both print machine-readable output and nothing else: an empty session list is [] rather than "No active sessions.", and whoami --json reports a missing sub or email as null instead of the (unknown) the table shows.

  • 723d581: Point a managed scaffold at the instance URL the portal computes.

    init composed the scaffolded backend's AUTH_SERVER_URL from application.domain, a stored column the portal superseded. It goes stale when a trial is upgraded and its tenant moves zones, and mvp and business instances are served at domain/<infraId> rather than at domain. Either way the scaffold pointed at a URL that does not answer, and the failure surfaced as an SDK error in the developer's app rather than as anything the CLI said.

    resolveAppInstanceUrl already existed for exactly this, and apps and the application picker already used it; the two scaffold paths did not. They do now.

    The connectable-application filter asked for domain too, so an application the portal computed an instanceUrl for but whose domain column was never populated was filtered out and never offered. It now asks the same question the scaffold does.

  • e1d3687: Write the signing key id a managed instance actually publishes.

    A managed scaffold hardcoded JWKS_KID=dev-main. Managed instances pin their kid per tier (trialkey1 for trials, paidkey1 for paid), so the value was never the instance's own. Nothing verifies against it, adapters resolve the key from the token header through the remote JWKS, but they do warn on boot while it is the dev default, so every managed scaffold produced an app that reported itself misconfigured.

    init now reads the kid from the instance's /.well-known/jwks.json. If the instance cannot be reached it keeps the old default, says so, and explains that nothing breaks except the warning.

  • 976081b: Say what could not be reached when a scaffold's network read fails.

    seamless init makes three remote reads: the template registry, the templates archive, and the auth server's .env.example. Each of them handles a non-ok HTTP response with a message naming the status and the URL, but a connection-level failure (offline, DNS, TLS, no route) rejects with a bare TypeError: fetch failed that propagated untouched to the top-level handler. The whole output was "Error: fetch failed", which named neither the host nor which of the three reads had failed.

    The three call sites now go through a shared helper that turns that rejection into a message naming the URL, what the CLI wanted from it, and the network as the likely cause, in the style login already uses for an unreachable instance. Non-ok responses keep the messages they had, and the original error is preserved as the thrown error's cause.

  • 4418970: Recognize the addresses a local dev instance actually answers on.

    isLocalInstanceUrl accepted only localhost, 127.0.0.1, ::1 and .localhost subdomains. It gates two things: whether plaintext http is allowed for an instance URL, and whether --local OTP delivery is permitted. A dev instance is commonly reached at none of those, a container bound to 0.0.0.0, a LAN address from a phone on the same network, or an mDNS .local name, and each was rejected as if it were production, forcing https onto a box with no certificate.

    Now also treated as local: the whole 127.0.0.0/8 loopback range, 0.0.0.0 and ::, the private IPv4 ranges (10/8, 172.16/12, 192.168/16), link-local (169.254/16 and fe80::/10), IPv6 unique-local (fc00::/7), and .local names. Ranges are matched by octet rather than by prefix, so 172.15, 172.32 and 1.10.0.1 stay public.

  • f4ccb30: Stop writing AUTH_MODE into scaffolded projects.

    The scaffold set AUTH_MODE=server in the auth server env and on the admin console container, but nothing reads it: not the auth API, not the admin dashboard (its entrypoint takes only API_URL), and not the web or api templates. It was config that looked meaningful and meant nothing.

  • 8e5c0f7: Remove code nothing calls.

    • src/utils/writeEnv.ts, an unused duplicate of core/env.ts's writeEnv that still emitted unquoted values, the bug fixed in the real one.
    • buildJWKSConfig in the docker generator, which had no callers and was the only user of core/jwks.ts, so that module went with it.
    • generateKid in core/secrets.ts. generateSecret beside it stays; it is widely used.
    • setupDockerAuth in the auth generator, unreachable because generateAuthServer is only ever called with "local". It also wrote a compose file mounting pgdata at /var/lib/postgresql/data, which the pinned postgres:18 ignores, so it had gone stale as well as unreachable.

    With the unreachable branch gone, generateAuthServer no longer needs a mode, and its context: any and mode: "local" | "docker" | Symbol parameters become a plain root: string.

  • 74ae06c: Stop printing Error: undefined when something other than an Error is thrown.

    The top-level handler in index.ts and the catch blocks in whoami and sessions all read .message off the thrown value, which throw does not guarantee exists. A rejected promise carrying a string, a parsed response body, or undefined printed nothing useful, naming neither the failure nor the fact that something unexpected came back.

    A new errorMessage renders any thrown value: an Error's message (or its name when the message is empty), a thrown string as it is, a message field off a thrown object, and otherwise the object's shape or a labelled primitive. A thrown object is scrubbed first, since it arrives from a rejected request as often as from our own code.

0.12.2

Patch Changes

  • cc54666: Make --admin=source produce a stack that runs.

    "Separate container, clone repo for modification" wrote build: ./admin into the compose file and recorded path: "./admin" in seamless.config.json, but nothing ever created admin/. docker compose up failed on a missing build context, and the success output said nothing about it, so a first-class menu option scaffolded a project that could not start.

    init now unpacks the admin dashboard into admin/ for that mode, from the same tag the published image is built from, so both admin modes scaffold the same dashboard. It arrives as plain files rather than a git clone, which is what a directory destined for the developer's own repository wants, and needs no git binary. Override the ref with SEAMLESS_ADMIN_DASHBOARD_REF, or scaffold from a local checkout with SEAMLESS_ADMIN_DASHBOARD_DIR.

    The fetch runs before the compose file is written, so a failed download stops the scaffold instead of leaving a project that cannot come up, and the success output now points at admin/.

    Extraction is constrained to the project directory: an archive entry whose resolved path would escape it fails the scaffold rather than being written or silently skipped. Not reachable while the archive is our own repository over https, but this is a new extraction site and it should not add to what #135 already tracks for the templates source.

  • 464f0c0: Stop config set from changing the type of a string value, and check the key first.

    parseValue ran JSON.parse on every value, so config set app_name 123 sent the number 123 and config set rpid true sent the boolean true for keys the instance types as strings. set also skipped the WRITABLE_KEYS filter that apply applies, so a key the instance's strict patch schema rejects was sent anyway and came back as an opaque failure.

    String-typed keys (app_name, rpid, access_token_ttl, session_idle_ttl, refresh_token_ttl) now take their value verbatim; every other key parses as JSON with the existing fallback to a string. An unwritable key is refused before the request, naming the keys that are writable.

    That guard is only correct if the list is, and it had drifted: authenticator_policy, session_idle_ttl, max_concurrent_sessions, and magic_link_redirect_uris are all accepted by the instance but were missing from WRITABLE_KEYS, so config apply had been silently dropping them. They are now included.

  • 40c533e: Remove the useDocker flag that could only ever be true.

    With the Docker confirm gone, runProjectSetupPrompts still returned useDocker: true unconditionally and init still gated the Docker Compose generation on it. The condition had no false case, so the guard read like a real choice while describing a scaffold the CLI never produced.

    The field is off the answers object and the compose file is now generated outright on the local scaffold path. Nothing about what init generates changes.

  • 5450c34: Stop asking a question whose answer was never used.

    Choosing to run the auth server from local source prompted "Auth server still requires Docker for full stack. Enable Docker?", then discarded the answer: useDocker was returned as true either way, and declining only printed "Enabling automatically". The full stack needs Docker regardless, so the prompt cost a keystroke to tell the developer their answer did not count.

    The prompt is gone, and with it the entire non-docker branch of the success output, which described a workflow (bring your own PostgreSQL, npm run dev per service) that no run could ever reach. Nothing about what init generates changes.

  • cccc0ed: Stop reading every failed code verification as a wrong code.

    completeLogin handled only 200 and 429 specially, so a 500, a 423 lockout, a 403 for a login method disabled mid-flow, and a 400 all fell through to "That code was not accepted", spent an attempt, and ended three attempts later on a generic message with the real reason nowhere in sight.

    Only a 401 is retried now, because that is how the instance answers a genuinely wrong code. Everything else stops immediately and reports what the instance said, with a lockout formatted the same way /login already formats it.

    Separately, when the five minute login window lapses while a code is being typed, the CLI drops that code and requests a new one. It now says so rather than appearing to ignore what was entered.

  • 9e13d09: Stop the login prompt from deciding what a valid one-time code looks like.

    The email branch required /^[A-Za-z]{6}$/ and the phone branch /^\d{4,8}$/, so a code outside those shapes was refused locally, before the instance ever saw it, with no way to override. The email code was also force-uppercased on the way out. Both encode a detail that belongs to the instance: the shape it issues can change, and it normalizes email OTP case itself, so the uppercasing was redundant at best and corrupting for a case-sensitive code.

    The prompt now refuses only an empty answer and sends what was typed, trimmed. The placeholder still shows today's shape as a hint.

  • da160ca: Redact secrets in the server validation details the CLI prints.

    patchSystemConfig and the OAuth provider mutations splice a rejected request's details payload into the error they throw, and runConfig prints it to stderr. Validation errors commonly quote the offending value back, and for seamless config oauth-providers add|update that request body carries a clientSecret, so a rejected provider config could print a client secret, in CI, into a build log. Both sites now scrub before stringifying.

    The scrubber itself grew to match. It covers secret, clientSecret, client_secret, password, apiKey, api_key, otp, and code alongside the token keys it already knew, and it now masks by shape as well as by key: a JWT or a Bearer ... credential quoted inside a message string is caught wherever it appears, including in a bare string body, which key matching alone could never reach.

    redactToken and scrubTokens moved from core/keychain.ts to a new core/redact.ts. They are a logging concern with no keychain dependency. --json output is deliberately left unscrubbed: it is a machine-readable contract, and rewriting values there would break scripts and hide data the caller asked for.

  • 1f9e25f: Make transparent token refresh survive rotation, concurrency, and a persistent 401.

    Four problems, all sharpened by the same instance behaviour: /refresh rotates the refresh token on every call, and a second use of a spent one is read as theft and revokes the whole session chain.

    • A refresh that renewed only the access token wiped the session. tokensFromAuthResponse required both tokens, which is right for a login but not for a refresh: the response schema marks both optional. A new tokensFromRefreshResponse keeps the current refresh token when the instance does not send a new one.
    • Concurrent 401s fired parallel refreshes. With reuse detection that does not merely race, it logs the developer out. Refreshes are now single-flight, and a request whose token was already replaced while it was in flight simply retries instead of refreshing again.
    • A headless run broke itself after the first rotation. SEAMLESS_REFRESH_TOKEN is read fresh on every run, so a rotated token written to the keychain was never read back, and the next run re-sent the spent one, revoking the session. The CLI no longer writes it, and a rejected environment token now explains rotation and names the command that issues a new one.
    • A 401 that survived a refresh was returned verbatim, leaving commands to report an opaque failure rather than saying the session is gone. It now raises ReauthRequired.
  • eab9c29: Send --limit and --offset to the server on seamless users list.

    Both flags were parsed and then dropped: listUsers called GET /admin/users with no query, and the command sliced the single page the server returned (its own default of 50) client-side. An --offset past that page printed "No users." while the summary line reported a larger total, and --json ignored paging entirely, so a script could never walk past the first 50 users.

    The flags now travel as query params, which the API already honours, and --json returns the same page the table does. A non-numeric or negative --limit/--offset is rejected instead of being coerced to NaN and silently returning nothing.

0.12.1

Patch Changes

  • 2acea63: Stop reading a 401 from /login as "no such account", and stop claiming an account exists when the instance will not say.

    POST /login no longer answers 401. An identifier with no usable account, which used to cover unknown, unverified, and no-permitted-method, now gets 200 and a decoy pre-auth token so the response cannot be used to test whether an account exists. Two branches in completeLogin read that 401 and are now unreachable: the "not verified yet" message and "No account was found for X". Both are removed, because there is no longer an answer for them to read.

    The messages that surrounded them were making a claim the CLI can no longer support. "A code was sent to X" is now "If an account exists for X, a code is on its way", and "This account cannot use email otp login" is now phrased as what the instance offered, since that method list comes back for an unknown identifier too.

    An unknown identifier therefore runs the ordinary flow and fails at the code step. That is the intended behaviour and not something the CLI can shortcut, so the final error now says so: it names the identifier and points at registering, instead of "Could not verify a code" with no explanation of the likeliest reason.

    423 is now reported on its own terms, with how long to wait when the instance says. Previously it fell through to "Login request failed (423)". It is also the one answer left that does imply an account exists, which is a deliberate and documented tradeoff on the API side.

    Adds verify/harness/api/loginEnumeration.spec.ts to the conformance matrix, pinning the guarantee against a running instance: an unknown identifier gets the same status and the same fields as a registered one, the same identifier keeps the same subject and the same method list across attempts, the OTP send reports success and sends nothing, the verify fails the way a wrong code fails, and a credential id that cannot exist is refused identically for both.

    The loginMethods assertion is the one worth reading. It is deliberately not equality between one account and one decoy: that list is filtered by what an account can do, and a decoy's capabilities are derived per identifier, so any two can legitimately differ. What must hold is that a real account's list is one a decoy can also produce, which is what makes a narrow list stop being proof of existence. The spec asserts that over a sample.

  • c3c9d57: Give seamless verify a build cache in CI, and stop paying for provenance attestations nothing reads.

    The conformance job spends nearly all of its time building Docker images, and none of it running tests. On the run that prompted this, Run seamless verify was 28m02s and the tests inside it took 23.7 seconds. Every image is rebuilt from scratch on every run, because CI has no Docker layer cache, and the host-side npm caches do nothing for a build that happens inside Docker.

    seamless verify now layers an extra compose file over the base one when SEAMLESS_VERIFY_COMPOSE_OVERRIDE is set. CI points it at verify/docker-compose.cache.yml, which attaches a type=gha build cache scoped per service, and per web template for the react image since each template is different source. Nothing changes for a local run: the variable is unset, the base compose file is used as it always was, and type=gha would be an error outside Actions anyway.

    The Dockerfiles were already ordered for this. Each installs dependencies from a lockfile before copying source, so a source change leaves the expensive layer intact and only a lockfile change invalidates it.

    Also sets BUILDX_NO_DEFAULT_ATTESTATIONS in the workflow. The images are thrown away when the job ends, so the provenance attestation buys nothing, and on that same run resolving provenance for metadata file took 301s by itself.

  • 20f8e66: Follow the React SDK's passkey enrollment through the removal of its naming step.

    @seamless-auth/react used to open a "Name This Device" modal after "Register Passkey" was pressed, and the harness typed into it before the WebAuthn ceremony would start. That view is gone from the SDK, so registerWithPasskey waited 30 seconds for a placeholder that no longer renders and then timed out, failing react/passkeyRegister.spec.ts and react/passkeyLogin.spec.ts (which registers before it signs back in).

    The helper now stops at the button, which is where the ceremony begins. This tracks the SDK release that drops the modal, so a --local run against an older React checkout, or a published run before that release lands on npm, will fail on the modal this no longer dismisses.

0.12.0

Minor Changes

  • 35d9f3e: Scaffold new projects on PostgreSQL 18, and run the conformance harness on it too.

    seamless init generated a stack pinned to postgres:17 while the verify harness ran postgres:16. Both are now postgres:18, so what the harness certifies is the major a fresh scaffold actually gets.

    This does not touch an existing project. The image is written into each scaffold's own docker-compose.yml at generation time, so a project keeps whatever major it was scaffolded with and its data directory is never pulled out from under it. Only newly scaffolded projects get 18, on a fresh volume. Upgrading an existing project is a deliberate act: change the image in its docker-compose.yml, and dump and restore the volume, since PostgreSQL will not start against a data directory written by a different major.

  • a39c5c6: Scaffold from seamless-templates v0.9.0.

    Both React starters move to @seamless-auth/react ^0.9.0, which makes the bundled auth screens themeable: every colour reads from a --seamless-* CSS custom property with the previous literal as its fallback, so a scaffolded project can match the auth UI to its brand by setting those variables on :root or on any ancestor of <AuthRoutes />. The public API is unchanged, and a project that sets no variables renders exactly as before.

    Both API starters now pin postgres:18-alpine, with the volume mounted at /var/lib/postgresql. Until this bump a scaffold named two different majors in the same directory — postgres:18 in the CLI-generated compose file and postgres:16-alpine in the API starter copied in beside it.

    Every starter also ships a working test, lint, and format setup out of the box: Vitest with tests that pass on a fresh npm install, Prettier alongside ESLint, and the same script names across all four (typecheck, lint, lint:fix, format, format:check, test, test:watch, test:coverage, and a check that runs the whole gate).

  • d5da781: seamless verify now exercises the Fastify starter. The scaffold has offered a Fastify API since the templates bump, but the conformance harness only ever drove the Express adapter, so a green run said nothing about whether a Fastify-scaffolded project actually worked.

    The stack gains a second adopter backend (verify/adapter-fastify-app, on port 3001) built on @seamless-auth/fastify, a twin of the Express one: same routes, same env contract, same capture transport. The existing adapter specs run against both without being duplicated, since the two Playwright projects share a test directory and differ only in which backend they point at. The conformance grid gains an adapter-fastify column, so a failure in one framework is attributable to that framework.

    --api-only and --no-react are unchanged, and --local builds and packs @seamless-auth/fastify from source alongside core and express.

Patch Changes

  • 5cb6911: Fix the scaffolded database failing to start on PostgreSQL 18.

    The PostgreSQL 18 bump moved the image tag but not the volume mount. PostgreSQL 18+ images store data in a major-versioned subdirectory (/var/lib/postgresql/18/docker), so a mount at /var/lib/postgresql/data is ignored and the container refuses to start, restart-looping on:

    Error: in 18+, these Docker images are configured to store database data in a
           format which is compatible with "pg_ctlcluster" ...
           Counter to that, there appears to be PostgreSQL data in:
             /var/lib/postgresql/data (unused mount/volume)
    

    The generated docker-compose.yml now mounts pgdata:/var/lib/postgresql. See docker-library/postgres#1259.

    This only ever affected projects scaffolded from the unreleased PostgreSQL 18 change, so no published version of the CLI produced a broken scaffold.

0.11.0

Minor Changes

  • 3ef3b68: Add seamless apps, so a portal account can see what it owns without opening the dashboard. apps list prints name, plan, status, and instance URL; apps get <id> adds the console URL, region, owners, trial expiry, and whether a service token has been issued (masked, never the live value). Both take --json and both require a portal session.

    Applications are now read through the portal's instanceUrl, which is derived from the service plan, rather than the stored domain column that goes stale when a trial is upgraded. Applications that have not finished provisioning are listed instead of being silently dropped. init is unchanged: it still reads domain and still considers only applications that have one.

    apps list shows the infra id as the reference (falling back to the id before provisioning), and apps get accepts an id, a name, or an infra id.

  • 9d089c7: Move the scaffold onto the current Seamless ecosystem: auth API v0.7.1, admin dashboard v0.4.0, and seamless-templates v0.8.1 (which carries @seamless-auth/react ^0.8.0 in both React starters, @seamless-auth/express ^0.12.0 in the Express starter, and @seamless-auth/fastify ^0.3.1 in the Fastify starter).

    A scaffolded project can now finish registration without a passkey. Registration used to end on a screen with a single control, leaving anyone who did not want a passkey, or whose device could not make one, with no way forward. The starters offer a skip when the instance has another login method enabled, and say so plainly when it does not. That reads from GET /system-config/public, a new unauthenticated route on the auth server that returns the configured login methods, so the sign-in screens can offer what an instance actually has enabled instead of a hardcoded guess. The API, the adapters, and the web templates all had to move together for it to work, which is why this bumps them as a set.

    Registration against a scaffolded Fastify API used to fail with a 500 and TypeError: option maxAge is invalid: 300. The auth server sent the registration response's ttl as the string "300", and the Fastify adapter handed it to a cookie library that requires an integer. The Express starter never showed this, because its adapter multiplies the value into milliseconds and so coerced the string on the way past. It is fixed from both ends: @seamless-auth/core 0.12.1 parses the lifetime before it reaches an adapter and rejects anything that is not a positive whole number of seconds, and auth API v0.7.1 sends the value as a number.

    seamless init now offers Fastify as a backend, listed as "Fastify (beta)" beside Express. It serves the same surface as the Express starter on the same environment contract, including the admin console at /console behind SERVE_ADMIN_CONSOLE. Two boot-time fixes land with it: an empty PORT= in .env now falls back to 3000 rather than binding a random free port, and pino-pretty moves to a runtime dependency so an install without dev dependencies boots. Both Express and Fastify starters ship .env.example secret placeholders long enough to clear the adapter's 32 character minimum, so the documented cp .env.example .env && npm run dev path boots. A project from seamless init was already unaffected, because the CLI fills COOKIE_SIGNING_KEY itself.

    Both React starters gain a protected /session route that shows the issued claims, roles, organization context, step-up freshness, and registered passkeys, so the first authenticated screen reads as an app rather than a JSON.stringify dump. Missing configuration now stops a scaffolded project with a message naming the variable instead of surfacing later as a 500, and the Express starter reports every configuration problem at once.

    The auth API drops the admin bootstrap invite flow in favor of the OWNER_EMAIL grant the CLI already writes, so the generated .env no longer carries SEAMLESS_BOOTSTRAP_ENABLED, SEAMLESS_BOOTSTRAP_SECRET, or SEAMLESS_AUTH_DEBUG_SECRETS. AVAILABLE_ROLES now offers admin:read and admin:write alongside bare admin, and assigning a role the instance does not list is rejected rather than silently doing nothing. Postgres TLS is configurable through DB_SSL, DB_SSL_CA, and DB_SSL_REJECT_UNAUTHORIZED, and DB_URI is accepted as a DATABASE_URL alias.

    The conformance harness adapter moves to @seamless-auth/express ^0.12.0, which is also what proxies the new public system-config route. The breaking change in 0.11.0 splits error into errorCode and errorBody on the handler result types, which only affects code importing handlers from @seamless-auth/core directly; the adapter uses createSeamlessAuthServer, so it needed no source change.

  • 4917717: Make seamless init template flags discoverable, predictable, and safe to get wrong.

    Add seamless templates list [--json], which prints every starter init can scaffold with its id, kind, framework, selecting flags, and status. It reads the same registry init does (so SEAMLESS_TEMPLATES_DIR and SEAMLESS_TEMPLATES_REF apply) and needs no login, so the available templates no longer have to be looked up in the source.

    Every template now answers to --<id> as well as its shorter --<alias>, so seamless init --react-vite works alongside --basic, and the api starters (--express, --fastify) have a flag for the first time. The "unknown option" error lists both spellings and points at seamless templates list.

    Template flags are also resolved before init creates a directory or asks whether to write into one that is not empty. An unrecognized or conflicting flag now fails immediately instead of surfacing only after the overwrite confirmation.

  • 6e107cb: init now offers the managed path instead of assuming it. A portal session used to make managed the default silently, with --local as the only escape and no way to learn you needed it until after the template prompts. When your account has a provisioned application, init asks whether to connect it or scaffold a local stack, with managed leading.

    Whether managed is even possible is resolved before the first prompt. An account with nothing to connect no longer answers two prompts and then fails: it says why and continues to a local scaffold. That message now distinguishes "no applications yet" from "still provisioning", which the old NoApplicationsError got wrong for anyone mid-provision.

    A directory that already has files is no longer forced down the integrate path. init asks whether to connect it to a managed application or scaffold in place. Scaffolding into a non-empty directory was previously impossible, so a stray README or .git was enough to block a local project, and every route that now reaches it confirms first: starter files overwrite anything with the same name, and the confirmation defaults to no.

    An unreachable control plane asks before scaffolding a local stack rather than degrading silently.

    --local and --app <id> skip the new prompts, and --app without a session still fails rather than falling back.

  • 490391d: Add a non-interactive seamless init. --yes (-y) answers every question with the option the prompt marks as recommended, so a scaffold runs from CI, a Dockerfile, or a script with no terminal attached:

    seamless init my-app --local --yes --email=you@example.com

    Each question also gets its own flag, honored with or without --yes: --web=<id|alias> and --api=<id|alias> choose the starters, --email=<address> sets the owner who becomes the admin, --auth=<docker|local> picks how the auth server runs, and --admin=<api|image|source|none> picks where the admin console is hosted. Unspecified values fall back to the recommended option, except the owner email, which has no safe default and is taken from --email or the portal session.

    --yes deliberately stops rather than guessing in three places. Choosing between a managed application and a local stack needs --app <id> or --local. Scaffolding into a directory that is not empty needs --force, since starter files overwrite anything with the same name. Rotating a managed application's existing service token needs --force too, because it breaks whatever is already deployed on the old one.

  • cc13a6b: Connecting a project to a managed application now wires up its bundled database. init reads the application's database and writes DATABASE_URL into api/.env as postgres://USER:PASSWORD@host:port/db?sslmode=require.

    The user and password stay as literal placeholders. The control plane only returns them for ?reveal=true, which this CLI never asks for, so a live database credential never reaches the developer's disk or terminal: they copy those from the dashboard. Anything printed as a connection string has its userinfo masked regardless.

    An application whose database is still provisioning produces a warning rather than a failure, and the database is read before the service token is rotated so a missing one is never reported against a project whose old token has already been invalidated. Running init inside an existing project adds DATABASE_URL only when there is not one already, so a working connection string is never replaced by a placeholder.

    This needs the templates release that teaches the express starter to read DATABASE_URL and negotiate TLS. Until SEAMLESS_TEMPLATES_REF is bumped to it, the value is computed but the pinned starter does not declare the placeholder, so nothing is written.

  • 15b487a: init now asks for your email and writes it to the scaffolded auth server as OWNER_EMAIL. The auth server grants the admin role at account creation to a signup matching that address, so the local flow is init, docker compose up, register. When you are signed in to the portal, the prompt defaults to that account's email.

    seamless bootstrap-admin is removed. It existed to mint the first admin invite, which the owner grant now covers, and the scaffolded stack no longer enables the bootstrap route or carries a bootstrap secret. seamless verify keeps its own bootstrap secret for the conformance stack and is unaffected.

    The grant applies at signup only, so changing OWNER_EMAIL after an account exists promotes nobody. The success output and the README both say so.

  • 4ecf296: Add per-command help. Every command now answers -h / --help with usage, flags, subcommands, and examples scoped to that command (seamless init -h, seamless verify --help), and seamless help <command> prints the same thing. The help text lives in one registry (src/commands/helpTopics.ts) that both the full seamless --help output and the per-command output render from, so a flag is documented once and appears in both.

    The help check runs before a command parses its own arguments, so seamless init -h prints help instead of treating -h as a project name. A -- separator ends the check, so a command can still take a literal -h value (seamless config set key -- -h).

  • 6deafb1: seamless login now signs in to the Seamless portal instead of the active profile's instance, and no longer needs a profile to exist first. The portal session is stored beside the profile map in config.json and is the only session init uses to connect a managed application, so a session for a local or self-hosted instance no longer sends its token to the control plane.

    Instance login moves to seamless profile login [name], which signs in without changing the active profile. seamless login --profile <name> keeps working for one more minor version and prints a pointer to the new command. whoami and logout default to the portal session and take --profile <name> to target an instance.

    Set SEAMLESS_PORTAL_AUTH_URL to point the portal login at a different auth host.

  • 51d9a9e: No command renders a prompt when stdin is not a terminal. seamless init got this in 0.11.0; it now covers every command that prompts (login, profile add, users delete, users prepare-device-replacement, sessions revoke, org members remove, config apply, and config oauth-providers remove). Each one stops naming the flag that answers the question, so a CI step or a scripted run fails immediately instead of hanging until its job times out.

    The confirmations that guard a destructive action now take --force, matching what init already means by it:

    seamless users delete <id> --force
    seamless sessions revoke --all --force
    seamless org members remove <orgId> <userId> --force
    seamless config apply config.json --force

    --yes and -y are accepted aliases everywhere, so seamless config oauth-providers remove --yes keeps working. --force does not override --dry-run: config apply --dry-run --force still changes nothing, and cancelling a confirmation still reads as declining rather than as an error.

  • f08c21a: Scaffolding now requires init. An unrecognized command reports itself and exits instead of being treated as a project name, so seamless verfy no longer silently creates a directory called verfy and drops into the interactive scaffold. The error names seamless init <name> for anyone who was using the old shortcut.

    Ctrl-C is handled everywhere. Clack answers an interrupted prompt with a symbol, which several prompts cast straight to a string; that surfaced as a TypeError mid-scaffold, or as "Selected template Symbol(...) is not in the registry". Every prompt in init, the OAuth setup, the managed application picker, and bootstrap-admin now cancels cleanly and exits 130.

    init no longer leaves a project directory behind. Any failure or cancellation after the directory is created removes it, including a Ctrl-C during a download or a git clone, so a retry is not blocked by "Directory already exists". Only a directory the command itself created is ever removed, never an existing one and never the working directory.

    Declining the service token rotation prompt, or cancelling the application picker, now cancels the whole command rather than returning quietly part-way through.

Patch Changes

  • bf857b1: Update the seamless auth api image to v0.5.0.

  • e2f53b3: seamless init no longer hangs when it has no terminal to prompt on. Run on a pipe, it used to render a prompt nobody could answer and wait forever, so a CI step failed only when its job timed out. It now stops on the first unanswered question and names the flag that answers it:

    $ seamless init --local < /dev/null
    Error: "Web example" needs an interactive terminal, and this run does not have one.
    Pass --web=<id> to choose one (see `seamless templates list`), or --yes to take the
    recommended template.
    

    A run whose answers all come from flags is unaffected and works the same on a pipe as on a terminal. A terminal too narrow to render a prompt (a pty allocated without a size reports one column, which used to print one character per line) now warns instead of just looking broken.

  • f725752: Generated compose files now publish every port on 127.0.0.1 instead of all interfaces. A scaffolded stack was reachable from any machine on the same network, which mattered most for the auth server: it is configured with ALLOW_UNCREDENTIALED_DELIVERY_SECRETS=true so seamless login --local can read OTP codes from the response, and that opt-in is honored before any service-token check. Anyone on the LAN could request a code for any user of the stack and read it. Postgres was exposed on the same terms, with the fixed credentials the compose file ships.

    Local development is unchanged: the browser, the CLI, and inter-container traffic all still work.

  • ffe4331: Update the conformance harness for the removal of the admin bootstrap invite flow in seamless-auth-api. The verify stack now sets OWNER_EMAIL instead of SEAMLESS_BOOTSTRAP_ENABLED/SEAMLESS_BOOTSTRAP_SECRET, and the first-admin spec registers the owner address and asserts the admin role is granted at signup.

  • 2d898e2: Fix conformance project routing when the checkout path contains a directory named with an api/ segment. Playwright applies a testMatch regex to the absolute file path, so the api project's pattern also claimed every adapter and react spec, running browser tests in a project with no baseURL. Each project now scopes itself with testDir instead.

  • c2e4e1b: Scaffold from seamless-templates v0.5.0, which teaches the express starter to read DATABASE_URL and negotiate TLS when the connection string carries sslmode=require. This is what turns on the managed bundled database wiring: the CLI already computed the connection string, but the pinned v0.4.0 starter did not declare the placeholder, so nothing was written.

0.10.2

Patch Changes

  • 5c71d8c: Fix the released conformance smoke (released-smoke): bump the harness adapter's @seamless-auth/express pin from ^0.8.0 to ^0.9.0. The published 0.8.0 still served the OTP/magic-link generate routes as GET, while the harness flows and @seamless-auth/react 0.5.0 both POST them, so every adapter (and downstream react) flow failed with generate-email-otp -> 404. 0.9.0 serves them as POST, matching what the source (--local) conformance already builds.

0.10.1

Patch Changes

  • 166afdd: Bump the pinned Seamless component versions the CLI pulls to their latest published tags: auth-api v0.3.0 → v0.4.0, admin dashboard v0.1.0 → v0.3.0, and the templates ref v0.3.0 → v0.4.0.

0.10.0

Minor Changes

  • 46ed8dd: Let adopters choose how the admin console is hosted during seamless init.

    The old "Include Admin Dashboard?" / image-vs-source prompts are replaced by a single question with four options:

    • Served by your API at /console (recommended default) — the app backend proxies the console via the SDK's createSeamlessConsoleProxy, so it loads from the API's own origin. The scaffold sets SERVE_ADMIN_CONSOLE=true on the API, SERVE_ADMIN_DASHBOARD=true on the auth server, and adds the API origin to the auth server's ORIGINS so console passkey ceremonies verify. No separate admin container.
    • Separate container — official image or cloned source, as before, on http://localhost:5174.
    • None — no console is scaffolded.

    Each choice pre-configures the auth-server env, the app-backend env, the Docker Compose services, seamless.config.json, the success output, and seamless check accordingly. Pins the auth API image to v0.3.0 and the templates to v0.3.0 (which env-gate the console proxy).

  • e612a10: Enable email_otp in the scaffolded auth server's default login methods.

    The auth server's own default (passkey,magic_link) has no method the CLI can drive without a browser authenticator, so seamless login could not sign in to a freshly scaffolded local stack. buildAuthEnv now appends email_otp to LOGIN_METHODS (composing with the OAuth method when providers are configured), so email-OTP login works out of the box.

  • 7fbaff0: Enable ALLOW_UNCREDENTIALED_DELIVERY_SECRETS=true in the scaffolded auth server env.

    Companion to the email_otp default: with it set, seamless login --local reads the OTP straight from the auth server's response instead of needing a mail provider, so signing in to a freshly scaffolded local stack works end to end. It's a dev-only escape hatch — the auth server ignores it under a production NODE_ENV, and the scaffold runs as development.

Patch Changes

  • 2e27156: Fix seamless bootstrap-admin to target the app API instead of the login profile's auth server.

    The bootstrap invite route (/auth/internal/bootstrap/admin-invite) and its delivery are exposed by the app API (the SeamlessAuth server adapter), not the auth server directly — the auth server does not serve that path. Previously bootstrap-admin fell back to the active profile's instanceUrl, so once a profile pointed at the auth server (as seamless login and the admin commands require), bootstrap requests 404'd.

    bootstrap-admin now resolves its target independently of any profile: --api-url <url> → SEAMLESS_API_URL → the local default http://localhost:3000. The --profile flag is removed from this command (it no longer affects the target; the bootstrap secret is still resolved from the local project).

  • 3dbdc61: Reconcile documentation with actual behavior.

    • README: correct the Node requirement (24, per .nvmrc/engines, not 18); update bootstrap-admin docs to the --api-url → SEAMLESS_API_URL → http://localhost:3000 resolution (the removed --profile flag and auth-server-profile wording are gone).
    • AGENTS.md: drop the npm run typecheck/lint/format:check commands that don't exist (there is no lint/format tooling yet); note the --filter=<flow> (= form) for verify; list the instance-management commands; remove the stale top-level templates/ reference (templates live in the seamless-templates monorepo).
    • package.json: drop the dead templates entry from files.

    Closes #94.

  • e81a07d: Make seamless check managed-aware and resilient to partial config.

    checkCompose dereferenced config.docker.composeFile, which is null for managed projects, so seamless check crashed with a TypeError on any managed scaffold. check now branches on services.auth.mode === "managed": it validates the remote instance's /health/status and skips the Docker/compose/container checks (which don't apply remotely). It also wraps JSON.parse and guards missing service entries so a malformed or partial seamless.config.json prints a friendly message instead of a stack trace. The local console health check is derived from services.admin.mode (image/source → :5174, api → :3000/console, none/hosted → skipped).

    Closes #78.

  • c4a0f93: Fix the conformance adapter flows to match the SDK's POST OTP/magic-link routes.

    @seamless-auth/express now serves OTP generate routes (and /magic-link) over POST, but the harness adapter flows still called them with GET, so every adapter spec failed at generate-email-otp -> 404 once the stack came up. The adapter flows now POST /auth/otp/generate-email-otp, /auth/otp/generate-login-email-otp, and /auth/magic-link.

    Closes #111.

  • 545aafc: Disable auth rate limits in the conformance stack.

    The conformance suite drives many OTP/registration/magic-link flows from a single IP and trips auth-api's dedicated per-IP limiters (which RATE_LIMIT doesn't tune), so the adapter layer failed with 429s once the stack came up. The verify compose now sets DISABLE_AUTH_RATE_LIMITS=true on the auth-api service — a dev-only flag (ignored under NODE_ENV=production) added in seamless-auth-api.

    Requires seamless-auth-api with DISABLE_AUTH_RATE_LIMITS support; conformance builds it from source, so no release is needed.

  • f7e5b5d: Fix the conformance stack and surface container logs on failure.

    auth-api 0.3.0 requires FRONTEND_URL at startup (a required system config), which the verify compose never set — so the auth-api container exited on boot and every conformance run (here and in sibling repos calling the reusable workflow) aborted with a bare "docker failed" before any layer ran. verify/docker-compose.verify.yml now sets FRONTEND_URL, and seamless verify dumps recent container logs on a setup failure so a container that exits on startup is diagnosable instead of hidden.

    Closes #107.

  • 54f1f02: Quote generated .env values that a dotenv parser would otherwise misread.

    writeEnv wrote bare KEY=value, so a value containing #, whitespace, quotes, a backslash, or a newline (e.g. a managed API_SERVICE_TOKEN or a pasted OAuth secret) produced a .env that dotenv truncates or mis-parses. Values that need it are now double-quoted and escaped, and parseEnv/parseEnvString unquote on read so the CLI round-trips its own output. Simple values (tokens, URLs, hex secrets) are still written bare.

    Closes #81.

  • 943d13d: Harden the managed init flow.

    • Explicit managed intent no longer silently scaffolds local. When --app is given but there is no usable session (expired or control plane unreachable), init now fails with an actionable message instead of quietly scaffolding a self-hosted project and ignoring the flag. Without --app, a missing session or an unreachable control plane falls back to local with a clear warning (rather than aborting on transient network errors, as it previously did for non-reauth failures). Closes #79.
    • Service-token rotation is now recoverable. Rotation invalidates the app's previous token, so it runs after templates are copied (the likeliest failure point), and every step after it is guarded: if scaffolding fails post-rotation, the freshly issued token is printed so a deployed app can be re-wired instead of left bricked. The same guard covers integrateExistingProject. Closes #80.
  • 9eaaf0e: Fix the conformance adapter crash: the dev service token was too short.

    seamless verify defaulted API_SERVICE_TOKEN to a 24-char constant, which the adapter reuses as its cookie secret; a newer @seamless-auth/express rejects a cookieSecret shorter than 32, so the adapter container exited and conformance failed after the stack came up. The dev service token and bootstrap secret defaults are now >=32 characters.

    Closes #109.

0.9.0

Minor Changes

  • 3de03ef: Add seamless config oauth-providers <list|add|update|remove> for per-provider OAuth management, backed by the auth API's dedicated provider routes (GET/POST /system-config/oauth-providers, PATCH/DELETE /system-config/oauth-providers/:id). Each command touches a single provider, so concurrent edits no longer clobber the whole oauth_providers array the way config set oauth_providers / config apply do. add and update accept an inline JSON object or --file <path>; remove confirms first (skip with --yes). Client secrets stay server-side: providers are referenced by clientSecretEnv and the secret value is never sent. The whole-config editor commands are unchanged.
  • 9043643: Add seamless login --local for self-hosted and local instances. It asks the instance to return the email or phone OTP in the response body instead of sending it, then verifies with that code automatically, so logins work without a real mail or SMS provider. It only runs against local hosts and requires the auth API to run outside production with ALLOW_UNCREDENTIALED_DELIVERY_SECRETS=true.

Patch Changes

  • 26a78d6: Fix seamless login rejecting valid email OTP codes. Email OTPs are six letters, but the code prompt only accepted digits, so no real email code could be entered. The prompt is now channel-aware: it accepts a six-letter code (case-insensitive, normalized to uppercase) for email logins and a numeric code for phone logins, with matching placeholder text.
  • f8dc6fc: Fix the conformance harness (seamless verify --local) failing to build after the ecosystem moved to Express 5 on Node 24. The verify adapter app pinned express@^4, but @seamless-auth/express now requires express@>=5 as a peer, so installing the locally-built SDK tarball aborted with an ERESOLVE peer conflict and the Docker build failed. The adapter now depends on express@^5.1.0 (and @seamless-auth/express@^0.8.0), and the verify Docker images are bumped from node:20 to node:24 to match the ecosystem's supported runtime.

0.8.0

Minor Changes

  • 8468863: Target bootstrap-admin at the active profile's instance URL instead of requiring a local seamless.config.json. The command now resolves the instance from the active profile (respecting --profile and SEAMLESS_PROFILE), with SEAMLESS_API_URL as an override and http://localhost:3000 as the fallback when no profile is configured. The bootstrap-secret auth flow is unchanged.

Patch Changes

  • 5126998: Document bootstrap-admin in the README: add a "Creating the first admin" section covering the command, its profile-based instance targeting (with the SEAMLESS_API_URL override and http://localhost:3000 fallback), and the bootstrap-secret authentication it uses because it runs before any admin exists.

0.7.0

Minor Changes

  • 6af3216: feat(init): connect scaffolds to a managed instance when a profile is logged in

    seamless init now defaults to the managed control plane whenever a profile has an active session. It reads the logged-in session, lists your applications, issues the application's service token from the control plane (rather than generating a local secret), and points the scaffolded web and api at the managed auth instance. Running init inside an existing project wires the managed credentials into api/.env in place, or prints them when there is no api directory. The self-hosted flow stays available with seamless init --local, and is still used automatically when no session is present. Set SEAMLESS_PORTAL_API_URL to override the control-plane host.

Patch Changes

  • f8c4478: chore(templates): scaffold from seamless-templates v0.2.4

    Bump SEAMLESS_TEMPLATES_REF to v0.2.4. The Express template now has a working production npm run start (fixed extensionless ESM imports and the migration runner path) and ships the docker-compose.yml its scripts and README referenced. The web templates (react-vite and react-oauth) drop the unused VITE_AUTH_SERVER_URL, and all templates document both the local and managed init paths.

0.6.0

Minor Changes

  • d09317c: Add admin verbs for users and organizations (requires an admin role). seamless users covers list (with client-side --limit/--offset paging and --json), delete <id> (with confirmation), credentials <id> (from the admin user detail endpoint), and prepare-device-replacement <id> for admin-assisted recovery. seamless org covers list, create, get, and update, and seamless org members covers list, add (by --user id or --email, with --roles/--scopes), update, and remove (with confirmation). Every command surfaces a 403 as a clear permission error, and device replacement explains the step-up requirement when the CLI session is not elevated. Accepts --profile and honors SEAMLESS_PROFILE.
  • 29cef7d: Add a multi-profile config store and seamless profile commands so the CLI can target multiple Seamless Auth instances (self-hosted, managed tenant, local dev) under named profiles. Profiles live in ~/.config/seamless/config.json (respecting XDG_CONFIG_HOME) and hold no secrets. New subcommands: profile list, profile add, profile use, and profile remove. The active profile can be selected per command with --profile <name> or the SEAMLESS_PROFILE environment variable, defaulting to the default profile.
  • 4d9cc23: Add an authenticated HTTP client that targets the active profile's instance, attaches the Bearer access token, and transparently refreshes on expiry. On a 401 it calls POST /refresh with the opaque refresh token, persists the rotated pair, and retries the original request once. A rotated or reused refresh token clears the local session and raises a clear re-login prompt instead of a stack trace. Non-JSON and empty response bodies are parsed defensively, and rate-limit (429) responses are surfaced without triggering a refresh.
  • f24a71d: Add seamless config, config-as-code for an instance's system configuration (requires an admin role). config get [key] [--json] reads the config from GET /system-config/admin, config set <key> <value> writes one key via PATCH /system-config/admin (the value is parsed as JSON, falling back to a string, so TTLs, arrays, booleans, and numbers all work), and config roles lists the instance's roles. config diff <file> shows how a local JSON config file differs from the instance, and config apply <file> applies the delta after a confirmation prompt, with --dry-run to preview. Read-only or unknown keys in a file are ignored on apply, and a non-admin user gets a clear permission error. Accepts --profile and honors SEAMLESS_PROFILE.
  • 69fb8c8: Store session tokens in the OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service) via @napi-rs/keyring instead of on disk. Tokens are scoped per profile (keyed by profile name and instance URL) so multiple instances never collide, and the refresh token, the durable secret, never touches config or logs. seamless profile remove now clears the profile's keychain entry. When no keychain is available (for example headless CI), the CLI reads a refresh token from SEAMLESS_REFRESH_TOKEN if set and otherwise fails with a clear, documented error rather than writing secrets to disk.
  • 4301da5: Add seamless login, an interactive email OTP login for the active profile's Seamless Auth instance. It calls POST /login (honoring the instance's returned loginMethods), triggers the code with GET /otp/generate-login-email-otp, prompts for the code you paste from your inbox, and verifies it with POST /otp/verify-login-email-otp. On success it stores the session in the OS keychain and records the identity (sub, email, identifier type) on the profile. The command caps local code retries so it does not trip the per-IP OTP limiter, surfaces a 429 clearly, refreshes the code automatically if the 5 minute ephemeral window lapses, and reports unreachable instances without a stack trace. Accepts the identifier positionally or with --identifier, and targets a specific profile with --profile.
  • 79d076e: Add seamless sessions to list and revoke the logged-in user's active sessions. seamless sessions (or sessions list) calls GET /sessions and renders each session's id, device or user agent, IP, and last-used time, marking the current session. seamless sessions revoke <id> calls DELETE /sessions/:id, and seamless sessions revoke --all calls DELETE /sessions. Revoking the current session, or all sessions, prompts for confirmation first and then clears the local keychain tokens, since that request signs you out. Accepts --profile and honors SEAMLESS_PROFILE.
  • a9c1ef3: Add seamless whoami and seamless logout. whoami calls GET /users/me through the authenticated client and prints the identity (sub, email, roles) alongside the active profile and instance URL, failing cleanly with a "not logged in" message when there is no session. logout ends the current session with DELETE /logout and then clears the profile's keychain tokens; logout --all revokes every session for the user with DELETE /logout/all first. Both commands accept --profile (and honor SEAMLESS_PROFILE) and always clear the local tokens even if the server session was already gone.

Patch Changes

  • f8e8cdb: Document the CLI authentication commands in the README (profiles, login, whoami, sessions, logout, config, and the users and organizations admin verbs), including per-platform keychain behavior and the headless SEAMLESS_REFRESH_TOKEN fallback. Add a gated end-to-end test that drives the real login flow, an authenticated call, transparent refresh, and refresh-reuse rejection against a running instance (enabled with SEAMLESS_E2E_URL), plus an opt-in rate-limit check.
  • f978890: seamless verify now prints a consolidated summary report at the end of the run: the seamless package versions under test (source versions for --local, the declared pins for released runs), one line per conformance layer (API / adapter and each web template) with its pass/fail status and duration, and an overall verdict. It is printed after teardown so it stays on screen without scrolling back through the phase output.

0.5.2

Patch Changes

  • 9992373: Bump the pinned seamless-templates ref to v0.2.3, so seamless init scaffolds the templates that ship @seamless-auth/react ^0.4.0 (TOTP support) and @seamless-auth/express ^0.7.0.

0.5.1

Patch Changes

  • 9a9953a: Fix local auth mode regenerating the auth server's .env a second time with fresh secrets, which left the scaffolded API's API_SERVICE_TOKEN mismatched with the auth server's when services run outside Docker. The compose builder now reads the already-written auth env instead of rewriting it.

0.5.0

Minor Changes

  • 9bfaa5f: When the OAuth template is selected, seamless init now prompts for OIDC providers (Google, GitHub, Microsoft, GitLab) and their client id/secret, then wires them into the scaffolded auth server: OAUTH_PROVIDERS config, a per-provider *_CLIENT_SECRET env var, the oauth login method, and the http://localhost:5173/oauth/callback redirect URI. Providers left without credentials are scaffolded disabled with a printed next-steps note. Apple is documented as manual (its client secret is a signed JWT and it has no userinfo endpoint). The scaffold now also generates REFRESH_TOKEN_LOOKUP_SECRET, TOTP_SECRET_ENCRYPTION_KEY, and OAUTH_STATE_SECRET (previously left empty, falling back to the service token), and adds a healthcheck to the generated web container.

0.4.0

Minor Changes

  • 7409238: seamless init --<example> selects a use-case starter by its registry alias (e.g. seamless init --oauth), skipping the web prompt. Aliases are defined in the templates registry, so adding an example needs no CLI change. Unknown flags list the available examples, and the interactive web prompt now presents the available examples.
  • fcac569: Scaffold web and api starters from the seamless-templates registry instead of hardcoded per-framework generators. The CLI now reads the registry to build its prompts, downloads the selected templates from the templates monorepo at a pinned ref, and applies each template's env contract. Adding a new framework is a templates-repo change, not a CLI change. Set SEAMLESS_TEMPLATES_DIR to scaffold from a local checkout, or SEAMLESS_TEMPLATES_REF to pin a different ref.
  • 27a6ec1: seamless verify now conformance-tests every web template in the registry. It runs the API and adapter layers once, then builds, serves, and drives each web template in turn, scoping the browser suite to the flows the template's manifest declares (verify.flows, e.g. ["oauth"]). React specs are tagged by feature (@login, @oauth) so a template runs only its relevant flows; a template with no declared flows runs the full suite. SEAMLESS_REACT_DIR still overrides with a single template.

Patch Changes

  • fbcc17e: Bump the verify adapter to @seamless-auth/express@^0.6.0 (the stable release that includes the registration-session and non-JSON-response fixes), so the released conformance run tests against the current published packages.
  • 57c7819: Point the conformance harness at the seamless-templates monorepo. seamless verify now resolves the React web template from ../seamless-templates/templates/web/react-vite by default (still overridable with SEAMLESS_REACT_DIR), and the reusable verify-conformance.yml workflow checks out seamless-templates (input templates-ref) instead of the standalone starter repo.
  • c41e68b: seamless verify now resolves the web template to conformance-test from the seamless-templates registry (the first runnable web template) instead of a hardcoded path. SEAMLESS_REACT_DIR still overrides with a direct template path, and SEAMLESS_TEMPLATES_DIR points at a local templates checkout. Running the browser suite against every web template is a follow-up.

0.3.0

Minor Changes

  • 993d386: Add seamless verify, a one-command cross-package auth conformance harness. It stands up the auth API, the Express adapter, and the React starter, then runs a matrix of flows (register, email and phone OTP, magic-link, passkey register and login, OAuth, TOTP, step-up, refresh, sessions, logout, organizations, admin bootstrap, and JWKS) across the api, adapter, and browser layers, and prints a pass/fail grid alongside JUnit and HTML reports.

    Supports --local (build the @seamless-auth/* packages from source for pre-publish contract testing) and a released mode against the published packages, plus filters like --api-only, --no-react, --filter, and --keep-up. Ships a reusable GitHub workflow so a change in any ecosystem repo runs the matrix against the others.