@foir/sdk 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -108,6 +108,77 @@ nobody asking for `@foir/sdk` gets it early. The push that carries the commit to
108
108
 
109
109
  Changes on `dev` that have not been published to npm.
110
110
 
111
+ ## [0.25.0] — 2026-10-18
112
+
113
+ ### Changed
114
+
115
+ - **`deleteMyAccount` for an account with no password needs a recent sign-in**
116
+ (`@foir/sdk/auth`). `confirm` alone used to be enough. Now it's refused
117
+ unless the user signed in within the last ten minutes, so a stolen session
118
+ can't delete an account. Migration: when it's refused, send the user through
119
+ sign-in again, then call it once more.
120
+
121
+ ### Added
122
+
123
+ - **`organizations.pool`, `organizations.startPool` and `organizations.dropPool`**
124
+ (`@foir/sdk/app`). In an operation, read which pool the people of the
125
+ organisation the run acts in sign in to, start its own pool, or drop it.
126
+ Reading needs `organizations:read`, and the other two `organizations:write`.
127
+ Starting one is refused while anybody of the pool it signs in to now stands
128
+ in it or beneath it, and dropping one while it holds anybody; each refusal
129
+ says how many. Each answers an `AppOrganizationPool`.
130
+ - **`AppOrganizationPool`** (`@foir/sdk/app`, type).
131
+ - **`organizations.provision` makes its first member by address**
132
+ (`@foir/sdk/app`): `firstMember: { email, firstName?, lastName? }`, made in
133
+ the pool that serves the new organisation (its own with `ownPool`). It also
134
+ needs `users:create`.
135
+ - **`leaveMembership`** (`@foir/sdk/auth`). The signed-in person ends their own
136
+ membership where the request stands: the organisation `orgId` names, or the
137
+ project's own. It asks no permission. It's refused where it would leave an
138
+ organisation with nobody who can staff it, or leave them nowhere to sign in,
139
+ since a project's people sign in only where they stand.
140
+ - **`myAddresses`, `addMyAddress`, `proveMyAddress`, `makeMyAddressPrimary`
141
+ and `removeMyAddress`** (`@foir/sdk/auth`), and the **`MyAddress`** type. A
142
+ signed-in person adds an email address to their own account. It's kept only
143
+ once the code it was sent proves it. Adding one asks for their current
144
+ password, or, for an account with none, a sign-in in the last ten minutes.
145
+ An address another account in the project holds isn't added. The address
146
+ the account uses moves only to a proven one, and it can't be removed.
147
+ - **`exportMyData`** (`@foir/sdk/auth`), and the **`MyDataExport`** type. A
148
+ signed-in person takes a copy of their own data: their account, and
149
+ everything deleting it would erase (the records and files they own). It
150
+ holds no password, code or token value.
151
+ - **`renameFactor`** (`@foir/sdk/auth`). A signed-in person renames one of
152
+ their own security keys or passkeys. A name proves nothing, so it asks for
153
+ no step-up.
154
+
155
+ ## [0.24.0] — 2026-10-18
156
+
157
+ ### Changed
158
+
159
+ - **`joinWaitlist` resolves `true` for every address** (`@foir/sdk/auth`). It
160
+ used to resolve the waitlist entry, whose status told anyone who asked
161
+ whether an address had been approved. The server now answers every address
162
+ alike, whether it was queued, was already let in, or already has an account,
163
+ and what differs is only the email the address is sent. Migration: stop
164
+ reading `id`, `email` or `status` from the result; a resolved call means the
165
+ address was not refused.
166
+ - **`myProviderToken` and `logout` act for one sign-in** (`@foir/sdk/auth`). A
167
+ provider's token belongs to the sign-in that obtained it, so `logout` ends
168
+ this device's and no other device's, and a sign-in with nothing kept for it
169
+ resolves `linked: false` with `reauthenticate: true`. Migration: where
170
+ `reauthenticate` is true, show the user the way to sign in through the
171
+ provider again rather than an empty or failed page.
172
+
173
+ ### Removed
174
+
175
+ - **`WaitlistJoinResult`** (`@foir/sdk/auth`, type). `joinWaitlist` resolves a
176
+ boolean.
177
+ - **`verifyEmail`** (`@foir/sdk/auth`). It redeemed verification tokens that
178
+ only the console's own sign-up minted, and that lane is gone. An address is
179
+ proven by the code it is sent: `requestOTP`, then `loginWithOTP`, which marks
180
+ the address proven and lets in a person who was waiting to prove it.
181
+
111
182
  ## [0.23.0] — 2026-10-16
112
183
 
113
184
  ### Added
@@ -186,23 +257,23 @@ Changes on `dev` that have not been published to npm.
186
257
 
187
258
  ### Changed
188
259
 
189
- - **Breaking: everyone who signs in is a user, and nothing says customer or
190
- admin as a kind.** It needs the platform release that serves the same
260
+ - **Breaking: everyone who signs in is a user, and no name says what kind
261
+ of person somebody is.** It needs the platform release that serves the same
191
262
  names; an earlier platform refuses every call below by name.
192
263
  - `@foir/sdk/auth`: the raw calls take the server's op names: `login`,
193
264
  `register`, `requestOTP`, `loginWithOTP`, `refreshTokens` (the
194
265
  `refreshToken` op), `logout`, `joinWaitlist`, `signupContext`,
195
266
  `requestPasswordReset`, `resetPassword`, `updatePassword`, `verifyEmail`,
196
- `loginWithProvider`, `providerCallback`, `providerVerifyOTP` (were
197
- `customerLogin` and the rest), and `registerOrganization` founds an
198
- organization over the `registerOrganization` op (was
199
- `registerOperatorOrganization`). The helpers that save a session are
267
+ `loginWithProvider`, `providerCallback`, `providerVerifyOTP`, each
268
+ renamed from a name that said what kind of person called it, and
269
+ `registerOrganization` founds an organization over the
270
+ `registerOrganization` op. The helpers that save a session are
200
271
  `signInWithPassword`, `signUpWithPassword` and `signInWithOTP` (were
201
272
  `loginWithPassword`, `registerWithPassword`, `loginWithOTP`), beside
202
- `signOut`. `getSessionCustomer` is `getSessionUser`, `customerAccess` is
203
- `userAccess`, `CustomerAuthResult` is `AuthResult` and carries `userId`,
204
- `FoirCustomer` is `FoirUser` (`{ id, email }`), and `FoirAccess.customerId`
205
- is `userId`. A session is stored under `foir:userId`, so one saved by an
273
+ `signOut`. The session, access and result helpers are `getSessionUser`,
274
+ `userAccess` and `AuthResult` (which carries `userId`), the signed-in
275
+ person is `FoirUser` (`{ id, email }`), and `FoirAccess` names them as
276
+ `userId`. A session is stored under `foir:userId`, so one saved by an
206
277
  earlier version reads as signed out once.
207
278
  - `@foir/sdk/auth`: a provider sign-in that names an address somebody
208
279
  already holds waits for the code sent to it: `providerSignInRefusalOf`
@@ -211,23 +282,23 @@ Changes on `dev` that have not been published to npm.
211
282
  - `@foir/sdk/auth`: the hosted session answers `userId`
212
283
  (`HostedSession.userId`, `FoirSessionUser.userId`), from the platform's
213
284
  `user_id`, and its cookie is `foir_session`.
