@foir/sdk 0.22.0 → 0.24.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,84 @@ 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.24.0] — 2026-10-18
112
+
113
+ ### Changed
114
+
115
+ - **`joinWaitlist` resolves `true` for every address** (`@foir/sdk/auth`). It
116
+ used to resolve the waitlist entry, whose status told anyone who asked
117
+ whether an address had been approved. The server now answers every address
118
+ alike, whether it was queued, was already let in, or already has an account,
119
+ and what differs is only the email the address is sent. Migration: stop
120
+ reading `id`, `email` or `status` from the result; a resolved call means the
121
+ address was not refused.
122
+ - **`myProviderToken` and `logout` act for one sign-in** (`@foir/sdk/auth`). A
123
+ provider's token belongs to the sign-in that obtained it, so `logout` ends
124
+ this device's and no other device's, and a sign-in with nothing kept for it
125
+ resolves `linked: false` with `reauthenticate: true`. Migration: where
126
+ `reauthenticate` is true, show the user the way to sign in through the
127
+ provider again rather than an empty or failed page.
128
+
129
+ ### Removed
130
+
131
+ - **`WaitlistJoinResult`** (`@foir/sdk/auth`, type). `joinWaitlist` resolves a
132
+ boolean.
133
+ - **`verifyEmail`** (`@foir/sdk/auth`). It redeemed verification tokens that
134
+ only the console's own sign-up minted, and that lane is gone. An address is
135
+ proven by the code it is sent: `requestOTP`, then `loginWithOTP`, which marks
136
+ the address proven and lets in a person who was waiting to prove it.
137
+
138
+ ## [0.23.0] — 2026-10-16
139
+
140
+ ### Added
141
+
142
+ - **`signInRefusalOf(error)`** (`@foir/sdk/auth`) reads why a sign-in your app's
143
+ own form sent was refused, as a stable code: `password_sign_in_off` from
144
+ `login` or `register` where the project, or the app the sign-in came through,
145
+ takes no password, and `code_sign_in_off` from `requestOTP` or `loginWithOTP`
146
+ where it takes no code. Null for any other failure.
147
+ - **`hostedSignInNoticeOf` reads what the hosted login sent back** (`@foir/sdk/auth`).
148
+ A refused sign-in's `foir_error` code, or the provider a sign-in connected to
149
+ the account (`foir_connected`), each with a sentence your app can show as it
150
+ is. A code it has no words for reads as a general sentence, never the code.
151
+ - **`myProviderToken` says what it did about the token** (`@foir/sdk/auth`).
152
+ `refresh` is `REFRESHED`, `FAILED` (with `refreshFailure` saying where),
153
+ `HELD_BACK` or `NOT_ATTEMPTED`; `reauthenticate` is true when only signing in
154
+ with the provider again mends the connection; and `linked: false` answers a
155
+ signed-in user with nothing stored for the provider. Pass `forceRefresh`
156
+ after the provider refused the token it was given. A token whose provider
157
+ stated no expiry is now renewed when read, and two reads at once renew it
158
+ once. New type: `ProviderTokenRefresh`.
159
+ - **`requestPasswordReset` takes a `clientId`** (`@foir/sdk/auth`). Where the
160
+ project has no App base URL, the reset email links to Foir's hosted reset
161
+ page, and naming your app sends the user to its initiate login URL to sign in
162
+ once the password is changed.
163
+ - **`registerOrganization` takes an `appBaseUrl`** (`@foir/sdk/auth`), so an
164
+ organization a user founds has its address from the first request and an app
165
+ that finds its organization by `orgUrl` finds this one at once. The address
166
+ sits under the project's own app address or a domain it verified, and one
167
+ another organization holds is refused.
168
+
169
+ ### Changed
170
+
171
+ - **Breaking: `myOrganizations` pages from an entry's `cursor`** (`@foir/sdk/auth`).
172
+ Each `MyOrganization` it lists carries `cursor`; pass the last one's as
173
+ `after` for the next page. `after` no longer takes an organization's id. A
174
+ user whose role holds `organizations:read` in the project is listed every
175
+ organization of it, suspended ones included.
176
+
177
+ - **`ProviderToken.accessToken` may be `null`**, when `linked` is false.
178
+ - **`holds` reads a grant that names one field** (`@foir/sdk/auth`), as
179
+ `records:read:<model>.<field>` or `records:write:<model>.<field>`, the way the
180
+ platform does. A guarded field is covered by the grant that names it and by
181
+ one over every owner's records of every model, and not by its model's grant,
182
+ so `holds(['records:read'], 'records:read:order.taxId')` is `false`.
183
+ - **Breaking: a key in an older format is refused.** Foir answers `401` to a
184
+ key that begins `pk_`, `sk_` or `tc_` and says it no longer reads that format,
185
+ and the SDK reads a kind only from `foir_pub_`, `foir_srv_` and `foir_adm_`.
186
+ Make a key of its kind in its place: see
187
+ [Credentials](https://docs.foir.dev/credentials).
188
+
111
189
  ## [0.22.0] — 2026-10-15
112
190
 
113
191
  ### Added
@@ -135,23 +213,23 @@ Changes on `dev` that have not been published to npm.
135
213
 
136
214
  ### Changed
137
215
 
138
- - **Breaking: everyone who signs in is a user, and nothing says customer or
139
- admin as a kind.** It needs the platform release that serves the same
216
+ - **Breaking: everyone who signs in is a user, and no name says what kind
217
+ of person somebody is.** It needs the platform release that serves the same
140
218
  names; an earlier platform refuses every call below by name.
141
219
  - `@foir/sdk/auth`: the raw calls take the server's op names: `login`,
142
220
  `register`, `requestOTP`, `loginWithOTP`, `refreshTokens` (the
143
221
  `refreshToken` op), `logout`, `joinWaitlist`, `signupContext`,
144
222
  `requestPasswordReset`, `resetPassword`, `updatePassword`, `verifyEmail`,
145
- `loginWithProvider`, `providerCallback`, `providerVerifyOTP` (were
146
- `customerLogin` and the rest), and `registerOrganization` founds an
147
- organization over the `registerOrganization` op (was
148
- `registerOperatorOrganization`). The helpers that save a session are
223
+ `loginWithProvider`, `providerCallback`, `providerVerifyOTP`, each
224
+ renamed from a name that said what kind of person called it, and
225
+ `registerOrganization` founds an organization over the
226
+ `registerOrganization` op. The helpers that save a session are
149
227
  `signInWithPassword`, `signUpWithPassword` and `signInWithOTP` (were
150
228
  `loginWithPassword`, `registerWithPassword`, `loginWithOTP`), beside
151
- `signOut`. `getSessionCustomer` is `getSessionUser`, `customerAccess` is
152
- `userAccess`, `CustomerAuthResult` is `AuthResult` and carries `userId`,
153
- `FoirCustomer` is `FoirUser` (`{ id, email }`), and `FoirAccess.customerId`
154
- is `userId`. A session is stored under `foir:userId`, so one saved by an
229
+ `signOut`. The session, access and result helpers are `getSessionUser`,
230
+ `userAccess` and `AuthResult` (which carries `userId`), the signed-in
231
+ person is `FoirUser` (`{ id, email }`), and `FoirAccess` names them as
232
+ `userId`. A session is stored under `foir:userId`, so one saved by an
155
233
  earlier version reads as signed out once.
156
234
  - `@foir/sdk/auth`: a provider sign-in that names an address somebody
157
235
  already holds waits for the code sent to it: `providerSignInRefusalOf`
@@ -160,23 +238,23 @@ Changes on `dev` that have not been published to npm.
160
238
  - `@foir/sdk/auth`: the hosted session answers `userId`
161
239
  (`HostedSession.userId`, `FoirSessionUser.userId`), from the platform's
162
240
  `user_id`, and its cookie is `foir_session`.
163
- - `@foir/sdk/react`: `useFoirSession()` answers `user` (was `customer`).
164
- - `@foir/sdk/app`: `records.user` and `users` (were `records.customer` and
165
- `customers`), over the platform's `saveUserRecord`, `userRecordByIdentity`,
241
+ - `@foir/sdk/react`: `useFoirSession()` answers `user`.
242
+ - `@foir/sdk/app`: `records.user` and `users`, over the platform's
243
+ `saveUserRecord`, `userRecordByIdentity`,
166
244
  `eraseUserRecord`, `createUser`, `user`, `users`, `userByIdentity` and
167
245
  `userIdentitiesById`. `users.list()` answers `{ users }`. `AppUser`,
168
246
  `AppUserList`, `AppUserSaveResult` (`userCreated`), `AppUserEraseResult`
169
247
  (`userDeleted`, `userKeptBecause`), `AppUserEmailUnavailableReason`,
170
- `appUserUnlinkedOf` and `appUserEmailUnavailableOf` replace the
171
- `AppCustomer` names. A notification, a consent change and an
248
+ `appUserUnlinkedOf` and `appUserEmailUnavailableOf` are the app client's
249
+ names for the people it reads. A notification, a consent change and an
172
250
  organization's first member name `userId`.
173
251
  - `@foir/sdk/server`: an operation's context names who started the run as
174
- `actorId` and the user it acts for as `userId` (was `userId` and
175
- `customerId`). `ScopedTokenIngress.userId` was `customerId`.
252
+ `actorId` and the user it acts for as `userId` (`userId` used to name
253
+ who started it). `ScopedTokenIngress` names the user as `userId`.
176
254
  - `@foir/sdk/data`: `FoirGranteeKind` is `'USER'`, a note's subject may be
177
- `'user'`, `FoirUserNote` and `FoirUserNoteAttachment` replace the
178
- `FoirCustomerNote` names, and `FoirAuthDowngrade.userToken` was
179
- `customerToken`.
255
+ `'user'`, a note of a user's is `FoirUserNote` with
256
+ `FoirUserNoteAttachment`, and `FoirAuthDowngrade` names the dropped
257
+ token `userToken`.
180
258
 
181
259
  ### Removed
182
260
 
@@ -184,8 +262,9 @@ Changes on `dev` that have not been published to npm.
184
262
  (`@foir/sdk/auth`). Nothing stores where a user stands: a request names the
185
263
  organization it acts in (`orgId` or `orgUrl`), and the platform checks they
186
264
  stand there on that request. `myOrganizations` still lists where they may.
187
- - **Breaking: `requireAdmin` and `RequireAdminOptions`, and `isAdmin` on
188
- `FoirUser`** (`@foir/sdk/auth`). What a user may do is a role or a scope:
265
+ - **Breaking: the helpers that guarded on a person being of the managing
266
+ kind, and the flag on `FoirUser` that said so** (`@foir/sdk/auth`). What a
267
+ user may do is a role or a scope:
189
268
  guard on it with `requireRole` or `requireScope`. `requireAuth` takes the
190
269
  type `resolveUser` answers, for a project that reads its own profile.
191
270
 
@@ -195,7 +274,7 @@ Changes on `dev` that have not been published to npm.
195
274
 
196
275
  - **An operation provisions an organization** (`@foir/sdk/app`).
197
276
  `organizations.provision({ provisionId, name, parentId?, settings?, firstMember? })`
198
- makes an organization with a customer as its first member, in the role the
277
+ makes an organization with a user as its first member, in the role the
199
278
  project names for a founder, and answers `{ organizationId }`. It needs
200
279
  `organizations:create`. With `settings: { modelKey, data }` it writes the
201
280
  organization's settings record too and answers `settingsRecordId`; that
@@ -210,7 +289,7 @@ Changes on `dev` that have not been published to npm.
210
289
  needs the platform release that carries `scheduleOrganizationDeletion`.
211
290
  - **`FoirWriteNoteArgs`** (`@foir/sdk/data`): the arguments of
212
291
  `foir.ns.notes.writeNote`, the one field that writes a note for a signed-in
213
- customer and for a visitor. It asks `notes:create` and needs the platform
292
+ user and for a visitor. It asks `notes:create` and needs the platform
214
293
  release that carries `writeNote`. `foir.ns.notes` answers any field the
215
294
  platform serves, so the call itself needs no new SDK.
216
295
 
@@ -237,7 +316,7 @@ Changes on `dev` that have not been published to npm.
237
316
  itself uses `createServerClient` from `@foir/sdk/server`. A key minted
238
317
  before a key had a kind (`sk_`) is not refused here.
239
318
  - **A key's kind is read from its prefix in one place.** A key is publishable
240
- (`foir_pub_`), server (`foir_srv_`) or admin (`foir_adm_`), and a key minted
319
+ (`foir_pub_`), server (`foir_srv_`) or the console (`foir_adm_`), and a key minted
241
320
  earlier is `pk_`, `sk_` or `tc_`. Only a publishable key, in either format,
242
321
  is named in a URL and takes the shared cache lane. Earlier versions looked
243
322
  for the letters `pk_` alone, so a `foir_pub_` key was sent to the origin and
@@ -301,7 +380,7 @@ to this version then.
301
380
  reads it as what those were worth, exactly as the platform does.
302
381
 
303
382
  A record grant reaches the operation's own records: the project's, or those
304
- of the customer a run acts for. `records:read.all:<model>`, and the write,
383
+ of the user a run acts for. `records:read.all:<model>`, and the write,
305
384
  delete and publish forms like it, reach every owner's records of the model.
306
385
  An older token's write of a model was every owner's, so it is read as
307
386
  `records:write.all:<model>` and `records:create:<model>`, and takes that
@@ -330,19 +409,19 @@ to this version then.
330
409
  platform holds a running install's setting to a category the project has.
331
410
 
332
411
  - **An app can say what connecting changes** (`@foir/sdk/app/contract`).
333
- `Credentials.connectNotice` is the app's own words, shown to the operator
412
+ `Credentials.connectNotice` is the app's own words, shown to the user
334
413
  beside the step that connects their account and, for an install the vendor
335
414
  started, before the install is confirmed. A vendor that keeps one live
336
415
  connection per account (Shopify, per store) says here that connecting moves
337
416
  it from any other project. Empty when there is nothing to say.
338
417
 
339
- - **The roles a signed-in customer holds, and what they may do**
340
- (`@foir/sdk/auth`, `@foir/sdk/react`). A storefront can now ask which customer
341
- roles the signed-in customer holds and show the page to match, where before
418
+ - **The roles a signed-in user holds, and what they may do**
419
+ (`@foir/sdk/auth`, `@foir/sdk/react`). A storefront can now ask which user
420
+ roles the signed-in user holds and show the page to match, where before
342
421
  it had to treat one of a role's permissions as a stand-in for the role.
343
422
 
344
- - `getSessionAccess(adapter, env)` and `customerAccess(env, { bearerToken })`
345
- return a `FoirAccess`: the customer's id, the keys of the roles they hold
423
+ - `getSessionAccess(adapter, env)` and `userAccess(env, { bearerToken })`
424
+ return a `FoirAccess`: the user's id, the keys of the roles they hold
346
425
  (`roles`), and everything they may do through the key (`scopes`). Plain
347
426
  data, so a loader can return it. `null` when nobody is signed in.
348
427
  - `hasRole(access, key)` and `hasScope(access, scope)` read it. `hasScope`
@@ -361,7 +440,7 @@ to this version then.
361
440
  and a role taken away is gone the next time it is asked.
362
441
 
363
442
  `roles` is `null`, not an empty list, when the platform did not say which
364
- roles the customer holds. `hasRole` answers false for it and `requireRole`
443
+ roles the user holds. `hasRole` answers false for it and `requireRole`
365
444
  refuses with `roles_unknown`, so an outage is never read as "not staff". A
366
445
  platform that does not serve the field yet reads the same way: the role
367
446
  helpers need the platform release that carries it.
@@ -373,7 +452,7 @@ to this version then.
373
452
  `accessStatus`, `loadAccess` and `ensureAccess`.
374
453
 
375
454
  - **A save says when it made a record the operation may not read**
376
- (`@foir/sdk/app`). `records.upsert` and `records.customer.save` answer
455
+ (`@foir/sdk/app`). `records.upsert` and `records.user.save` answer
377
456
  `fieldsWithheld`. It is true when the operation declared the create of a
378
457
  model and not the read: the save made the record, and `record` carries its
379
458
  `id` and `modelKey` and nothing else. The record is not empty.
@@ -389,13 +468,13 @@ to this version then.
389
468
  app's operations are given the one they could use, so an installed app keeps
390
469
  running; a manifest served from then on says it.
391
470
 
392
- - **Breaking, with the platform release that carries it: erasing a customer
393
- takes `identities:delete`** (`@foir/sdk/app`). `records.customer.erase` needed
471
+ - **Breaking, with the platform release that carries it: erasing a user
472
+ takes `identities:delete`** (`@foir/sdk/app`). `records.user.erase` needed
394
473
  `users:create` beside the delete and the read of the model, and now needs
395
474
  `identities:delete` there. `APP_GRANTS` carries it, with `files:delete`,
396
475
  `notifications:read` and every owner's files (`files:read.all`,
397
476
  `files:write.all`, `files:delete.all`), which a manifest may now declare.
