Releases: fells-code/seamless-cli
Release list
v0.17.0
Minor Changes
-
2bcfa33: Scaffold from seamless-templates
v0.15.0, the release that carries the Expo starter.0.16.0taughtinitthemobiletemplate kind but left the registry pin onv0.14.0, which has
no such kind, so the question was never asked and--mobile=expowas refused as an unknown option.
With the pin onv0.15.0the registry lists the starter asexpo(aliasmobile, beta):init
offers it after the backend,--mobilescaffolds it atmobile/withEXPO_PUBLIC_API_URLfilled
from the manifest, and the success output says how to start it. The starter is a complete Expo
Router app on@seamless-auth/react-native0.1.0(email code, sign-in link, passkeys, a
protected call to the companion API), and itstools/associations/generator writes the files
native passkeys need.--yesstill scaffolds without it.The same release moves the API starters to
@seamless-auth/express0.16.0and
@seamless-auth/fastify0.7.0and passesauthServerUrlandaudienceintorequireAuth, so a
scaffolded API accepts the auth API's access token from the native app alongside browser cookies,
and its/authmount answers a bearer client with tokens in the body. The conformance harness's
adapters take the same^0.16.0and^0.7.0. The starters also keep their SSL options when
DATABASE_URLcarriessslmode(templates0.15.0's patch note). The web starters stay on
@seamless-auth/react0.12.0, and the auth API and admin dashboard pins do not move.
v0.16.0
Minor Changes
-
b9529c6:
seamless initlearns themobiletemplate kind.seamless-templatesgains a third kind next towebandapi, 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, andverifyfiltered it out without a word.A mobile starter is optional.
initasks "Mobile app" after the backend, defaulting to none;
--mobile=<id|alias>(or the bare alias,--mobile/--expo) includes one;--yesscaffolds
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 atmobile/with its.envfilled from the
manifest, is recorded asservices.mobileinseamless.config.json, is checked byseamless checkonly when recorded, and is called out in the success output with how to start it and the
Android emulator host.verifyannounces the mobile templates it cannot drive instead of
filtering them silently. Template copying skips.expo,ios, andandroid.The templates registry pin moves to the release that carries the starter in a follow-up.
Patch Changes
-
4bbf7ac:
seamless verify --localpacks@seamless-auth/clientalongside@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/clientnext 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 readsworkspacesfrom the checkout's
package.json, packs both packages when it finds one, and the react image installs every
tarball in onenpm installso the dependency resolves from the sibling file. A checkout
that predates the workspace is packed as before. The version line reported for
@seamless-auth/reactreads the react package's own manifest in a workspace.
v0.15.0
Minor Changes
-
c99c759: Move the scaffold onto auth API
v0.13.1, admin dashboardv0.7.0, and seamless-templatesv0.14.0.v0.13.0instruments the auth path. Everyauth_eventsrow 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 newjti).GET /internal/metrics/funnelreports time to registration, time to login and passkey adoption, and
GET /internal/metrics/sign-insreports sign-in outcomes per method, device class, mail provider
and owner flag, with where attempts stop. The same release ships dashboardv0.7.0inside 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.1is the same server on a
release image that applies Debian security updates at build time, sincev0.13.0never published an
image (its scan failed on a base-imagepcre2finding).The admin dashboard pin moves with it, so the standalone console (
--admin=imageand
--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.0carries@seamless-auth/express0.15.0and@seamless-auth/fastify0.6.0
in the API starters, and the conformance harness's adapters take the same^0.15.0and^0.6.0.
Those adapters forward the browser's user agent to the API asx-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 readsunknown; the other dimensions do not
depend on it. Nothing else in the starters moves: the manifest contract and the registry are
byte-identical tov0.13.0, and the React starters stay on@seamless-auth/react0.12.0, since
the token claim the API added is one the client never reads.
v0.14.0
Minor Changes
-
af41ba4: Move the scaffold onto auth API
v0.12.0, admin dashboardv0.6.0, and seamless-templatesv0.13.0.v0.11.0puts passkey enrollment behind an access session. It is a breaking change to the wire
contract:/webAuthn/register/startand/webAuthn/register/finishused 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, answering200with the credential instead.
The same release declares and validates the window onGET /admin/users, and adds organization
deletion along with a paged, searchable admin organization list.v0.12.0ships dashboardv0.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=imageand
--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.0carries@seamless-auth/react0.12.0,@seamless-auth/express0.14.0and
@seamless-auth/fastify0.5.0in the starters, and the conformance harness's adapters take the
same^0.14.0and^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 answer401at
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
onGET /admin/usersandGET /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 dashboardv0.5.0,
and seamless-templatesv0.12.0.Three releases of auth API work land together.
v0.8.0gives 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 addsAUTHENTICATOR_POLICY, which decides attachment, user
verification, attestation, and whether a synced passkey may enrol.v0.9.0puts 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.0drops thesessions.refreshTokenHashcolumn 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_POLICYalong withSESSION_IDLE_TTL,MAX_CONCURRENT_SESSIONSand a commented
TRUST_PROXY, andREFRESH_TOKEN_TTLmoves from1hto1d.The admin dashboard bump keeps the standalone console (
--admin=imageand--admin=source) in step
with the one the API image now serves at/console, sincev0.10.0embeds dashboardv0.5.0itself.
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.0moves the starters onto@seamless-auth/react0.11.0,@seamless-auth/express
0.13.0and@seamless-auth/fastify0.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
fromAUTH_COOKIE_PREFIX, so two Seamless apps on one host stop overwriting each other's session. The
manifest contract is unchanged, so nothing ininitmoves with it.The conformance harness's adapters follow the same SDKs the starters now install:
@seamless-auth/express^0.13.0and@seamless-auth/fastify^0.4.0. Both stop repeating the
access and refresh tokens in the body of the response that sets them ashttpOnlycookies, and both
forward a magic link'sredirectUrito the API. -
ec85b59: Match the list windows the auth API now enforces, and let
org listpage.The API validates the window on its admin list routes:
limitis 1 to 100 and
offsetis 0 or more. The CLI checked both flags against one range with a floor
of zero, which was right for--offsetand wrong for--limit, so
users list --limit 0andusers list --limit 500were 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 0is 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 listgains--limit,--offsetand--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 wayusers listalready did, and--search
matches the name and slug server-side.
v0.13.0
Minor Changes
-
63ff4ff: Add
seamless check --strict, which exits non-zero when a check fails.checkis 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.--strictexits 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 runcheckat all.
Patch Changes
-
79aa298: Clean
distbefore building, so a stale artifact cannot be packed.buildwas a baretsc, which overwrites but never removes. A file deleted or renamed
insrcleft its old output behind, andprepublishOnlyruns the same script, so a local
publish could ship something no longer in the source tree. Thecleanscript already
existed and is now wired in. -
cfed9e6: Correct the documentation that had drifted from the rest of the ecosystem.
seamless checkandseamless verifynow 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 itmigrate:up, and the Docker path runs it for
you at container start. init --profilewas 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.mdthe 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, includingseamless-auth-serverandseamless-auth-react. config sethelp was missingsession_idle_ttlfrom the string-typed keys.
-
d039e18: Add
--jsontoseamless whoamiandseamless sessions list.config get/roles,users list/credentials, andorg list/get/members listall 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.", andwhoami --jsonreports a missingsuboremailas
nullinstead of the(unknown)the table shows. -
723d581: Point a managed scaffold at the instance URL the portal computes.
initcomposed the scaffolded backend'sAUTH_SERVER_URLfromapplication.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 atdomain/<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.resolveAppInstanceUrlalready existed for exactly this, andappsand the application
picker already used it; the two scaffold paths did not. They do now.The connectable-application filter asked for
domaintoo, so an application the portal
computed aninstanceUrlfor but whosedomaincolumn 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
(trialkey1for trials,paidkey1for 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.initnow 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 initmakes 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 bareTypeError: fetch failedthat 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
styleloginalready uses for an unreachable instance. Non-ok responses keep the messages
they had, and the original error is preserved as the thrown error'scause. -
4418970: Recognize the addresses a local dev instance actually answers on.
isLocalInstanceUrlaccepted onlylocalhost,127.0.0.1,::1and.localhost
subdomains. It gates two things: whether plaintexthttpis allowed for an instance URL,
and whether--localOTP delivery is permitted. A dev instance is commonly reached at
none of those, a container bound to0.0.0.0, a LAN address from a phone on the same
network, or an mDNS.localname, and each was rejected as if it were production, forcing
httpsonto a box with no certificate.Now also treated as local: the whole
127.0.0.0/8loopback range,0.0.0.0and::, the
private IPv4 ranges (10/8,172.16/12,192.168/16), link-local (169.254/16and
fe80::/10), IPv6 unique-local (fc00::/7), and.localnames. Ranges are matched by
octet rather than by prefix, so172.15,172.32and1.10.0.1stay public. -
f4ccb30: Stop writing
AUTH_MODEinto scaffolded projects.The scaffold set
AUTH_MODE=serverin 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 onlyAPI_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 ofcore/env.ts'swriteEnvthat still
emitted unquoted values, the bug fixed in the real one.buildJWKSConfigin the docker generator, which had no callers and was the only user of
core/jwks.ts, so that module went with it.generateKidincore/secrets.ts.generateSecretbeside it stays; it is widely used.setupDockerAuthin the auth generator, unreachable becausegenerateAuthServeris only
ever called with"local". It also wrote a compose file mountingpgdataat
/var/lib/postgresql/data, which the pinnedpostgres:18ignores, so it had gone stale
as well as unreachable.
With the unreachable branch gone,
generateAuthServerno longer needs a mode, and its
context: anyandmode: "local" | "docker" | Symbolparameters become a plainroot: string. -
74ae06c: Stop printing
Error: undefinedwhen something other than anErroris thrown.The top-level handler in
index.tsand the catch blocks inwhoamiandsessionsall
read.messageoff the thrown value, whichthrowdoes not guarantee exists. A rejected
promise carrying a string, a parsed response body, orundefinedprinted nothing useful,
naming neither the failure nor the fact that something unexpected came back.A new
errorMessagerenders any thrown value: anError's message (or its name when the
message is empty), a thrown string as it is, amessagefield 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.
v0.12.1
Patch Changes
-
2acea63: Stop reading a
401from/loginas "no such account", and stop claiming an account
exists when the instance will not say.POST /loginno longer answers401. An identifier with no usable account, which used
to cover unknown, unverified, and no-permitted-method, now gets200and a decoy pre-auth
token so the response cannot be used to test whether an account exists. Two branches in
completeLoginread that401and 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.423is 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.tsto 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
loginMethodsassertion 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 verifya 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 verifywas 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 verifynow layers an extra compose file over the base one when
SEAMLESS_VERIFY_COMPOSE_OVERRIDEis set. CI points it atverify/docker-compose.cache.yml,
which attaches atype=ghabuild 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, andtype=ghawould 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_ATTESTATIONSin 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 filetook 301s by itself. -
20f8e66: Follow the React SDK's passkey enrollment through the removal of its naming step.
@seamless-auth/reactused 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, soregisterWithPasskeywaited 30 seconds for a placeholder that no longer
renders and then timed out, failingreact/passkeyRegister.spec.tsandreact/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--localrun against an older React checkout, or a published
run before that release lands on npm, will fail on the modal this no longer dismisses.
v0.11.0
Minor Changes
-
3ef3b68: Add
seamless apps, so a portal account can see what it owns without opening the dashboard.
apps listprints 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--jsonand both require a portal session.Applications are now read through the portal's
instanceUrl, which is derived from the service
plan, rather than the storeddomaincolumn that goes stale when a trial is upgraded. Applications
that have not finished provisioning are listed instead of being silently dropped.initis
unchanged: it still readsdomainand still considers only applications that have one.apps listshows the infra id as the reference (falling back to the id before provisioning), andapps getaccepts an id, a name, or an infra id. -
9d089c7: Move the scaffold onto the current Seamless ecosystem: auth API
v0.7.1, admin dashboardv0.4.0,
and seamless-templatesv0.8.1(which carries@seamless-auth/react^0.8.0in both React
starters,@seamless-auth/express^0.12.0in the Express starter, and@seamless-auth/fastify
^0.3.1in 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 fromGET /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'sttl
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/core0.12.1parses the lifetime before it reaches an adapter and rejects anything
that is not a positive whole number of seconds, and auth APIv0.7.1sends the value as a number.seamless initnow 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/consolebehindSERVE_ADMIN_CONSOLE. Two boot-time fixes land with it: an empty
PORT=in.envnow falls back to 3000 rather than binding a random free port, andpino-pretty
moves to a runtime dependency so an install without dev dependencies boots. Both Express and Fastify
starters ship.env.examplesecret placeholders long enough to clear the adapter's 32 character
minimum, so the documentedcp .env.example .env && npm run devpath boots. A project from
seamless initwas already unaffected, because the CLI fillsCOOKIE_SIGNING_KEYitself.Both React starters gain a protected
/sessionroute 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 aJSON.stringifydump. 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_EMAILgrant the CLI
already writes, so the generated.envno longer carriesSEAMLESS_BOOTSTRAP_ENABLED,
SEAMLESS_BOOTSTRAP_SECRET, orSEAMLESS_AUTH_DEBUG_SECRETS.AVAILABLE_ROLESnow offers
admin:readandadmin:writealongside bareadmin, and assigning a role the instance does not
list is rejected rather than silently doing nothing. Postgres TLS is configurable throughDB_SSL,
DB_SSL_CA, andDB_SSL_REJECT_UNAUTHORIZED, andDB_URIis accepted as aDATABASE_URLalias.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 in0.11.0splitserrorinto
errorCodeanderrorBodyon the handler result types, which only affects code importing handlers
from@seamless-auth/coredirectly; the adapter usescreateSeamlessAuthServer, so it needed no
source change. -
4917717: Make
seamless inittemplate flags discoverable, predictable, and safe to get wrong.Add
seamless templates list [--json], which prints every starterinitcan scaffold with its id,
kind, framework, selecting flags, and status. It reads the same registryinitdoes (so
SEAMLESS_TEMPLATES_DIRandSEAMLESS_TEMPLATES_REFapply) 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>, soseamless init --react-viteworks 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
initcreates 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:
initnow offers the managed path instead of assuming it. A portal session used to make managed the
default silently, with--localas the only escape and no way to learn you needed it until after the
template prompts. When your account has a provisioned application,initasks 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
NoApplicationsErrorgot wrong for anyone mid-provision.A directory that already has files is no longer forced down the integrate path.
initasks whether
to connect it to a managed application or scaffold in place. Scaffolding into a non-empty directory
was previously impossible, so a strayREADMEor.gitwas 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.
--localand--app <id>skip the new prompts, and--appwithout 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--emailor the portal session.--yesdeliberately 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--forcetoo, 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.
initreads the
application's database and writesDATABASE_URLintoapi/.envas
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. Runninginitinside an existing project adds
DATABASE_URLonly 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_URLand
negotiate TLS. UntilSEAMLESS_TEMPLATES_REFis bumped to it, the value is computed but the pinned
starter does not declare the placeholder, so nothing is written. -
15b487a:
initnow asks for your email and writes it to the scaffolded auth server asOWNER_EMAIL. The auth
server grants the admin role at account creation to a signup matching that address, so the local...
v0.10.2
Patch Changes
- 5c71d8c: Fix the released conformance smoke (
released-smoke): bump the harness adapter's
@seamless-auth/expresspin from^0.8.0to^0.9.0. The published 0.8.0 still
served the OTP/magic-link generate routes asGET, while the harness flows and
@seamless-auth/react0.5.0 bothPOSTthem, so every adapter (and downstream
react) flow failed withgenerate-email-otp -> 404. 0.9.0 serves them asPOST,
matching what the source (--local) conformance already builds.
v0.10.1
Patch Changes
- 166afdd: Bump the pinned Seamless component versions the CLI pulls to their latest
published tags: auth-apiv0.3.0→v0.4.0, admin dashboardv0.1.0→
v0.3.0, and the templates refv0.3.0→v0.4.0.
v0.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'screateSeamlessConsoleProxy, so it loads
from the API's own origin. The scaffold setsSERVE_ADMIN_CONSOLE=trueon the
API,SERVE_ADMIN_DASHBOARD=trueon the auth server, and adds the API origin
to the auth server'sORIGINSso 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, andseamless checkaccordingly. Pins the auth API image tov0.3.0and the templates to
v0.3.0(which env-gate the console proxy). - Served by your API at /console (recommended default) — the app backend
-
e612a10: Enable
email_otpin 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, soseamless logincould not sign in to
a freshly scaffolded local stack.buildAuthEnvnow appendsemail_otpto
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=truein the scaffolded auth server env.Companion to the
email_otpdefault: 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-adminto 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-adminfell back to the active profile'sinstanceUrl, so once a
profile pointed at the auth server (asseamless loginand the admin commands
require), bootstrap requests 404'd.bootstrap-adminnow resolves its target independently of any profile:
--api-url <url>→SEAMLESS_API_URL→ the local defaulthttp://localhost:3000.
The--profileflag 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);
updatebootstrap-admindocs to the--api-url→SEAMLESS_API_URL→
http://localhost:3000resolution (the removed--profileflag and
auth-server-profile wording are gone). - AGENTS.md: drop the
npm run typecheck/lint/format:checkcommands that
don't exist (there is no lint/format tooling yet); note the--filter=<flow>
(=form) forverify; list the instance-management commands; remove the stale
top-leveltemplates/reference (templates live in theseamless-templates
monorepo). package.json: drop the deadtemplatesentry fromfiles.
Closes #94.
- README: correct the Node requirement (24, per
-
e81a07d: Make
seamless checkmanaged-aware and resilient to partial config.checkComposedereferencedconfig.docker.composeFile, which isnullfor
managed projects, soseamless checkcrashed with aTypeErroron any managed
scaffold.checknow branches onservices.auth.mode === "managed": it validates
the remote instance's/health/statusand skips the Docker/compose/container
checks (which don't apply remotely). It also wrapsJSON.parseand guards missing
service entries so a malformed or partialseamless.config.jsonprints 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/expressnow serves OTP generate routes (and/magic-link) over
POST, but the harness adapter flows still called them with GET, so every adapter
spec failed atgenerate-email-otp -> 404once 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 (whichRATE_LIMITdoesn't
tune), so the adapter layer failed with 429s once the stack came up. The verify
compose now setsDISABLE_AUTH_RATE_LIMITS=trueon the auth-api service — a
dev-only flag (ignored underNODE_ENV=production) added in seamless-auth-api.Requires seamless-auth-api with
DISABLE_AUTH_RATE_LIMITSsupport; 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_URLat startup (a required system config), which
the verify compose never set — so the auth-api container exited on boot and every
conformancerun (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 setsFRONTEND_URL, andseamless verifydumps recent container logs on a
setup failure so a container that exits on startup is diagnosable instead of hidden.Closes #107.
-
54f1f02: Quote generated
.envvalues that a dotenv parser would otherwise misread.writeEnvwrote bareKEY=value, so a value containing#, whitespace, quotes,
a backslash, or a newline (e.g. a managedAPI_SERVICE_TOKENor a pasted OAuth
secret) produced a.envthat dotenv truncates or mis-parses. Values that need
it are now double-quoted and escaped, andparseEnv/parseEnvStringunquote 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
initflow.- Explicit managed intent no longer silently scaffolds local. When
--appis
given but there is no usable session (expired or control plane unreachable),
initnow 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 coversintegrateExistingProject. Closes #80.
- Explicit managed intent no longer silently scaffolds local. When
-
9eaaf0e: Fix the conformance adapter crash: the dev service token was too short.
seamless verifydefaultedAPI_SERVICE_TOKENto a 24-char constant, which the
adapter reuses as its cookie secret; a newer@seamless-auth/expressrejects 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.