214
- - `@foir/sdk/react`: `useFoirSession()` answers `user` (was `customer`).
215
- - `@foir/sdk/app`: `records.user` and `users` (were `records.customer` and
216
- `customers`), over the platform's `saveUserRecord`, `userRecordByIdentity`,
285
+ - `@foir/sdk/react`: `useFoirSession()` answers `user`.
286
+ - `@foir/sdk/app`: `records.user` and `users`, over the platform's
287
+ `saveUserRecord`, `userRecordByIdentity`,
217
288
  `eraseUserRecord`, `createUser`, `user`, `users`, `userByIdentity` and
218
289
  `userIdentitiesById`. `users.list()` answers `{ users }`. `AppUser`,
219
290
  `AppUserList`, `AppUserSaveResult` (`userCreated`), `AppUserEraseResult`
220
291
  (`userDeleted`, `userKeptBecause`), `AppUserEmailUnavailableReason`,
221
- `appUserUnlinkedOf` and `appUserEmailUnavailableOf` replace the
222
- `AppCustomer` names. A notification, a consent change and an
292
+ `appUserUnlinkedOf` and `appUserEmailUnavailableOf` are the app client's
293
+ names for the people it reads. A notification, a consent change and an
223
294
  organization's first member name `userId`.
224
295
  - `@foir/sdk/server`: an operation's context names who started the run as
225
- `actorId` and the user it acts for as `userId` (was `userId` and
226
- `customerId`). `ScopedTokenIngress.userId` was `customerId`.
296
+ `actorId` and the user it acts for as `userId` (`userId` used to name
297
+ who started it). `ScopedTokenIngress` names the user as `userId`.
227
298
  - `@foir/sdk/data`: `FoirGranteeKind` is `'USER'`, a note's subject may be
228
- `'user'`, `FoirUserNote` and `FoirUserNoteAttachment` replace the
229
- `FoirCustomerNote` names, and `FoirAuthDowngrade.userToken` was
230
- `customerToken`.
299
+ `'user'`, a note of a user's is `FoirUserNote` with
300
+ `FoirUserNoteAttachment`, and `FoirAuthDowngrade` names the dropped
301
+ token `userToken`.
231
302
 
232
303
  ### Removed
233
304
 
@@ -235,8 +306,9 @@ Changes on `dev` that have not been published to npm.
235
306
  (`@foir/sdk/auth`). Nothing stores where a user stands: a request names the
236
307
  organization it acts in (`orgId` or `orgUrl`), and the platform checks they
237
308
  stand there on that request. `myOrganizations` still lists where they may.
238
- - **Breaking: `requireAdmin` and `RequireAdminOptions`, and `isAdmin` on
239
- `FoirUser`** (`@foir/sdk/auth`). What a user may do is a role or a scope:
309
+ - **Breaking: the helpers that guarded on a person being of the managing
310
+ kind, and the flag on `FoirUser` that said so** (`@foir/sdk/auth`). What a
311
+ user may do is a role or a scope:
240
312
  guard on it with `requireRole` or `requireScope`. `requireAuth` takes the
241
313
  type `resolveUser` answers, for a project that reads its own profile.
242
314
 
@@ -246,7 +318,7 @@ Changes on `dev` that have not been published to npm.
246
318
 
247
319
  - **An operation provisions an organization** (`@foir/sdk/app`).
248
320
  `organizations.provision({ provisionId, name, parentId?, settings?, firstMember? })`
249
- makes an organization with a customer as its first member, in the role the
321
+ makes an organization with a user as its first member, in the role the
250
322
  project names for a founder, and answers `{ organizationId }`. It needs
251
323
  `organizations:create`. With `settings: { modelKey, data }` it writes the
252
324
  organization's settings record too and answers `settingsRecordId`; that
@@ -261,7 +333,7 @@ Changes on `dev` that have not been published to npm.
261
333
  needs the platform release that carries `scheduleOrganizationDeletion`.
262
334
  - **`FoirWriteNoteArgs`** (`@foir/sdk/data`): the arguments of
263
335
  `foir.ns.notes.writeNote`, the one field that writes a note for a signed-in
264
- customer and for a visitor. It asks `notes:create` and needs the platform
336
+ user and for a visitor. It asks `notes:create` and needs the platform
265
337
  release that carries `writeNote`. `foir.ns.notes` answers any field the
266
338
  platform serves, so the call itself needs no new SDK.
267
339
 
@@ -288,7 +360,7 @@ Changes on `dev` that have not been published to npm.
288
360
  itself uses `createServerClient` from `@foir/sdk/server`. A key minted
289
361
  before a key had a kind (`sk_`) is not refused here.
290
362
  - **A key's kind is read from its prefix in one place.** A key is publishable
291
- (`foir_pub_`), server (`foir_srv_`) or admin (`foir_adm_`), and a key minted
363
+ (`foir_pub_`), server (`foir_srv_`) or the console (`foir_adm_`), and a key minted
292
364
  earlier is `pk_`, `sk_` or `tc_`. Only a publishable key, in either format,
293
365
  is named in a URL and takes the shared cache lane. Earlier versions looked
294
366
  for the letters `pk_` alone, so a `foir_pub_` key was sent to the origin and
@@ -352,7 +424,7 @@ to this version then.
352
424
  reads it as what those were worth, exactly as the platform does.
353
425
 
354
426
  A record grant reaches the operation's own records: the project's, or those
355
- of the customer a run acts for. `records:read.all:<model>`, and the write,
427
+ of the user a run acts for. `records:read.all:<model>`, and the write,
356
428
  delete and publish forms like it, reach every owner's records of the model.
357
429
  An older token's write of a model was every owner's, so it is read as
358
430
  `records:write.all:<model>` and `records:create:<model>`, and takes that
@@ -381,19 +453,19 @@ to this version then.
381
453
  platform holds a running install's setting to a category the project has.
382
454
 
383
455
  - **An app can say what connecting changes** (`@foir/sdk/app/contract`).
384
- `Credentials.connectNotice` is the app's own words, shown to the operator
456
+ `Credentials.connectNotice` is the app's own words, shown to the user
385
457
  beside the step that connects their account and, for an install the vendor
386
458
  started, before the install is confirmed. A vendor that keeps one live
387
459
  connection per account (Shopify, per store) says here that connecting moves
388
460
  it from any other project. Empty when there is nothing to say.
389
461
 
390
- - **The roles a signed-in customer holds, and what they may do**
391
- (`@foir/sdk/auth`, `@foir/sdk/react`). A storefront can now ask which customer
392
- roles the signed-in customer holds and show the page to match, where before
462
+ - **The roles a signed-in user holds, and what they may do**
463
+ (`@foir/sdk/auth`, `@foir/sdk/react`). A storefront can now ask which user
464
+ roles the signed-in user holds and show the page to match, where before
393
465
  it had to treat one of a role's permissions as a stand-in for the role.
394
466
 
395
- - `getSessionAccess(adapter, env)` and `customerAccess(env, { bearerToken })`
396
- return a `FoirAccess`: the customer's id, the keys of the roles they hold
467
+ - `getSessionAccess(adapter, env)` and `userAccess(env, { bearerToken })`
468
+ return a `FoirAccess`: the user's id, the keys of the roles they hold
397
469
  (`roles`), and everything they may do through the key (`scopes`). Plain
398
470
  data, so a loader can return it. `null` when nobody is signed in.
399
471
  - `hasRole(access, key)` and `hasScope(access, scope)` read it. `hasScope`
@@ -412,7 +484,7 @@ to this version then.
412
484
  and a role taken away is gone the next time it is asked.