398
- `verifyScopedToken` reads a token minted before, that carried the customer
477
+ `verifyScopedToken` reads a token minted before, that carried the user
399
478
  create beside the delete of a model, as holding `identities:delete`, as the
400
479
  platform does.
401
480
 
@@ -413,17 +492,14 @@ to this version then.
413
492
  | `config:read` | `appconfig:read` |
414
493
  | `secrets:read.app`, `secrets:put.app`, `secrets:list.app`, `secrets:delete.app` | `appsecrets:read`, `appsecrets:put`, `appsecrets:list`, `appsecrets:delete` |
415
494
  | `files:list` | `files:read` |
416
- | `customers:write` | `users:create` |
417
- | `customers:read` | `users:read.all` |
418
- | `customers:read:named` | `users:read.named` |
419
- | `customers:read:identity` | `identities:read` |
495
+ | The names for writing and reading a project's users | `users:create`, `users:read.all`, `users:read.named`, `identities:read` |
420
496
  | `notifications:write` | `notifications:write.named` |
421
497
  | `records:write:<model>` | `records:write:<model>` and `records:create:<model>` |
422
498
 
423
499
  The last row is a change in what the name means. `records:write:<model>`
424
500
  changes a record that is there, and making one is `records:create:<model>`.
425
501
  An operation that does both declares both. `records.upsert` and