413
485
 
414
486
  `roles` is `null`, not an empty list, when the platform did not say which
415
- roles the customer holds. `hasRole` answers false for it and `requireRole`
487
+ roles the user holds. `hasRole` answers false for it and `requireRole`
416
488
  refuses with `roles_unknown`, so an outage is never read as "not staff". A
417
489
  platform that does not serve the field yet reads the same way: the role
418
490
  helpers need the platform release that carries it.
@@ -424,7 +496,7 @@ to this version then.
424
496
  `accessStatus`, `loadAccess` and `ensureAccess`.
425
497
 
426
498
  - **A save says when it made a record the operation may not read**
427
- (`@foir/sdk/app`). `records.upsert` and `records.customer.save` answer
499
+ (`@foir/sdk/app`). `records.upsert` and `records.user.save` answer
428
500
  `fieldsWithheld`. It is true when the operation declared the create of a
429
501
  model and not the read: the save made the record, and `record` carries its
430
502
  `id` and `modelKey` and nothing else. The record is not empty.
@@ -440,13 +512,13 @@ to this version then.
440
512
  app's operations are given the one they could use, so an installed app keeps
441
513
  running; a manifest served from then on says it.
442
514
 
443
- - **Breaking, with the platform release that carries it: erasing a customer
444
- takes `identities:delete`** (`@foir/sdk/app`). `records.customer.erase` needed
515
+ - **Breaking, with the platform release that carries it: erasing a user
516
+ takes `identities:delete`** (`@foir/sdk/app`). `records.user.erase` needed
445
517
  `users:create` beside the delete and the read of the model, and now needs
446
518
  `identities:delete` there. `APP_GRANTS` carries it, with `files:delete`,
447
519
  `notifications:read` and every owner's files (`files:read.all`,
448
520
  `files:write.all`, `files:delete.all`), which a manifest may now declare.
449
- `verifyScopedToken` reads a token minted before, that carried the customer
521
+ `verifyScopedToken` reads a token minted before, that carried the user
450
522
  create beside the delete of a model, as holding `identities:delete`, as the
451
523
  platform does.
452
524
 
@@ -464,17 +536,14 @@ to this version then.
464
536
  | `config:read` | `appconfig:read` |
465
537
  | `secrets:read.app`, `secrets:put.app`, `secrets:list.app`, `secrets:delete.app` | `appsecrets:read`, `appsecrets:put`, `appsecrets:list`, `appsecrets:delete` |
466
538
  | `files:list` | `files:read` |
467
- | `customers:write` | `users:create` |
468
- | `customers:read` | `users:read.all` |
469
- | `customers:read:named` | `users:read.named` |
470
- | `customers:read:identity` | `identities:read` |
539
+ | The names for writing and reading a project's users | `users:create`, `users:read.all`, `users:read.named`, `identities:read` |
471
540
  | `notifications:write` | `notifications:write.named` |
472
541
  | `records:write:<model>` | `records:write:<model>` and `records:create:<model>` |
473
542
 
474
543
  The last row is a change in what the name means. `records:write:<model>`
475
544
  changes a record that is there, and making one is `records:create:<model>`.
476
545
  An operation that does both declares both. `records.upsert` and
477
- `records.customer.save` are let through on either, and the platform asks for
546
+ `records.user.save` are let through on either, and the platform asks for
478
547
  the one the save turns out to need.
479
548
 
480
549
  - **Breaking, with the platform release that carries it: a full-page placement
@@ -487,7 +556,7 @@ to this version then.
487
556
  things change in what a placement writes:
488
557
 
489
558
  - **Every owner's records are written `.all`.** `records:read:order` was
490
- every order and is now the operator's own. Declare
559
+ every order and is now the user's own. Declare
491
560
  `records:read.all:order`.
492
561
  - **A write no longer includes its read.** `records:write:order` also read
493
562
  and created, and `apps:write` also read apps. Declare each one the page
@@ -496,8 +565,8 @@ to this version then.
496
565
  - **A wildcard and a permission that gives others are refused**, as `*`
497
566
  always was.
498
567
 
499
- The token then carries what the placement declares and the operator holds,
500
- permission by permission. An operator who holds part of a declared grant is
568
+ The token then carries what the placement declares and the user holds,
569
+ permission by permission. A user who holds part of a declared grant is
501
570
  given that part, where before the whole name was withheld.
502
571
 
503
572
  - **`session.can` answers by what the token covers** (`@foir/sdk/placement`).
@@ -538,11 +607,11 @@ to this version then.
538
607
  `records:read` for the model beside their own permission.
539
608
  - An operation that declares the create of a model and not the read
540
609
  (`@foir/sdk/app`) still makes its records. `records.upsert` landing on a
541
- record already there, `records.customer.save` likewise, `records.delete`,
610
+ record already there, `records.user.save` likewise, `records.delete`,
542
611
  `records.refresh`, `records.rename` and `records.updateMany` are refused
543
612
  until it declares the read too.
544
613
 
545
- - **Breaking, with the platform release that carries it: `records.customer.erase`
614
+ - **Breaking, with the platform release that carries it: `records.user.erase`
546
615
  needs the delete of the model** (`@foir/sdk/app`). An erase removes a record,
547
616
  so the operation declares `records:delete` and `records:read` for the model,
548
617
  and `users:create`. Until now the grant to save the record was enough. An
@@ -554,14 +623,14 @@ to this version then.
554
623
  `MyOrganizationRole.permissions` is gone and `MyOrganizationRole.grants`
555
624
  takes its place: what the role gives, in the permission catalogue's names. A
556
625
  grant stands for itself, so changing a record and creating one are two
557
- names (`records:write`, `records:create`), reading every customer's records
626
+ names (`records:write`, `records:create`), reading every user's records
558
627
  is `records:read.all`, and staffing an organization is `members:read` and
559
628
  `members:write`. Code that looked for `organizations:staff` or
560
629
  `records:list.all` in a role's list looks for the new names.
561
630
 
562
- - **Breaking, with the platform release that carries it: `customers.byIdentity`
563
- and `customers.identities` need `identities:read`, declared by name**
564
- (`@foir/sdk/app`). The read of every customer, `users:read.all`, no longer
631
+ - **Breaking, with the platform release that carries it: `users.byIdentity`
632
+ and `users.identities` need `identities:read`, declared by name**
633
+ (`@foir/sdk/app`). The read of every user, `users:read.all`, no longer
565
634
  includes them.
566
635
 
567
636
  ### Removed
@@ -632,29 +701,29 @@ the next promotion; `latest` moves to this version then.
632
701
  (`@foir/sdk/data`). Code that reads a drift is unaffected. Code that builds
633
702
  one by hand, for example to test a handler given to `onSchemaDrift` or made by
634
703
  `createFoirDriftHandler`, must add `incompatible: []`.
635
- - **Breaking (types): `AppConsentChange` names its customer one way**
636
- (`@foir/sdk/app`). It was an interface with a required `customerId`. It is
637
- now a type that holds a `customerId` or an `email`, never both. A change
638
- written as `{ customerId, ... }` is unaffected. An interface that extended
704
+ - **Breaking (types): `AppConsentChange` names its user one way**
705
+ (`@foir/sdk/app`). It was an interface with a required `userId`. It is
706
+ now a type that holds a `userId` or an `email`, never both. A change
707
+ written as `{ userId, ... }` is unaffected. An interface that extended
639
708
  `AppConsentChange` no longer compiles: intersect with it instead.