426
- `records.customer.save` are let through on either, and the platform asks for
502
+ `records.user.save` are let through on either, and the platform asks for
427
503
  the one the save turns out to need.
428
504
 
429
505
  - **Breaking, with the platform release that carries it: a full-page placement
@@ -436,7 +512,7 @@ to this version then.
436
512
  things change in what a placement writes:
437
513
 
438
514
  - **Every owner's records are written `.all`.** `records:read:order` was
439
- every order and is now the operator's own. Declare
515
+ every order and is now the user's own. Declare
440
516
  `records:read.all:order`.
441
517
  - **A write no longer includes its read.** `records:write:order` also read
442
518
  and created, and `apps:write` also read apps. Declare each one the page
@@ -445,8 +521,8 @@ to this version then.
445
521
  - **A wildcard and a permission that gives others are refused**, as `*`
446
522
  always was.
447
523
 
448
- The token then carries what the placement declares and the operator holds,
449
- permission by permission. An operator who holds part of a declared grant is
524
+ The token then carries what the placement declares and the user holds,
525
+ permission by permission. A user who holds part of a declared grant is
450
526
  given that part, where before the whole name was withheld.
451
527
 
452
528
  - **`session.can` answers by what the token covers** (`@foir/sdk/placement`).
@@ -487,11 +563,11 @@ to this version then.
487
563
  `records:read` for the model beside their own permission.
488
564
  - An operation that declares the create of a model and not the read
489
565
  (`@foir/sdk/app`) still makes its records. `records.upsert` landing on a
490
- record already there, `records.customer.save` likewise, `records.delete`,
566
+ record already there, `records.user.save` likewise, `records.delete`,
491
567
  `records.refresh`, `records.rename` and `records.updateMany` are refused
492
568
  until it declares the read too.
493
569
 
494
- - **Breaking, with the platform release that carries it: `records.customer.erase`
570
+ - **Breaking, with the platform release that carries it: `records.user.erase`
495
571
  needs the delete of the model** (`@foir/sdk/app`). An erase removes a record,
496
572
  so the operation declares `records:delete` and `records:read` for the model,
497
573
  and `users:create`. Until now the grant to save the record was enough. An
@@ -503,14 +579,14 @@ to this version then.
503
579
  `MyOrganizationRole.permissions` is gone and `MyOrganizationRole.grants`
504
580
  takes its place: what the role gives, in the permission catalogue's names. A
505
581
  grant stands for itself, so changing a record and creating one are two
506
- names (`records:write`, `records:create`), reading every customer's records
582
+ names (`records:write`, `records:create`), reading every user's records
507
583
  is `records:read.all`, and staffing an organization is `members:read` and
508
584
  `members:write`. Code that looked for `organizations:staff` or
509
585
  `records:list.all` in a role's list looks for the new names.
510
586
 
511
- - **Breaking, with the platform release that carries it: `customers.byIdentity`
512
- and `customers.identities` need `identities:read`, declared by name**
513
- (`@foir/sdk/app`). The read of every customer, `users:read.all`, no longer
587
+ - **Breaking, with the platform release that carries it: `users.byIdentity`
588
+ and `users.identities` need `identities:read`, declared by name**
589
+ (`@foir/sdk/app`). The read of every user, `users:read.all`, no longer
514
590
  includes them.
515
591
 
516
592
  ### Removed
@@ -581,29 +657,29 @@ the next promotion; `latest` moves to this version then.
581
657
  (`@foir/sdk/data`). Code that reads a drift is unaffected. Code that builds
582
658
  one by hand, for example to test a handler given to `onSchemaDrift` or made by
583
659
  `createFoirDriftHandler`, must add `incompatible: []`.
584
- - **Breaking (types): `AppConsentChange` names its customer one way**
585
- (`@foir/sdk/app`). It was an interface with a required `customerId`. It is
586
- now a type that holds a `customerId` or an `email`, never both. A change
587
- written as `{ customerId, ... }` is unaffected. An interface that extended
660
+ - **Breaking (types): `AppConsentChange` names its user one way**
661
+ (`@foir/sdk/app`). It was an interface with a required `userId`. It is
662
+ now a type that holds a `userId` or an `email`, never both. A change
663
+ written as `{ userId, ... }` is unaffected. An interface that extended
588
664
  `AppConsentChange` no longer compiles: intersect with it instead.
589
665
 
590
666
  ### Added
591
667
 
592
- - `notifications.setConsent` takes `email` in place of `customerId`
593
- (`@foir/sdk/app`), for an opt-in from somebody who may have no customer yet,
594
- such as a sign-up form. The platform finds the customer holding the address
668
+ - `notifications.setConsent` takes `email` in place of `userId`
669
+ (`@foir/sdk/app`), for an opt-in from somebody who may have no user yet,
670
+ such as a sign-up form. The platform finds the user holding the address
595
671
  at the install's level, or makes one, together with the change, so a change
596
672
  it refuses (a category the project does not have, a confirmation email the
597
- project cannot send) leaves no customer behind. Finding or making the
598
- customer first with `customers.ensure` did. The operation must declare
599
- `customers:write` as well as `notifications:consent`. `AppConsentSubject` is
673
+ project cannot send) leaves no user behind. Finding or making the
674
+ user first with `users.ensure` did. The operation must declare
675
+ `users:create` as well as `notifications:consent`. `AppConsentSubject` is
600
676
  exported.
601
677
 
602
678
  - `FoirSchemaDrift.incompatible`: what the generated module describes that the
603
679
  server no longer serves that way, as sorted paths into the manifest
604
680
  (`models.order.selection.status`, `models.order.ops.update`,
605
681
  `namespaces.search.ops.searchRecords.args.query`,
606
- `models.order.relations.customer`, `selectionUnions.Block.HeroBlock.title`).
682
+ `models.order.relations.buyer`, `selectionUnions.Block.HeroBlock.title`).
607
683
  Empty when the surface has only grown. `changed` keeps its meaning: what
608
684
  moved, not what broke.
609
685
 
@@ -614,26 +690,26 @@ platform serves from the next promotion; `latest` moves to it then.
614
690
 
615
691
  ### Changed
616
692
 
617
- - **Breaking: an app finds a customer by identity through its install, not
618
- through a model** (`@foir/sdk/app`). The customer sign-in an app's customers
693
+ - **Breaking: an app finds a user by identity through its install, not
694
+ through a model** (`@foir/sdk/app`). The user sign-in an app's users
619
695
  come through is paired with the install as a whole, so the two reads that
620
- walk it no longer name a model and have moved beside the other customer
696
+ walk it no longer name a model and have moved beside the other user
621
697
  reads:
622
- - `records.customer.identities(model, customerId)` is now
623
- `customers.identities(customerId)`.
624
- - `customers.byIdentity(identity)` is new: the id of the customer who holds
698
+ - `records.user.identities(model, userId)` is now
699
+ `users.identities(userId)`.
700
+ - `users.byIdentity(identity)` is new: the id of the user who holds
625
701
  an identity at the app's own system, or `null`. It replaces reading a
626
- customer's record for its owner.
702
+ user's record for its owner.
627
703
 
628
- Both need the new capability `customers:read:identity` (or `customers:read`),
704
+ Both need the new capability `identities:read` (or `users:read.all`),
629
705
  declared by the operation, and are refused at an install that pairs no
630
706
  sign-in. Reading or writing a model is no longer what admits them.
631
- - `install.config()` asks the platform for `customerSignIn`, a field a platform
707
+ - `install.config()` asks the platform for `userSignIn`, a field a platform
632
708
  older than this release does not have.
633
709
 
634
710
  ### Added
635
711
 
636
- - `install.config()` answers `customerSignIn`: the key of the customer sign-in
712
+ - `install.config()` answers `userSignIn`: the key of the user sign-in
637
713
  the install pairs, or `null` when it pairs none.
638
714
 
639
715
  ## [0.17.0] — 2026-10-02
@@ -643,19 +719,19 @@ platform serves from the next promotion; `latest` moves to it then.
643
719
 
644
720
  ### Added
645
721
 
646
- - **An app is told when a project cannot email its customers**
722
+ - **An app is told when a project cannot email its users**
647
723
  (`@foir/sdk/app`). A project with no App base URL used to accept a
648
724
  notification's email and drop it. The platform now refuses a notification
649
725
  that carries an email, and an opt-in to a category confirmed by email, with
650
- the reason as data. `appCustomerEmailUnavailableOf(error)` reads it:
726
+ the reason as data. `appUserEmailUnavailableOf(error)` reads it:
651
727
  `{ reason: 'NO_APP_BASE_URL' }`, or `null` for any other failure.
652
- - `install.config()` answers `customerEmailUnavailable`: the same reason ahead
653
- of time, or `null` when the project can send. `AppCustomerEmailUnavailableReason`
728
+ - `install.config()` answers `userEmailUnavailable`: the same reason ahead
729
+ of time, or `null` when the project can send. `AppUserEmailUnavailableReason`
654
730
  is the type.
655
731
 
656
732
  ### Changed
657
733
 
658
- - `install.config()` asks the platform for `customerEmailUnavailable`, a field
734
+ - `install.config()` asks the platform for `userEmailUnavailable`, a field
659
735
  a platform older than this release does not have.
660
736
 
661
737
  ## [0.16.0] — 2026-10-02
@@ -666,11 +742,11 @@ it at the next promotion.
666
742
  ### Added
667
743
 
668
744
  - **An embedded page is told how the console looks** (`@foir/sdk/placement`).
669
- The console's theme is the operator's own setting, which a framed page cannot
745
+ The console's theme is the user's own setting, which a framed page cannot
670
746
  see, so a page that followed the system's preference could sit dark inside a
671
747
  light console. `connectPlacement` takes `onAppearance`, called with
672
748
  `{ theme: 'light' | 'dark' }` as soon as the console says (before the token
673
- arrives) and again whenever the operator changes it; `session.appearance`
749
+ arrives) and again whenever the user changes it; `session.appearance`
674
750
  holds the current one. `PlacementAppearance` is the type, and
675
751
  `PLACEMENT_MESSAGE.appearance` the message.
676
752
  - A console older than this says nothing: `onAppearance` is not called and
@@ -688,7 +764,7 @@ then.
688
764
  - **`notifications.setConsent` answers `confirmationPending`**, and takes
689
765
  `givenElsewhere` and `locale` (`@foir/sdk/app`). A project can ask that an
690
766
  opt-in an app collects in a category is confirmed by email before it counts.
691
- In such a category an opt-in writes nothing: the platform sends the customer
767
+ In such a category an opt-in writes nothing: the platform sends the user
692
768
  one confirmation email, in the page's `locale` when they have stated no
693
769
  language of their own, and the answer says `confirmationPending`. The
694
770
  preference turns on when they follow the link. Everywhere else an opt-in is
@@ -709,17 +785,17 @@ then.
709
785
 
710
786
  ### Added
711
787
 