640
709
 
641
710
  ### Added
642
711
 
643
- - `notifications.setConsent` takes `email` in place of `customerId`
644
- (`@foir/sdk/app`), for an opt-in from somebody who may have no customer yet,
645
- such as a sign-up form. The platform finds the customer holding the address
712
+ - `notifications.setConsent` takes `email` in place of `userId`
713
+ (`@foir/sdk/app`), for an opt-in from somebody who may have no user yet,
714
+ such as a sign-up form. The platform finds the user holding the address
646
715
  at the install's level, or makes one, together with the change, so a change
647
716
  it refuses (a category the project does not have, a confirmation email the
648
- project cannot send) leaves no customer behind. Finding or making the
649
- customer first with `customers.ensure` did. The operation must declare
650
- `customers:write` as well as `notifications:consent`. `AppConsentSubject` is
717
+ project cannot send) leaves no user behind. Finding or making the
718
+ user first with `users.ensure` did. The operation must declare
719
+ `users:create` as well as `notifications:consent`. `AppConsentSubject` is
651
720
  exported.
652
721
 
653
722
  - `FoirSchemaDrift.incompatible`: what the generated module describes that the
654
723
  server no longer serves that way, as sorted paths into the manifest
655
724
  (`models.order.selection.status`, `models.order.ops.update`,
656
725
  `namespaces.search.ops.searchRecords.args.query`,
657
- `models.order.relations.customer`, `selectionUnions.Block.HeroBlock.title`).
726
+ `models.order.relations.buyer`, `selectionUnions.Block.HeroBlock.title`).
658
727
  Empty when the surface has only grown. `changed` keeps its meaning: what
659
728
  moved, not what broke.
660
729
 
@@ -665,26 +734,26 @@ platform serves from the next promotion; `latest` moves to it then.
665
734
 
666
735
  ### Changed
667
736
 
668
- - **Breaking: an app finds a customer by identity through its install, not
669
- through a model** (`@foir/sdk/app`). The customer sign-in an app's customers
737
+ - **Breaking: an app finds a user by identity through its install, not
738
+ through a model** (`@foir/sdk/app`). The user sign-in an app's users
670
739
  come through is paired with the install as a whole, so the two reads that
671
- walk it no longer name a model and have moved beside the other customer
740
+ walk it no longer name a model and have moved beside the other user
672
741
  reads:
673
- - `records.customer.identities(model, customerId)` is now
674
- `customers.identities(customerId)`.
675
- - `customers.byIdentity(identity)` is new: the id of the customer who holds
742
+ - `records.user.identities(model, userId)` is now
743
+ `users.identities(userId)`.
744
+ - `users.byIdentity(identity)` is new: the id of the user who holds
676
745
  an identity at the app's own system, or `null`. It replaces reading a
677
- customer's record for its owner.
746
+ user's record for its owner.
678
747
 
679
- Both need the new capability `customers:read:identity` (or `customers:read`),
748
+ Both need the new capability `identities:read` (or `users:read.all`),
680
749
  declared by the operation, and are refused at an install that pairs no
681
750
  sign-in. Reading or writing a model is no longer what admits them.
682
- - `install.config()` asks the platform for `customerSignIn`, a field a platform
751
+ - `install.config()` asks the platform for `userSignIn`, a field a platform
683
752
  older than this release does not have.
684
753
 
685
754
  ### Added
686
755
 
687
- - `install.config()` answers `customerSignIn`: the key of the customer sign-in
756
+ - `install.config()` answers `userSignIn`: the key of the user sign-in
688
757
  the install pairs, or `null` when it pairs none.
689
758
 
690
759
  ## [0.17.0] — 2026-10-02
@@ -694,19 +763,19 @@ platform serves from the next promotion; `latest` moves to it then.
694
763
 
695
764
  ### Added
696
765
 
697
- - **An app is told when a project cannot email its customers**
766
+ - **An app is told when a project cannot email its users**
698
767
  (`@foir/sdk/app`). A project with no App base URL used to accept a
699
768
  notification's email and drop it. The platform now refuses a notification
700
769
  that carries an email, and an opt-in to a category confirmed by email, with
701
- the reason as data. `appCustomerEmailUnavailableOf(error)` reads it:
770
+ the reason as data. `appUserEmailUnavailableOf(error)` reads it:
702
771
  `{ reason: 'NO_APP_BASE_URL' }`, or `null` for any other failure.
703
- - `install.config()` answers `customerEmailUnavailable`: the same reason ahead
704
- of time, or `null` when the project can send. `AppCustomerEmailUnavailableReason`
772
+ - `install.config()` answers `userEmailUnavailable`: the same reason ahead
773
+ of time, or `null` when the project can send. `AppUserEmailUnavailableReason`
705
774
  is the type.
706
775
 
707
776
  ### Changed
708
777
 
709
- - `install.config()` asks the platform for `customerEmailUnavailable`, a field
778
+ - `install.config()` asks the platform for `userEmailUnavailable`, a field
710
779
  a platform older than this release does not have.
711
780
 
712
781
  ## [0.16.0] — 2026-10-02
@@ -717,11 +786,11 @@ it at the next promotion.
717
786
  ### Added
718
787
 
719
788
  - **An embedded page is told how the console looks** (`@foir/sdk/placement`).
720
- The console's theme is the operator's own setting, which a framed page cannot
789
+ The console's theme is the user's own setting, which a framed page cannot
721
790
  see, so a page that followed the system's preference could sit dark inside a
722
791
  light console. `connectPlacement` takes `onAppearance`, called with
723
792
  `{ theme: 'light' | 'dark' }` as soon as the console says (before the token
724
- arrives) and again whenever the operator changes it; `session.appearance`
793
+ arrives) and again whenever the user changes it; `session.appearance`
725
794
  holds the current one. `PlacementAppearance` is the type, and
726
795
  `PLACEMENT_MESSAGE.appearance` the message.
727
796
  - A console older than this says nothing: `onAppearance` is not called and
@@ -739,7 +808,7 @@ then.
739
808
  - **`notifications.setConsent` answers `confirmationPending`**, and takes
740
809
  `givenElsewhere` and `locale` (`@foir/sdk/app`). A project can ask that an
741
810
  opt-in an app collects in a category is confirmed by email before it counts.
742
- In such a category an opt-in writes nothing: the platform sends the customer
811
+ In such a category an opt-in writes nothing: the platform sends the user
743
812
  one confirmation email, in the page's `locale` when they have stated no
744
813
  language of their own, and the answer says `confirmationPending`. The
745
814
  preference turns on when they follow the link. Everywhere else an opt-in is
@@ -760,17 +829,17 @@ then.
760
829
 
761
830
  ### Added
762
831
 
763
- - **`customers.get`, `customers.byEmail` and `customers.list`** on the app
764
- client (`@foir/sdk/app`): an operation reads the customers its work is about.
765
- `get(id)` and `byEmail(email)` answer one customer or null, and need
766
- `customers:read:named`: the customer the operation names, by id or by their
832
+ - **`users.get`, `users.byEmail` and `users.list`** on the app
833
+ client (`@foir/sdk/app`): an operation reads the users its work is about.
834
+ `get(id)` and `byEmail(email)` answer one user or null, and need
835
+ `users:read.named`: the user the operation names, by id or by their
767
836
  exact email address at the install's level, and nothing wider. `list` pages