712
- - **`customers.get`, `customers.byEmail` and `customers.list`** on the app
713
- client (`@foir/sdk/app`): an operation reads the customers its work is about.
714
- `get(id)` and `byEmail(email)` answer one customer or null, and need
715
- `customers:read:named`: the customer the operation names, by id or by their
788
+ - **`users.get`, `users.byEmail` and `users.list`** on the app
789
+ client (`@foir/sdk/app`): an operation reads the users its work is about.
790
+ `get(id)` and `byEmail(email)` answer one user or null, and need
791
+ `users:read.named`: the user the operation names, by id or by their
716
792
  exact email address at the install's level, and nothing wider. `list` pages
717
- through the customers at the install's level and needs `customers:read`,
793
+ through the users at the install's level and needs `users:read.all`,
718
794
  which covers the two lookups as well. An operation declaring only the named
719
- read is refused the list. `AppCustomer` and `AppCustomerList` are their types.
720
- - **`customers.ensure` answers `verified`**: whether the customer it found or
721
- made has proved the address is theirs. It finds the customer holding the
722
- email either way, so one shopper asking twice is one customer; a caller with
795
+ read is refused the list. `AppUser` and `AppUserList` are their types.
796
+ - **`users.ensure` answers `verified`**: whether the user it found or
797
+ made has proved the address is theirs. It finds the user holding the
798
+ email either way, so one shopper asking twice is one user; a caller with
723
799
  something that should wait for proof asks the flag first.
724
800
 
725
801
  ## [0.13.0] — 2026-10-01
@@ -746,7 +822,7 @@ then.
746
822
  order, at most `first` of them, with `hasMore` saying the filter matched more.
747
823
  For work an app hands out in turn, such as the next ten people waiting: list
748
824
  them, then update the ones listed by id. A model's list is named by its
749
- plural, which its operator may set, so the first list a client makes reads
825
+ plural, which its user may set, so the first list a client makes reads
750
826
  the data API's surface descriptor (`GET /v1/manifest`) as the same token,
751
827
  once. `AppRecordList` is its query's type.
752
828
 
@@ -771,12 +847,12 @@ production serves from the next promotion; `latest` moves to it then.
771
847
  `whoami`, `install.config`, `install.credentials`,
772
848
  `install.writeCredentials`, `install.reportStatus`, `records.upsert`,
773
849
  `read`, `delete`, `refresh`, `rename`, `updateMany` and `aggregate`,
774
- `records.customer.save`, `read`, `erase` and `identities`,
775
- `customers.ensure`, `notifications.send` and `notifications.setConsent`, and
850
+ `records.user.save`, `read`, `erase` and `identities`,
851
+ `users.ensure`, `notifications.send` and `notifications.setConsent`, and
776
852
  `request` for a document of your own. Nothing is checked before a call: what
777
853
  the token may do is the platform's to hold it to, and a refusal is a
778
854
  `FoirRequestError` carrying the platform's answer.
779
- - `appLookupConflictOf`, `appCustomerUnlinkedOf` and `isAppFieldWithheld` read
855
+ - `appLookupConflictOf`, `appUserUnlinkedOf` and `isAppFieldWithheld` read
780
856
  the refusals an app acts on, as data.
781
857
  - `readAppDispatch` and `readCredentialProbe` read the two bodies the platform
782
858
  posts to an app, and `redeemExchangeTicket` trades an OAuth exchange ticket
@@ -804,7 +880,7 @@ production serves from the next promotion; `latest` moves to it then.
804
880
  what it throws when it has no keys to judge a token by.
805
881
  - **`requireScopedToken` answers more about who is calling.** The ingress it
806
882
  resolves with gains `orgId` (the organisation level the token was minted at,
807
- `null` when it states none), `customerId`, `expiresAt` and `tokenId`.
883
+ `null` when it states none), `userId`, `expiresAt` and `tokenId`.
808
884
  `ScopedTokenError` gains `reason` (`missing`, `invalid`, `forbidden` or
809
885
  `unavailable`), and its `status` may now be 503: keys that cannot be fetched
810
886
  are this service's outage, which the platform retries, and not a verdict on
@@ -816,7 +892,7 @@ production serves from the next promotion; `latest` moves to it then.
816
892
  passed. It takes `apiUrl`, used when a dispatch names no address to report
817
893
  to, `retry`, and `signal`. A call the platform did not answer, or answered
818
894
  with a 5xx or a 429, is tried again, with the same signed bytes.
819
- - **`@foir/sdk/placement`**, a new subpath for an app's own admin console shown
895
+ - **`@foir/sdk/placement`**, a new subpath for an app's own console shown
820
896
  inside Foir's as a full-page placement. `connectPlacement` tells the console
821
897
  the page is ready, takes the token it posts into the frame (only from the
822
898
  consoles you list), keeps it fresh, and gives you `request`, `can` and
@@ -875,8 +951,8 @@ production serves from the next promotion; `latest` moves to it then.
875
951
  once and asks again, failing with `FoirSessionExpiredError` when the session
876
952
  is over.
877
953
  - A request that sent no bearer is untouched.
878
- - **A refresh that names no customer keeps the session's identity.**
879
- `saveSession` used to write the customer id and email unconditionally, so a
954
+ - **A refresh that names no user keeps the session's identity.**
955
+ `saveSession` used to write the user id and email unconditionally, so a
880
956
  refresh answered without them left a valid token beside an empty identity, and
881
957
  `requireAuth` turned the session away. Given `env`, `requireAuth` now reads a
882
958
  missing identity once from `currentUser` rather than failing.
@@ -934,10 +1010,10 @@ surface version instead of the schema counter.
934
1010
  credential.
935
1011
 
936
1012
  Two things a dispatch client does not do. It does not subscribe: the
937
- subscription socket authenticates a key, an admin token or a customer bearer
1013
+ subscription socket authenticates a key, a console user token or a user bearer
938
1014
  and has no path for a dispatch token, so `onChange` and friends throw, naming
939
1015
  why. And it takes no key-side option (`apiKey`, `bearerToken`, `orgId`,
940
- `channel`, the socket options), because the token carries its own customer,
1016
+ `channel`, the socket options), because the token carries its own user,
941
1017
  organization and channel; each is a compile error and a runtime refusal.
942
1018
 
943
1019
  - `readDispatch(request)` and `OperationPayloadError` on `@foir/sdk/server`.
@@ -964,7 +1040,7 @@ surface version instead of the schema counter.
964
1040
 
965
1041
  - `FoirRecordOwner` on `@foir/sdk/data`: `{ id, ownerId, ownerKind }`, what
966
1042
  `setOwner` answers with. An operation that declares `records:attribute:<model>`
967
- hands a record to another customer with `foir.<model>.setOwner({ id, ownerId })`,
1043
+ hands a record to another user with `foir.<model>.setOwner({ id, ownerId })`,
968
1044
  and names the owner of a record it creates with `create({ input, ownerId })`.
969
1045
  The answer carries the owner and not the record, because an operation acting
970
1046
  for the previous holder may no longer read it.
@@ -1208,14 +1284,14 @@ returns a result object rather than `HostedSession | null`, and
1208
1284
  silent wrong answer for a thrown one that discards the answer. Nothing changes
1209
1285
  for a caller that does not pass it.
1210
1286
 
1211
- - `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.
1212
- - **The operator's organization context, on `@foir/sdk/auth`** — `myOrganizations`
1287
+ - `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.
1288
+ - **The user's organization context, on `@foir/sdk/auth`** — `myOrganizations`
1213
1289
  and `selectOrganization`, plus the types `MyOrganization` and