768
- through the customers at the install's level and needs `customers:read`,
837
+ through the users at the install's level and needs `users:read.all`,
769
838
  which covers the two lookups as well. An operation declaring only the named
770
- read is refused the list. `AppCustomer` and `AppCustomerList` are their types.
771
- - **`customers.ensure` answers `verified`**: whether the customer it found or
772
- made has proved the address is theirs. It finds the customer holding the
773
- email either way, so one shopper asking twice is one customer; a caller with
839
+ read is refused the list. `AppUser` and `AppUserList` are their types.
840
+ - **`users.ensure` answers `verified`**: whether the user it found or
841
+ made has proved the address is theirs. It finds the user holding the
842
+ email either way, so one shopper asking twice is one user; a caller with
774
843
  something that should wait for proof asks the flag first.
775
844
 
776
845
  ## [0.13.0] — 2026-10-01
@@ -797,7 +866,7 @@ then.
797
866
  order, at most `first` of them, with `hasMore` saying the filter matched more.
798
867
  For work an app hands out in turn, such as the next ten people waiting: list
799
868
  them, then update the ones listed by id. A model's list is named by its
800
- plural, which its operator may set, so the first list a client makes reads
869
+ plural, which its user may set, so the first list a client makes reads
801
870
  the data API's surface descriptor (`GET /v1/manifest`) as the same token,
802
871
  once. `AppRecordList` is its query's type.
803
872
 
@@ -822,12 +891,12 @@ production serves from the next promotion; `latest` moves to it then.
822
891
  `whoami`, `install.config`, `install.credentials`,
823
892
  `install.writeCredentials`, `install.reportStatus`, `records.upsert`,
824
893
  `read`, `delete`, `refresh`, `rename`, `updateMany` and `aggregate`,
825
- `records.customer.save`, `read`, `erase` and `identities`,
826
- `customers.ensure`, `notifications.send` and `notifications.setConsent`, and
894
+ `records.user.save`, `read`, `erase` and `identities`,
895
+ `users.ensure`, `notifications.send` and `notifications.setConsent`, and
827
896
  `request` for a document of your own. Nothing is checked before a call: what
828
897
  the token may do is the platform's to hold it to, and a refusal is a
829
898
  `FoirRequestError` carrying the platform's answer.
830
- - `appLookupConflictOf`, `appCustomerUnlinkedOf` and `isAppFieldWithheld` read
899
+ - `appLookupConflictOf`, `appUserUnlinkedOf` and `isAppFieldWithheld` read
831
900
  the refusals an app acts on, as data.
832
901
  - `readAppDispatch` and `readCredentialProbe` read the two bodies the platform
833
902
  posts to an app, and `redeemExchangeTicket` trades an OAuth exchange ticket
@@ -855,7 +924,7 @@ production serves from the next promotion; `latest` moves to it then.
855
924
  what it throws when it has no keys to judge a token by.
856
925
  - **`requireScopedToken` answers more about who is calling.** The ingress it
857
926
  resolves with gains `orgId` (the organisation level the token was minted at,
858
- `null` when it states none), `customerId`, `expiresAt` and `tokenId`.
927
+ `null` when it states none), `userId`, `expiresAt` and `tokenId`.
859
928
  `ScopedTokenError` gains `reason` (`missing`, `invalid`, `forbidden` or
860
929
  `unavailable`), and its `status` may now be 503: keys that cannot be fetched
861
930
  are this service's outage, which the platform retries, and not a verdict on
@@ -867,7 +936,7 @@ production serves from the next promotion; `latest` moves to it then.
867
936
  passed. It takes `apiUrl`, used when a dispatch names no address to report
868
937
  to, `retry`, and `signal`. A call the platform did not answer, or answered
869
938
  with a 5xx or a 429, is tried again, with the same signed bytes.
870
- - **`@foir/sdk/placement`**, a new subpath for an app's own admin console shown
939
+ - **`@foir/sdk/placement`**, a new subpath for an app's own console shown
871
940
  inside Foir's as a full-page placement. `connectPlacement` tells the console
872
941
  the page is ready, takes the token it posts into the frame (only from the
873
942
  consoles you list), keeps it fresh, and gives you `request`, `can` and
@@ -926,8 +995,8 @@ production serves from the next promotion; `latest` moves to it then.
926
995
  once and asks again, failing with `FoirSessionExpiredError` when the session
927
996
  is over.
928
997
  - A request that sent no bearer is untouched.
929
- - **A refresh that names no customer keeps the session's identity.**
930
- `saveSession` used to write the customer id and email unconditionally, so a
998
+ - **A refresh that names no user keeps the session's identity.**
999
+ `saveSession` used to write the user id and email unconditionally, so a
931
1000
  refresh answered without them left a valid token beside an empty identity, and
932
1001
  `requireAuth` turned the session away. Given `env`, `requireAuth` now reads a
933
1002
  missing identity once from `currentUser` rather than failing.
@@ -985,10 +1054,10 @@ surface version instead of the schema counter.
985
1054
  credential.
986
1055
 
987
1056
  Two things a dispatch client does not do. It does not subscribe: the
988
- subscription socket authenticates a key, an admin token or a customer bearer
1057
+ subscription socket authenticates a key, a console user token or a user bearer
989
1058
  and has no path for a dispatch token, so `onChange` and friends throw, naming
990
1059
  why. And it takes no key-side option (`apiKey`, `bearerToken`, `orgId`,
991
- `channel`, the socket options), because the token carries its own customer,
1060
+ `channel`, the socket options), because the token carries its own user,
992
1061
  organization and channel; each is a compile error and a runtime refusal.
993
1062
 
994
1063
  - `readDispatch(request)` and `OperationPayloadError` on `@foir/sdk/server`.
@@ -1015,7 +1084,7 @@ surface version instead of the schema counter.
1015
1084
 
1016
1085
  - `FoirRecordOwner` on `@foir/sdk/data`: `{ id, ownerId, ownerKind }`, what
1017
1086
  `setOwner` answers with. An operation that declares `records:attribute:<model>`
1018
- hands a record to another customer with `foir.<model>.setOwner({ id, ownerId })`,
1087
+ hands a record to another user with `foir.<model>.setOwner({ id, ownerId })`,
1019
1088
  and names the owner of a record it creates with `create({ input, ownerId })`.
1020
1089
  The answer carries the owner and not the record, because an operation acting
1021
1090
  for the previous holder may no longer read it.
@@ -1259,14 +1328,14 @@ returns a result object rather than `HostedSession | null`, and
1259
1328
  silent wrong answer for a thrown one that discards the answer. Nothing changes
1260
1329
  for a caller that does not pass it.
1261
1330
 
1262
- - `registerOrganization(env, { key, name }, { bearerToken })` on `@foir/sdk/auth`: a customer founds an organization of their own and becomes its first member, holding the role the project's `organizationSignupRole` setting names. Returns the same `MyOrganization` entry `myOrganizations` lists. Present on the wire only while the project's `organizationSignupEnabled` setting is on.
1263
- - **The operator's organization context, on `@foir/sdk/auth`** — `myOrganizations`
1331
+ - `registerOrganization(env, { key, name }, { bearerToken })` on `@foir/sdk/auth`: a user founds an organization of their own and becomes its first member, holding the role the project's `organizationSignupRole` setting names. Returns the same `MyOrganization` entry `myOrganizations` lists. Present on the wire only while the project's `organizationSignupEnabled` setting is on.
1332
+ - **The user's organization context, on `@foir/sdk/auth`** — `myOrganizations`
1264
1333
  and `selectOrganization`, plus the types `MyOrganization` and