1214
1290
  `SelectedOrganization`, and two entries on `AUTH_OPS`.
1215
1291
 
1216
1292
  A Fóir project can be partitioned into organizations, one per tenant of a SaaS
1217
- built on Fóir. An *operator* is a customer who runs one or more of them.
1218
- `myOrganizations()` answers which ones this customer may stand in — from their
1293
+ built on Fóir. An *user* is a user who runs one or more of them.
1294
+ `myOrganizations()` answers which ones this user may stand in — from their
1219
1295
  own membership rows, so it is not the project's inventory and cannot be used as
1220
1296
  one — and `selectOrganization({ orgId })` says which. An empty `orgId` clears
1221
1297
  the selection.
@@ -1225,7 +1301,7 @@ returns a result object rather than `HostedSession | null`, and
1225
1301
  caller's membership on every request. An application therefore cannot forget
1226
1302
  to send it, cannot send another organization's, and cannot be made to cross
1227
1303
  the boundary by a query it built wrong. These two functions are the entire
1228
- per-customer surface; everything downstream names no organization at all.
1304
+ per-user surface; everything downstream names no organization at all.
1229
1305
  (The project credential's side of the same boundary is the client-level
1230
1306
  `orgId` option below — a property of the client, still never of a call.)
1231
1307
 
@@ -1339,7 +1415,7 @@ returns a result object rather than `HostedSession | null`, and
1339
1415
  Schema drift is a different problem with the same remedy. A deploy that
1340
1416
  renames a field leaves every cached entry perfectly current and quietly wrong:
1341
1417
  no record changed, nothing is out of order, and the bytes describe a schema
1342
- that no longer exists — until the tab is reloaded, which on a long admin
1418
+ that no longer exists — until the tab is reloaded, which on a long console
1343
1419
  session can be days. `createFoirDriftHandler` wires the `FoirSchemaDrift` the
1344
1420
  data client already reports to a full clear.
1345
1421
 
@@ -1460,7 +1536,7 @@ returns a result object rather than `HostedSession | null`, and
1460
1536
  credential and the bearer is re-exchanged when it dies. A consumer that cannot
1461
1537
  see the lifetime has to hardcode the platform's (coupling itself to a server
1462
1538
  constant it cannot observe) or decode `exp` off the JWT — or, as happened,
1463
- treat a 15-minute bearer as good forever and have customers appear signed out
1539
+ treat a 15-minute bearer as good forever and have users appear signed out
1464
1540
  mid-session. `createFoirSessionClient` on the SPA path has read `expires_in`
1465
1541
  and scheduled a proactive refresh all along; this closes the asymmetry.
1466
1542
 
@@ -1470,10 +1546,10 @@ returns a result object rather than `HostedSession | null`, and
1470
1546
  - **`HostedSessionResult`** exported from `./auth`.
1471
1547
 
1472
1548
  - **`FoirAuthDowngrade`** on `./data` and `./server`, plus `extensions` on the
1473
- response envelope — Foir now reports when it DISCARDED a customer token you
1549
+ response envelope — Foir now reports when it DISCARDED a user token you
1474
1550
  sent, and the SDK stops dropping that report on the floor.
1475
1551
 
1476
- A customer JWT is optional: one that fails to verify is not refused, the read
1552
+ A user JWT is optional: one that fails to verify is not refused, the read
1477
1553
  is served anonymously and returns `200`. So a lapsed token and a genuinely
1478
1554
  anonymous request were indistinguishable — nothing threw, no refresh armed,
1479
1555
  and auth-targeted content quietly resolved to its anonymous variant. The
@@ -1482,7 +1558,7 @@ returns a result object rather than `HostedSession | null`, and
1482
1558
  ```json
1483
1559
  { "data": { "…": "…" },
1484
1560
  "extensions": { "auth": {
1485
- "customerToken": "rejected", "reason": "expired", "servedAs": "anonymous"
1561
+ "userToken": "rejected", "reason": "expired", "servedAs": "anonymous"
1486
1562
  } } }
1487
1563
  ```
1488
1564
 
@@ -1502,7 +1578,7 @@ returns a result object rather than `HostedSession | null`, and
1502
1578
 
1503
1579
  The GraphQL `events` subscription has been live on the public schema since the
1504
1580
  events spine shipped, with no client able to reach it. It is an `sk_`-only
1505
- operator surface, so it lives on `./server` beside the rest of the events
1581
+ console surface, so it lives on `./server` beside the rest of the events
1506
1582
  client and inherits its `pk_` and `Authorization` refusals unchanged.
1507
1583
 
1508
1584
  New exports: `FoirEventsSubscribeParams`, plus `FoirSubscription` and
@@ -1524,7 +1600,7 @@ returns a result object rather than `HostedSession | null`, and
1524
1600
  It collapsed every failure to `null`: no cookie, a `401`, a `502`, a rate
1525
1601
  limit, a DNS failure. Consumers have to act on that `null`, and the only
1526
1602
  safe-looking action is to clear the session and redirect to the front door —
1527
- so one blip on `/session/token` signed out a customer whose 30-day cookie was
1603
+ so one blip on `/session/token` signed out a user whose 30-day cookie was
1528
1604
  perfectly valid, and nothing recorded that it had happened. The route's own
1529
1605
  rate limiter is deliberately fail-open for the opposite reason.
1530
1606
 
@@ -1543,7 +1619,7 @@ returns a result object rather than `HostedSession | null`, and
1543
1619
  `outcome` says what to DO; `reason` says what happened. Only `401` and a
1544
1620
  missing cookie are `signed-out`. A `403` reports as `unavailable` with reason
1545
1621
  `forbidden` — it means the CSRF header did not arrive, which is a
1546
- misconfiguration rather than a blip, but signing the customer out over it
1622
+ misconfiguration rather than a blip, but signing the user out over it
1547
1623
  would still be wrong.
1548
1624
 
1549
1625
  - **`foir.ns.operations.onExecution` delivers `status` as the
@@ -1583,14 +1659,14 @@ Everything additive, so a MINOR either way under the pre-1.0 note.
1583
1659
  ### Added
1584
1660
 
1585
1661
  - **`hostedLogoutUrl(config, clientId, returnTo?)` on `./auth`** — the mirror of
1586
- `hostedLoginUrl`, and the only thing that actually signs a customer out on the
1662
+ `hostedLoginUrl`, and the only thing that actually signs a user out on the
1587
1663
  hosted-cookie flow.
1588
1664
 
1589
1665
  Login on that flow is symmetric across browser and server; logout was not.
1590
- The apex `foir_customer_session` cookie is the durable credential, and
1666
+ The apex `foir_session` cookie is the durable credential, and
1591
1667
  disposing of a bearer, clearing a local session, or calling the
1592
- `customerLogout` mutation all leave it in place — the next request
1593
- re-establishes the customer from it. The only surface that ended a session was
1668
+ `logout` mutation all leave it in place — the next request
1669
+ re-establishes the user from it. The only surface that ended a session was
1594
1670
  `POST /session/logout`, cookie-authenticated and CSRF-header-gated, which only
1595
1671
  a browser sending `credentials: 'include'` can reach.
1596
1672
 
@@ -1962,7 +2038,7 @@ note above.
1962
2038
 
1963
2039
  Reads take the shared, cacheable lane where they can:
1964
2040
  `GET /cdn/graphql?key=pk_…` with the persisted-query hash in the URL when the
1965
- call is a query, on a `pk_` key, with no customer bearer, on the published
2041
+ call is a query, on a `pk_` key, with no user bearer, on the published
1966
2042
  channel — and `POST /graphql` otherwise. The server's gate refuses on five
1967
2043
  conditions and the client decides all five before the request: the fifth,
1968
2044
  "the credential is in the URL", it satisfies by construction. The server is
@@ -1984,7 +2060,7 @@ note above.
1984
2060
  hand-written document may legitimately have several. As a document it reaches
1985
2061
  everything the credential can reach — including the two dozen root fields no
1986
2062
  model and no namespace claims (`designTokens`, `record`, `locales`, the
1987
- customer-auth mutations), which is the point of an escape hatch and is more
2063
+ user-auth mutations), which is the point of an escape hatch and is more
1988
2064
  than the old shape could address.
1989
2065
 
1990
2066
  - **Every op prints, and there is one wire format rather than two.** The
@@ -2047,19 +2123,19 @@ note above.
2047
2123
  schema knowledge the client is not allowed to hold. The manifest publishes
2048
2124
  each op's result shape, so the table has nothing left to answer. Neither
2049
2125
  symbol has appeared in a published release.
2050
- - **`customerResendVerificationEmail` from `@foir/sdk/auth`** — and the
2051
- `AUTH_OPS.customerResendVerificationEmail` entry with it. **It never sent an
2052
- email.** The resolver behind it checked that the request carried a customer id,
2053
- returned `true`, and called no RPC; there is no customer verification-code
2054
- column and no customer verification email in the action catalogue for it to
2126
+ - **`resendVerificationEmail` from `@foir/sdk/auth`** — and the
2127
+ `AUTH_OPS.resendVerificationEmail` entry with it. **It never sent an
2128
+ email.** The resolver behind it checked that the request carried a user id,
2129
+ returned `true`, and called no RPC; there is no user verification-code
2130
+ column and no user verification email in the action catalogue for it to
2055
2131
  have used. A caller got a success response every time and nothing was
2056
2132
  delivered, which is worse than an error, because there is no signal to act on.
2057
2133
 
2058
- **Use the OTP flow for mailbox proof:** `customerRequestOTP(env, email)` emails
2059
- a code, and `customerLoginOTP(env, { email, otp })` redeems it — activating a
2134
+ **Use the OTP flow for mailbox proof:** `requestOTP(env, email)` emails
2135
+ a code, and `loginWithOTP(env, { email, otp })` redeems it — activating a
2060
2136
  pending account on redemption and returning a session, so possession of the
2061
- mailbox is proven by the same call that signs the customer in. That is a live,
2062
- delivered path today; `customerVerifyEmail` remains for redeeming a token that
2137
+ mailbox is proven by the same call that signs the user in. That is a live,
2138
+ delivered path today; `verifyEmail` remains for redeeming a token that
2063
2139
  arrived by some other route.
2064
2140
 
2065
2141
  Like the two entries above, this symbol has not appeared in a published
@@ -2092,11 +2168,11 @@ note above.
2092
2168
  id are what the note is filed against and never appear on screen.
2093
2169
 
2094
2170
  Opening the panel reads `noteConfig`, which the API charges `notes:write` and
2095
- refuses to a caller with no customer session, so one probe settles whether the
2171
+ refuses to a caller with no session, so one probe settles whether the
2096
2172
  visitor is signed in, whether their role may write notes, and whether
2097
2173
  screenshot capture may be offered. A missing scope reads like a bad API key and
2098
- is not one, so it is reported as what it is: the signed-in customer's ROLE is
2099
- short `notes:write`, which a new project's default customer role does not
2174
+ is not one, so it is reported as what it is: the signed-in user's ROLE is
2175
+ short `notes:write`, which a new project's default user role does not
2100
2176
  carry.
2101
2177
 
2102
2178
  Screenshot capture is offered only when the project allows it and the browser
@@ -2126,11 +2202,11 @@ note above.
2126
2202
  New exports on `./media`: `captureFoirScreen`, `isFoirScreenCaptureSupported`,
2127
2203
  `FoirScreenCapture`, `FoirScreenCaptureOptions`.
2128
2204
 
2129
- - **Note attachments and note config on `./data`.** `FoirCustomerNote` gains
2205
+ - **Note attachments and note config on `./data`.** `FoirUserNote` gains
2130
2206
  `attachments`, `FoirCreateMyNoteArgs` gains an optional `attachmentFileIds`,
2131
2207
  and `FoirNoteConfig` is the shape `foir.ns.notes.noteConfig()` answers with.
2132
2208
 
2133
- New exports on `./data`: `FoirCustomerNoteAttachment`, `FoirNoteConfig`.
2209
+ New exports on `./data`: `FoirUserNoteAttachment`, `FoirNoteConfig`.
2134
2210
 
2135
2211
  - **A subject registry — the records a client fetched, so an annotation can
2136
2212
  anchor to the record instead of to a URL.** `createFoirSubjectRegistry()`
@@ -2174,11 +2250,11 @@ note above.
2174
2250
  `FoirSubject`, `FoirSubjectEntry`, `FoirSubjectOptions`, `FoirSubjectOrigin`,
2175
2251
  `FoirSubjectRegistry`, `FoirRecordPath`.
2176
2252
 
2177
- - **Types for the customer notes surface.** `foir.ns.notes.myNotes()` and
2253
+ - **Types for the user notes surface.** `foir.ns.notes.myNotes()` and
2178
2254
  `foir.ns.notes.createMyNote()` already worked — `notes` is a namespace the
2179
2255
  manifest publishes like any other — but their shapes had to be written out by
2180
2256
  hand at each call site. They are platform-owned and identical in every project,
2181
- so they now ship: `FoirCustomerNote`, `FoirMyNotesArgs`, `FoirMyNotesResult`,
2257
+ so they now ship: `FoirUserNote`, `FoirMyNotesArgs`, `FoirMyNotesResult`,
2182
2258
  `FoirCreateMyNoteArgs`, `FoirNoteSubjectType`.
2183
2259
 
2184
2260
  ```ts
@@ -2186,7 +2262,7 @@ note above.
2186
2262
  ```
2187
2263
 
2188
2264
  `FoirNoteSubjectType` is the closed set the platform's write path enforces
2189
- (`record`, `file`, `model`, `operation`, `schedule`, `customer`) and is never a
2265
+ (`record`, `file`, `model`, `operation`, `schedule`, `user`) and is never a
2190
2266
  model key — a note on a record is `'record'` plus the key on
2191
2267
  `subjectModelKey`. Like every mirrored platform table in `builtins.ts`, a kind
2192
2268
  added upstream widens this union in a minor release.
@@ -2343,12 +2419,12 @@ note above.
2343
2419
  `FoirEventsClientConfig`, `FoirEventsListParams`, `FoirEventsStreamParams`.
2344
2420
 
2345
2421
  **On `@foir/sdk/server` and no other subpath, deliberately.** The event log is
2346
- an OPERATOR surface (DECIDED 2026-08-04): reachable by an `sk_` key or
2347
- an admin session, never by a customer token, and `events:list.project` is not
2422
+ an USER surface (DECIDED 2026-08-04): reachable by an `sk_` key or
2423
+ a console session, never by a user token, and `events:list.project` is not
2348
2424
  mintable on a `pk_` key at all. `events.view` is plain tenant+project