1265
1334
  `SelectedOrganization`, and two entries on `AUTH_OPS`.
1266
1335
 
1267
1336
  A Fóir project can be partitioned into organizations, one per tenant of a SaaS
1268
- built on Fóir. An *operator* is a customer who runs one or more of them.
1269
- `myOrganizations()` answers which ones this customer may stand in — from their
1337
+ built on Fóir. An *user* is a user who runs one or more of them.
1338
+ `myOrganizations()` answers which ones this user may stand in — from their
1270
1339
  own membership rows, so it is not the project's inventory and cannot be used as
1271
1340
  one — and `selectOrganization({ orgId })` says which. An empty `orgId` clears
1272
1341
  the selection.
@@ -1276,7 +1345,7 @@ returns a result object rather than `HostedSession | null`, and
1276
1345
  caller's membership on every request. An application therefore cannot forget
1277
1346
  to send it, cannot send another organization's, and cannot be made to cross
1278
1347
  the boundary by a query it built wrong. These two functions are the entire
1279
- per-customer surface; everything downstream names no organization at all.
1348
+ per-user surface; everything downstream names no organization at all.
1280
1349
  (The project credential's side of the same boundary is the client-level
1281
1350
  `orgId` option below — a property of the client, still never of a call.)
1282
1351
 
@@ -1390,7 +1459,7 @@ returns a result object rather than `HostedSession | null`, and
1390
1459
  Schema drift is a different problem with the same remedy. A deploy that
1391
1460
  renames a field leaves every cached entry perfectly current and quietly wrong:
1392
1461
  no record changed, nothing is out of order, and the bytes describe a schema
1393
- that no longer exists — until the tab is reloaded, which on a long admin
1462
+ that no longer exists — until the tab is reloaded, which on a long console
1394
1463
  session can be days. `createFoirDriftHandler` wires the `FoirSchemaDrift` the
1395
1464
  data client already reports to a full clear.
1396
1465
 
@@ -1511,7 +1580,7 @@ returns a result object rather than `HostedSession | null`, and
1511
1580
  credential and the bearer is re-exchanged when it dies. A consumer that cannot
1512
1581
  see the lifetime has to hardcode the platform's (coupling itself to a server
1513
1582
  constant it cannot observe) or decode `exp` off the JWT — or, as happened,
1514
- treat a 15-minute bearer as good forever and have customers appear signed out
1583
+ treat a 15-minute bearer as good forever and have users appear signed out
1515
1584
  mid-session. `createFoirSessionClient` on the SPA path has read `expires_in`
1516
1585
  and scheduled a proactive refresh all along; this closes the asymmetry.
1517
1586
 
@@ -1521,10 +1590,10 @@ returns a result object rather than `HostedSession | null`, and
1521
1590
  - **`HostedSessionResult`** exported from `./auth`.
1522
1591
 
1523
1592
  - **`FoirAuthDowngrade`** on `./data` and `./server`, plus `extensions` on the
1524
- response envelope — Foir now reports when it DISCARDED a customer token you
1593
+ response envelope — Foir now reports when it DISCARDED a user token you
1525
1594
  sent, and the SDK stops dropping that report on the floor.
1526
1595
 
1527
- A customer JWT is optional: one that fails to verify is not refused, the read
1596
+ A user JWT is optional: one that fails to verify is not refused, the read
1528
1597
  is served anonymously and returns `200`. So a lapsed token and a genuinely
1529
1598
  anonymous request were indistinguishable — nothing threw, no refresh armed,
1530
1599
  and auth-targeted content quietly resolved to its anonymous variant. The
@@ -1533,7 +1602,7 @@ returns a result object rather than `HostedSession | null`, and
1533
1602
  ```json
1534
1603
  { "data": { "…": "…" },
1535
1604
  "extensions": { "auth": {
1536
- "customerToken": "rejected", "reason": "expired", "servedAs": "anonymous"
1605
+ "userToken": "rejected", "reason": "expired", "servedAs": "anonymous"
1537
1606
  } } }
1538
1607
  ```
1539
1608
 
@@ -1553,7 +1622,7 @@ returns a result object rather than `HostedSession | null`, and
1553
1622
 
1554
1623
  The GraphQL `events` subscription has been live on the public schema since the
1555
1624
  events spine shipped, with no client able to reach it. It is an `sk_`-only
1556
- operator surface, so it lives on `./server` beside the rest of the events
1625
+ console surface, so it lives on `./server` beside the rest of the events
1557
1626
  client and inherits its `pk_` and `Authorization` refusals unchanged.
1558
1627
 
1559
1628
  New exports: `FoirEventsSubscribeParams`, plus `FoirSubscription` and
@@ -1575,7 +1644,7 @@ returns a result object rather than `HostedSession | null`, and
1575
1644
  It collapsed every failure to `null`: no cookie, a `401`, a `502`, a rate
1576
1645
  limit, a DNS failure. Consumers have to act on that `null`, and the only
1577
1646
  safe-looking action is to clear the session and redirect to the front door —
1578
- so one blip on `/session/token` signed out a customer whose 30-day cookie was
1647
+ so one blip on `/session/token` signed out a user whose 30-day cookie was
1579
1648
  perfectly valid, and nothing recorded that it had happened. The route's own
1580
1649
  rate limiter is deliberately fail-open for the opposite reason.
1581
1650
 
@@ -1594,7 +1663,7 @@ returns a result object rather than `HostedSession | null`, and
1594
1663
  `outcome` says what to DO; `reason` says what happened. Only `401` and a
1595
1664
  missing cookie are `signed-out`. A `403` reports as `unavailable` with reason
1596
1665
  `forbidden` — it means the CSRF header did not arrive, which is a
1597
- misconfiguration rather than a blip, but signing the customer out over it
1666
+ misconfiguration rather than a blip, but signing the user out over it
1598
1667
  would still be wrong.
1599
1668
 
1600
1669
  - **`foir.ns.operations.onExecution` delivers `status` as the
@@ -1634,14 +1703,14 @@ Everything additive, so a MINOR either way under the pre-1.0 note.
1634
1703
  ### Added
1635
1704
 
1636
1705
  - **`hostedLogoutUrl(config, clientId, returnTo?)` on `./auth`** — the mirror of
1637
- `hostedLoginUrl`, and the only thing that actually signs a customer out on the
1706
+ `hostedLoginUrl`, and the only thing that actually signs a user out on the
1638
1707
  hosted-cookie flow.
1639
1708
 
1640
1709
  Login on that flow is symmetric across browser and server; logout was not.
1641
- The apex `foir_customer_session` cookie is the durable credential, and
1710
+ The apex `foir_session` cookie is the durable credential, and
1642
1711
  disposing of a bearer, clearing a local session, or calling the
1643
- `customerLogout` mutation all leave it in place — the next request
1644
- re-establishes the customer from it. The only surface that ended a session was
1712
+ `logout` mutation all leave it in place — the next request
1713
+ re-establishes the user from it. The only surface that ended a session was
1645
1714
  `POST /session/logout`, cookie-authenticated and CSRF-header-gated, which only
1646
1715
  a browser sending `credentials: 'include'` can reach.
1647
1716
 
@@ -2013,7 +2082,7 @@ note above.
2013
2082
 
2014
2083
  Reads take the shared, cacheable lane where they can:
2015
2084
  `GET /cdn/graphql?key=pk_…` with the persisted-query hash in the URL when the
2016
- call is a query, on a `pk_` key, with no customer bearer, on the published
2085
+ call is a query, on a `pk_` key, with no user bearer, on the published
2017
2086
  channel — and `POST /graphql` otherwise. The server's gate refuses on five
2018
2087
  conditions and the client decides all five before the request: the fifth,
2019
2088
  "the credential is in the URL", it satisfies by construction. The server is
@@ -2035,7 +2104,7 @@ note above.
2035
2104
  hand-written document may legitimately have several. As a document it reaches
2036
2105
  everything the credential can reach — including the two dozen root fields no
2037
2106
  model and no namespace claims (`designTokens`, `record`, `locales`, the
2038
- customer-auth mutations), which is the point of an escape hatch and is more
2107
+ user-auth mutations), which is the point of an escape hatch and is more
2039
2108
  than the old shape could address.
2040
2109
 
2041
2110
  - **Every op prints, and there is one wire format rather than two.** The
@@ -2098,19 +2167,19 @@ note above.
2098
2167
  schema knowledge the client is not allowed to hold. The manifest publishes
2099
2168
  each op's result shape, so the table has nothing left to answer. Neither
2100
2169
  symbol has appeared in a published release.
2101
- - **`customerResendVerificationEmail` from `@foir/sdk/auth`** — and the
2102
- `AUTH_OPS.customerResendVerificationEmail` entry with it. **It never sent an
2103
- email.** The resolver behind it checked that the request carried a customer id,
2104
- returned `true`, and called no RPC; there is no customer verification-code
2105
- column and no customer verification email in the action catalogue for it to
2170
+ - **`resendVerificationEmail` from `@foir/sdk/auth`** — and the
2171
+ `AUTH_OPS.resendVerificationEmail` entry with it. **It never sent an
2172
+ email.** The resolver behind it checked that the request carried a user id,
2173
+ returned `true`, and called no RPC; there is no user verification-code
2174
+ column and no user verification email in the action catalogue for it to
2106
2175
  have used. A caller got a success response every time and nothing was
2107
2176
  delivered, which is worse than an error, because there is no signal to act on.
2108
2177
 
2109
- **Use the OTP flow for mailbox proof:** `customerRequestOTP(env, email)` emails
2110
- a code, and `customerLoginOTP(env, { email, otp })` redeems it — activating a
2178
+ **Use the OTP flow for mailbox proof:** `requestOTP(env, email)` emails
2179
+ a code, and `loginWithOTP(env, { email, otp })` redeems it — activating a
2111
2180
  pending account on redemption and returning a session, so possession of the
2112
- mailbox is proven by the same call that signs the customer in. That is a live,
2113
- delivered path today; `customerVerifyEmail` remains for redeeming a token that
2181
+ mailbox is proven by the same call that signs the user in. That is a live,
2182
+ delivered path today; `verifyEmail` remains for redeeming a token that
2114
2183
  arrived by some other route.
2115
2184
 
2116
2185
  Like the two entries above, this symbol has not appeared in a published
@@ -2143,11 +2212,11 @@ note above.
2143
2212
  id are what the note is filed against and never appear on screen.
2144
2213
 
2145
2214
  Opening the panel reads `noteConfig`, which the API charges `notes:write` and
2146
- refuses to a caller with no customer session, so one probe settles whether the
2215
+ refuses to a caller with no session, so one probe settles whether the
2147
2216
  visitor is signed in, whether their role may write notes, and whether
2148
2217
  screenshot capture may be offered. A missing scope reads like a bad API key and
2149
- is not one, so it is reported as what it is: the signed-in customer's ROLE is
2150
- short `notes:write`, which a new project's default customer role does not
2218
+ is not one, so it is reported as what it is: the signed-in user's ROLE is
2219
+ short `notes:write`, which a new project's default user role does not
2151
2220
  carry.
2152
2221
 
2153
2222
  Screenshot capture is offered only when the project allows it and the browser
@@ -2177,11 +2246,11 @@ note above.
2177
2246
  New exports on `./media`: `captureFoirScreen`, `isFoirScreenCaptureSupported`,
2178
2247
  `FoirScreenCapture`, `FoirScreenCaptureOptions`.
2179
2248
 
2180
- - **Note attachments and note config on `./data`.** `FoirCustomerNote` gains
2249
+ - **Note attachments and note config on `./data`.** `FoirUserNote` gains
2181
2250
  `attachments`, `FoirCreateMyNoteArgs` gains an optional `attachmentFileIds`,
2182
2251
  and `FoirNoteConfig` is the shape `foir.ns.notes.noteConfig()` answers with.
2183
2252
 
2184
- New exports on `./data`: `FoirCustomerNoteAttachment`, `FoirNoteConfig`.
2253
+ New exports on `./data`: `FoirUserNoteAttachment`, `FoirNoteConfig`.
2185
2254
 
2186
2255
  - **A subject registry — the records a client fetched, so an annotation can
2187
2256
  anchor to the record instead of to a URL.** `createFoirSubjectRegistry()`
@@ -2225,11 +2294,11 @@ note above.
2225
2294
  `FoirSubject`, `FoirSubjectEntry`, `FoirSubjectOptions`, `FoirSubjectOrigin`,
2226
2295
  `FoirSubjectRegistry`, `FoirRecordPath`.
2227
2296
 
2228
- - **Types for the customer notes surface.** `foir.ns.notes.myNotes()` and
2297
+ - **Types for the user notes surface.** `foir.ns.notes.myNotes()` and
2229
2298
  `foir.ns.notes.createMyNote()` already worked — `notes` is a namespace the
2230
2299
  manifest publishes like any other — but their shapes had to be written out by
2231
2300
  hand at each call site. They are platform-owned and identical in every project,
2232
- so they now ship: `FoirCustomerNote`, `FoirMyNotesArgs`, `FoirMyNotesResult`,
2301
+ so they now ship: `FoirUserNote`, `FoirMyNotesArgs`, `FoirMyNotesResult`,
2233
2302
  `FoirCreateMyNoteArgs`, `FoirNoteSubjectType`.
2234
2303
 
2235
2304
  ```ts
@@ -2237,7 +2306,7 @@ note above.
2237
2306
  ```
2238
2307
 
2239
2308
  `FoirNoteSubjectType` is the closed set the platform's write path enforces
2240
- (`record`, `file`, `model`, `operation`, `schedule`, `customer`) and is never a
2309
+ (`record`, `file`, `model`, `operation`, `schedule`, `user`) and is never a
2241
2310
  model key — a note on a record is `'record'` plus the key on
2242
2311
  `subjectModelKey`. Like every mirrored platform table in `builtins.ts`, a kind
2243
2312
  added upstream widens this union in a minor release.
@@ -2394,12 +2463,12 @@ note above.
2394
2463
  `FoirEventsClientConfig`, `FoirEventsListParams`, `FoirEventsStreamParams`.
2395
2464
 
2396
2465
  **On `@foir/sdk/server` and no other subpath, deliberately.** The event log is
2397
- an OPERATOR surface (DECIDED 2026-08-04): reachable by an `sk_` key or
2398
- an admin session, never by a customer token, and `events:list.project` is not
2466
+ an USER surface (DECIDED 2026-08-04): reachable by an `sk_` key or
2467
+ a console session, never by a user token, and `events:list.project` is not
2399
2468
  mintable on a `pk_` key at all. `events.view` is plain tenant+project