2349
2425
  containment while `record.view` carries owner/grantee/borrow terms on top, so a
2350
2426
  containment-only read over the log describes rows whose records the same caller
2351
- could not read — and each row's `actor` names which *other* customer acted.
2427
+ could not read — and each row's `actor` names which *other* user acted.
2352
2428
  `createEventsClient` therefore throws on a `pk_` key and on an `Authorization`
2353
2429
  header rather than letting either reach the wire, and an import-graph test
2354
2430
  fails if the module becomes reachable from a subpath that ships to a browser.
@@ -2369,12 +2445,12 @@ note above.
2369
2445
  is the entire cost of GraphQL paid for one screen. Ten root fields closed:
2370
2446
 
2371
2447
  `getAuthConfig`, `getDefaultAuthProvider`, `currentUser`, `myProviderToken`,
2372
- `customerLogout`, `customerRequestPasswordReset`, `customerResetPassword`,
2373
- `customerUpdatePassword`, `customerVerifyEmail`, `deleteMyAccount`.
2448
+ `logout`, `requestPasswordReset`, `resetPassword`,
2449
+ `updatePassword`, `verifyEmail`, `deleteMyAccount`.
2374
2450
 
2375
2451
  Plus the session-layer pairings for the flows that need one:
2376
2452
  `resetPasswordAndSignIn` (redeem the emailed token and persist the session it
2377
- mints — the peer of `loginWithPassword` for the branch where the customer
2453
+ mints — the peer of `loginWithPassword` for the branch where the user
2378
2454
  cannot log in), `signOut` (revoke server-side, then clear locally, and clear
2379
2455
  locally even if the revoke fails), and `authedOp` (`authedRequest`'s twin for
2380
2456
  auth ops: refresh once, retry once, on a 401-shaped failure).
@@ -2391,10 +2467,10 @@ note above.
2391
2467
  ### Removed
2392
2468
 
2393
2469
  - **The nine auth operation documents on `@foir/sdk/auth`** —
2394
- `CustomerLoginDocument`, `CustomerRegisterDocument`,
2395
- `CustomerRefreshTokenDocument`, `CustomerRequestOTPDocument`,
2396
- `CustomerLoginOTPDocument`, `CustomerLoginWithProviderDocument`,
2397
- `CustomerProviderCallbackDocument`, `CustomerProviderVerifyOTPDocument`,
2470
+ `UserLoginDocument`, `UserRegisterDocument`,
2471
+ `UserRefreshTokenDocument`, `UserRequestOTPDocument`,
2472
+ `UserLoginOTPDocument`, `UserLoginWithProviderDocument`,
2473
+ `UserProviderCallbackDocument`, `UserProviderVerifyOTPDocument`,
2398
2474
  `AuthProvidersDocument`.
2399
2475
 
2400
2476
  **Breaking, so the next release is a MINOR** per the pre-1.0 note above.
@@ -2406,15 +2482,15 @@ note above.
2406
2482
  eleven were unreachable. The flows now address the server's generated `auth`
2407
2483
  namespace, so `@foir/sdk/auth` carries no transport of its own.
2408
2484
 
2409
- *Migration.* Nothing that called a FUNCTION changes: `customerLogin(env, …)`,
2485
+ *Migration.* Nothing that called a FUNCTION changes: `login(env, …)`,
2410
2486
  `getAuthProviders(env)`, `loginWithPassword(adapter, env, …)`, `refreshSession`,
2411
2487
  `requireAuth`, `authedRequest` and every React hook in `@foir/sdk/react` keep
2412
2488
  their exact signatures. Only code importing a `*Document` const is affected,
2413
2489
  and only if it was executing the document itself through its own GraphQL
2414
2490
  client. Two options:
2415
2491
 
2416
- - call the SDK function instead (`customerLogin(env, vars)` rather than running
2417
- `CustomerLoginDocument` through Apollo/urql), which is what the exports were
2492
+ - call the SDK function instead (`login(env, vars)` rather than running
2493
+ `UserLoginDocument` through Apollo/urql), which is what the exports were
2418
2494
  for; or
2419
2495
  - keep your own client and write the document in your own codebase — these are
2420
2496
  fixed-shape platform operations, so the text does not vary by project.
@@ -2478,7 +2554,7 @@ actually happened.
2478
2554
 
2479
2555
  Purely additive: `./auth` keeps exporting all four, unchanged, and nothing is
2480
2556
  moved or renamed. What changes is the cost of reaching them. `./auth` also
2481
- exports the customer-auth GraphQL documents, which build their
2557
+ exports the user-auth GraphQL documents, which build their
2482
2558
  TypedDocumentNodes with a top-level `parse()` that no bundler can treat as
2483
2559
  pure — so a browser app importing only `createFoirSessionClient` was shipping
2484
2560
  the whole `graphql` package to get it. `src/auth/session-client.ts` imports
@@ -2539,7 +2615,7 @@ on its own, which is the intended behaviour for a release that adds exports.
2539
2615
 
2540
2616
  ### Added
2541
2617
 
2542
- - `@foir/sdk/auth`: `customerSignupContext` and `customerJoinWaitlist`, plus the
2618
+ - `@foir/sdk/auth`: `signupContext` and `joinWaitlist`, plus the
2543
2619
  `SignupContext`, `SignupMode` and `WaitlistJoinResult` types — waitlist and
2544
2620
  restricted-signup flows for projects that are not open-registration.
2545
2621
  - A pinned public-export surface: `public-exports.snapshot.json` plus
@@ -2566,7 +2642,7 @@ on its own, which is the intended behaviour for a release that adds exports.
2566
2642
 
2567
2643
  ### Added
2568
2644
 
2569
- - `@foir/sdk/client`: `createFoirContentClient` — forwards the customer bearer
2645
+ - `@foir/sdk/client`: `createFoirContentClient` — forwards the user bearer
2570
2646
  token on content reads, so authenticated and segment-targeted content works
2571
2647
  without hand-rolling the header.
2572
2648
 
@@ -2583,7 +2659,7 @@ on its own, which is the intended behaviour for a release that adds exports.
2583
2659
 
2584
2660
  ### Added
2585
2661
 
2586
- - **New subpath `@foir/sdk/react`** — drop-in React customer auth: `FoirProvider`,
2662
+ - **New subpath `@foir/sdk/react`** — drop-in React user auth: `FoirProvider`,
2587
2663
  `SignIn`, `OAuthButtons`, `UserButton`, `useFoirAuth`, `useFoirSession`,
2588
2664
  `useFoirUser`.
2589
2665
  - **New subpath `@foir/sdk/next`** — Next.js App Router BFF auth adapter: cookie
@@ -2611,7 +2687,7 @@ First public release. Ten subpaths:
2611
2687
  - `@foir/sdk/media` — progressive media components and adapters.
2612
2688
  - `@foir/sdk/content` — `createContentRenderer` and `RichTextRenderer` with
2613
2689
  injected typography tokens.
2614
- - `@foir/sdk/auth` — customer auth flows, token lifecycle, hosted-login bridge.
2690
+ - `@foir/sdk/auth` — user auth flows, token lifecycle, hosted-login bridge.
2615
2691
  - `@foir/sdk/realtime` — `createCollabSession`, a thin y-websocket helper.
2616
2692
  - `@foir/sdk/codegen`, `@foir/sdk/codegen/block-registry`,
2617
2693
  `@foir/sdk/codegen/block-scaffold` — the `foirCodegen` preset and the