2400
2469
  containment while `record.view` carries owner/grantee/borrow terms on top, so a
2401
2470
  containment-only read over the log describes rows whose records the same caller
2402
- could not read — and each row's `actor` names which *other* customer acted.
2471
+ could not read — and each row's `actor` names which *other* user acted.
2403
2472
  `createEventsClient` therefore throws on a `pk_` key and on an `Authorization`
2404
2473
  header rather than letting either reach the wire, and an import-graph test
2405
2474
  fails if the module becomes reachable from a subpath that ships to a browser.
@@ -2420,12 +2489,12 @@ note above.
2420
2489
  is the entire cost of GraphQL paid for one screen. Ten root fields closed:
2421
2490
 
2422
2491
  `getAuthConfig`, `getDefaultAuthProvider`, `currentUser`, `myProviderToken`,
2423
- `customerLogout`, `customerRequestPasswordReset`, `customerResetPassword`,
2424
- `customerUpdatePassword`, `customerVerifyEmail`, `deleteMyAccount`.
2492
+ `logout`, `requestPasswordReset`, `resetPassword`,
2493
+ `updatePassword`, `verifyEmail`, `deleteMyAccount`.
2425
2494
 
2426
2495
  Plus the session-layer pairings for the flows that need one:
2427
2496
  `resetPasswordAndSignIn` (redeem the emailed token and persist the session it
2428
- mints — the peer of `loginWithPassword` for the branch where the customer
2497
+ mints — the peer of `loginWithPassword` for the branch where the user
2429
2498
  cannot log in), `signOut` (revoke server-side, then clear locally, and clear
2430
2499
  locally even if the revoke fails), and `authedOp` (`authedRequest`'s twin for
2431
2500
  auth ops: refresh once, retry once, on a 401-shaped failure).
@@ -2442,10 +2511,10 @@ note above.
2442
2511
  ### Removed
2443
2512
 
2444
2513
  - **The nine auth operation documents on `@foir/sdk/auth`** —
2445
- `CustomerLoginDocument`, `CustomerRegisterDocument`,
2446
- `CustomerRefreshTokenDocument`, `CustomerRequestOTPDocument`,
2447
- `CustomerLoginOTPDocument`, `CustomerLoginWithProviderDocument`,
2448
- `CustomerProviderCallbackDocument`, `CustomerProviderVerifyOTPDocument`,
2514
+ `UserLoginDocument`, `UserRegisterDocument`,
2515
+ `UserRefreshTokenDocument`, `UserRequestOTPDocument`,
2516
+ `UserLoginOTPDocument`, `UserLoginWithProviderDocument`,
2517
+ `UserProviderCallbackDocument`, `UserProviderVerifyOTPDocument`,
2449
2518
  `AuthProvidersDocument`.
2450
2519
 
2451
2520
  **Breaking, so the next release is a MINOR** per the pre-1.0 note above.
@@ -2457,15 +2526,15 @@ note above.
2457
2526
  eleven were unreachable. The flows now address the server's generated `auth`
2458
2527
  namespace, so `@foir/sdk/auth` carries no transport of its own.
2459
2528
 
2460
- *Migration.* Nothing that called a FUNCTION changes: `customerLogin(env, …)`,
2529
+ *Migration.* Nothing that called a FUNCTION changes: `login(env, …)`,
2461
2530
  `getAuthProviders(env)`, `loginWithPassword(adapter, env, …)`, `refreshSession`,
2462
2531
  `requireAuth`, `authedRequest` and every React hook in `@foir/sdk/react` keep
2463
2532
  their exact signatures. Only code importing a `*Document` const is affected,
2464
2533
  and only if it was executing the document itself through its own GraphQL
2465
2534
  client. Two options:
2466
2535
 
2467
- - call the SDK function instead (`customerLogin(env, vars)` rather than running
2468
- `CustomerLoginDocument` through Apollo/urql), which is what the exports were
2536
+ - call the SDK function instead (`login(env, vars)` rather than running
2537
+ `UserLoginDocument` through Apollo/urql), which is what the exports were
2469
2538
  for; or
2470
2539
  - keep your own client and write the document in your own codebase — these are
2471
2540
  fixed-shape platform operations, so the text does not vary by project.
@@ -2529,7 +2598,7 @@ actually happened.
2529
2598
 
2530
2599
  Purely additive: `./auth` keeps exporting all four, unchanged, and nothing is
2531
2600
  moved or renamed. What changes is the cost of reaching them. `./auth` also
2532
- exports the customer-auth GraphQL documents, which build their
2601
+ exports the user-auth GraphQL documents, which build their
2533
2602
  TypedDocumentNodes with a top-level `parse()` that no bundler can treat as
2534
2603
  pure — so a browser app importing only `createFoirSessionClient` was shipping
2535
2604
  the whole `graphql` package to get it. `src/auth/session-client.ts` imports
@@ -2590,7 +2659,7 @@ on its own, which is the intended behaviour for a release that adds exports.
2590
2659
 
2591
2660
  ### Added
2592
2661
 
2593
- - `@foir/sdk/auth`: `customerSignupContext` and `customerJoinWaitlist`, plus the
2662
+ - `@foir/sdk/auth`: `signupContext` and `joinWaitlist`, plus the
2594
2663
  `SignupContext`, `SignupMode` and `WaitlistJoinResult` types — waitlist and
2595
2664
  restricted-signup flows for projects that are not open-registration.
2596
2665
  - A pinned public-export surface: `public-exports.snapshot.json` plus
@@ -2617,7 +2686,7 @@ on its own, which is the intended behaviour for a release that adds exports.
2617
2686
 
2618
2687
  ### Added
2619
2688
 
2620
- - `@foir/sdk/client`: `createFoirContentClient` — forwards the customer bearer
2689
+ - `@foir/sdk/client`: `createFoirContentClient` — forwards the user bearer
2621
2690
  token on content reads, so authenticated and segment-targeted content works
2622
2691
  without hand-rolling the header.
2623
2692
 
@@ -2634,7 +2703,7 @@ on its own, which is the intended behaviour for a release that adds exports.
2634
2703
 
2635
2704
  ### Added
2636
2705
 
2637
- - **New subpath `@foir/sdk/react`** — drop-in React customer auth: `FoirProvider`,
2706
+ - **New subpath `@foir/sdk/react`** — drop-in React user auth: `FoirProvider`,
2638
2707
  `SignIn`, `OAuthButtons`, `UserButton`, `useFoirAuth`, `useFoirSession`,
2639
2708
  `useFoirUser`.
2640
2709
  - **New subpath `@foir/sdk/next`** — Next.js App Router BFF auth adapter: cookie
@@ -2662,7 +2731,7 @@ First public release. Ten subpaths:
2662
2731
  - `@foir/sdk/media` — progressive media components and adapters.
2663
2732
  - `@foir/sdk/content` — `createContentRenderer` and `RichTextRenderer` with
2664
2733
  injected typography tokens.
2665
- - `@foir/sdk/auth` — customer auth flows, token lifecycle, hosted-login bridge.
2734
+ - `@foir/sdk/auth` — user auth flows, token lifecycle, hosted-login bridge.
2666
2735
  - `@foir/sdk/realtime` — `createCollabSession`, a thin y-websocket helper.
2667
2736
  - `@foir/sdk/codegen`, `@foir/sdk/codegen/block-registry`,
2668
2737
  `@foir/sdk/codegen/block-scaffold` — the `foirCodegen` preset and the