@fleetless/contracts 5.2.0 → 6.0.0-next.1

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/artifacts/openapi.json +1975 -415
  3. package/artifacts/routes.json +1697 -251
  4. package/artifacts/schema/accept-team-invite-request.schema.json +13 -7
  5. package/artifacts/schema/app-auth-config.schema.json +108 -4
  6. package/artifacts/schema/app-hosted-pages.schema.json +33 -0
  7. package/artifacts/schema/app-invitation.schema.json +2 -2
  8. package/artifacts/schema/app-mail-template-list-response.schema.json +4 -3
  9. package/artifacts/schema/app-mail-template.schema.json +3 -2
  10. package/artifacts/schema/app-sign-in-methods.schema.json +19 -0
  11. package/artifacts/schema/app-user-list-response.schema.json +36 -0
  12. package/artifacts/schema/app-user.schema.json +36 -0
  13. package/artifacts/schema/auth-me-response.schema.json +27 -0
  14. package/artifacts/schema/auth-ok.schema.json +13 -1
  15. package/artifacts/schema/client-accept-invitation-request.schema.json +3 -4
  16. package/artifacts/schema/client-identity.schema.json +13 -1
  17. package/artifacts/schema/client-login-code-request.schema.json +24 -0
  18. package/artifacts/schema/client-login-code-verify-request.schema.json +30 -0
  19. package/artifacts/schema/client-provider-list-response.schema.json +22 -2
  20. package/artifacts/schema/client-register-request.schema.json +3 -4
  21. package/artifacts/schema/client-sign-in-result.schema.json +55 -0
  22. package/artifacts/schema/client-two-factor-disable-request.schema.json +15 -0
  23. package/artifacts/schema/client-two-factor-setup-confirm-request.schema.json +20 -0
  24. package/artifacts/schema/client-two-factor-setup-confirm-response.schema.json +49 -0
  25. package/artifacts/schema/client-two-factor-setup-request.schema.json +12 -0
  26. package/artifacts/schema/client-two-factor-verify-request.schema.json +25 -0
  27. package/artifacts/schema/create-app-invitation-request.schema.json +1 -1
  28. package/artifacts/schema/create-passkey-request.schema.json +25 -0
  29. package/artifacts/schema/create-passkey-response.schema.json +84 -0
  30. package/artifacts/schema/developer-passkey.schema.json +56 -0
  31. package/artifacts/schema/developer-two-factor.schema.json +105 -0
  32. package/artifacts/schema/fleetless-user-list-response.schema.json +22 -0
  33. package/artifacts/schema/fleetless-user.schema.json +22 -0
  34. package/artifacts/schema/invalid-code-details.schema.json +16 -0
  35. package/artifacts/schema/org.schema.json +5 -0
  36. package/artifacts/schema/patch-org-request.schema.json +5 -3
  37. package/artifacts/schema/patch-org-response.schema.json +5 -0
  38. package/artifacts/schema/put-app-auth-look-request.schema.json +22 -0
  39. package/artifacts/schema/put-app-auth-mcp-request.schema.json +1 -14
  40. package/artifacts/schema/put-app-auth-sign-in-request.schema.json +39 -0
  41. package/artifacts/schema/put-app-auth-urls-request.schema.json +31 -4
  42. package/artifacts/schema/recovery-codes-response.schema.json +20 -0
  43. package/artifacts/schema/rename-passkey-request.schema.json +16 -0
  44. package/artifacts/schema/totp-confirm-request.schema.json +15 -0
  45. package/artifacts/schema/totp-confirm-response.schema.json +27 -0
  46. package/artifacts/schema/two-factor-challenge.schema.json +24 -0
  47. package/artifacts/schema/two-factor-setup-response.schema.json +21 -0
  48. package/artifacts/schema/webauthn-options-response.schema.json +18 -0
  49. package/dist/app-users.d.ts +154 -34
  50. package/dist/app-users.js +177 -45
  51. package/dist/client-auth.d.ts +133 -24
  52. package/dist/client-auth.js +139 -28
  53. package/dist/config.d.ts +2 -2
  54. package/dist/errors.d.ts +10 -1
  55. package/dist/errors.js +34 -5
  56. package/dist/identity.d.ts +172 -123
  57. package/dist/identity.js +189 -101
  58. package/dist/index.d.ts +9 -9
  59. package/dist/index.js +11 -7
  60. package/dist/protocol.d.ts +1 -1
  61. package/dist/realtime.d.ts +1 -0
  62. package/dist/rest.d.ts +2 -2
  63. package/dist/rest.js +17 -28
  64. package/dist/routes.d.ts +19 -0
  65. package/dist/routes.js +497 -183
  66. package/package.json +1 -1
  67. package/artifacts/schema/developer-login-request.schema.json +0 -19
  68. package/artifacts/schema/password-reset-confirm.schema.json +0 -19
  69. package/artifacts/schema/password-reset-request.schema.json +0 -14
  70. package/artifacts/schema/sign-up-request.schema.json +0 -26
  71. package/artifacts/schema/sign-up-response.schema.json +0 -127
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "sign_in_methods": {
6
+ "type": "object",
7
+ "properties": {
8
+ "password": {
9
+ "type": "boolean",
10
+ "description": "Whether app users may sign in with a password. Off refuses `POST /api/client/login` with `method_not_allowed`, and registration and invitations then take no password."
11
+ },
12
+ "email_code": {
13
+ "type": "boolean",
14
+ "description": "Whether app users may sign in with a six-digit code mailed to them, valid ten minutes. A code needs no URL, so it works in local development and in an app with no web UI."
15
+ }
16
+ },
17
+ "required": [
18
+ "password",
19
+ "email_code"
20
+ ],
21
+ "additionalProperties": false,
22
+ "description": "Which sign-in methods the app offers: password, emailed code, or both — at least one. Identity providers stay on top of either. The default is password only."
23
+ },
24
+ "two_factor": {
25
+ "type": "string",
26
+ "enum": [
27
+ "off",
28
+ "optional",
29
+ "required"
30
+ ],
31
+ "description": "Whether the app asks for an authenticator code: `off` (the default), `optional` or `required`. A person with a confirmed authenticator is asked at every sign-in whatever the policy; a sign-in through an identity provider is never asked."
32
+ }
33
+ },
34
+ "required": [
35
+ "sign_in_methods",
36
+ "two_factor"
37
+ ],
38
+ "additionalProperties": false
39
+ }
@@ -2,6 +2,19 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "type": "object",
4
4
  "properties": {
5
+ "app_url": {
6
+ "anyOf": [
7
+ {
8
+ "type": "string",
9
+ "maxLength": 500,
10
+ "format": "uri"
11
+ },
12
+ {
13
+ "type": "null"
14
+ }
15
+ ],
16
+ "description": "The app's own home page, linked as `Open <app>` when a hosted flow is done. `null` makes the hosted done page say `You can close this tab`."
17
+ },
5
18
  "invite_url": {
6
19
  "anyOf": [
7
20
  {
@@ -12,7 +25,7 @@
12
25
  "type": "null"
13
26
  }
14
27
  ],
15
- "description": "The page in the developer's app that accepts an invitation, with `{token}` where the token goes. `null` when unconfigured, and then an invitation still issues but `send_mail` is refused with `409 target_state_conflict` — there would be nowhere for the link to point."
28
+ "description": "The page in the developer's app that accepts an invitation, with `{token}` where the token goes. `null` means the hosted page in `hosted_pages` is used."
16
29
  },
17
30
  "verify_url": {
18
31
  "anyOf": [
@@ -24,7 +37,7 @@
24
37
  "type": "null"
25
38
  }
26
39
  ],
27
- "description": "The page that confirms a new address, with `{token}` where the token goes. Self-registration needs it: without a page to send people to, a registration would leave an account nobody can activate."
40
+ "description": "The page that confirms a new address, with `{token}` where the token goes. `null` means the hosted page in `hosted_pages` is used."
28
41
  },
29
42
  "reset_url": {
30
43
  "anyOf": [
@@ -36,13 +49,27 @@
36
49
  "type": "null"
37
50
  }
38
51
  ],
39
- "description": "The page that takes a new password, with `{token}` where the token goes."
52
+ "description": "The page that takes a new password, with `{token}` where the token goes. `null` means the hosted page in `hosted_pages` is used."
53
+ },
54
+ "mcp_login_url": {
55
+ "anyOf": [
56
+ {
57
+ "type": "string",
58
+ "maxLength": 500
59
+ },
60
+ {
61
+ "type": "null"
62
+ }
63
+ ],
64
+ "description": "The page an MCP authorization redirects to, with `{interaction}` where the interaction id goes. Not a token: the id names a pending request the server already holds, and the app authenticates the user itself before approving it. `null` means the hosted MCP sign-in in `hosted_pages` is used."
40
65
  }
41
66
  },
42
67
  "required": [
68
+ "app_url",
43
69
  "invite_url",
44
70
  "verify_url",
45
- "reset_url"
71
+ "reset_url",
72
+ "mcp_login_url"
46
73
  ],
47
74
  "additionalProperties": false
48
75
  }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "recovery_codes": {
6
+ "minItems": 10,
7
+ "maxItems": 10,
8
+ "type": "array",
9
+ "items": {
10
+ "type": "string",
11
+ "pattern": "^[a-z2-7]{5}-[a-z2-7]{5}$"
12
+ },
13
+ "description": "The ten new recovery codes, lowercase, shown once. Every earlier code is void."
14
+ }
15
+ },
16
+ "required": [
17
+ "recovery_codes"
18
+ ],
19
+ "additionalProperties": false
20
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "name": {
6
+ "type": "string",
7
+ "minLength": 1,
8
+ "maxLength": 80,
9
+ "description": "The new name."
10
+ }
11
+ },
12
+ "required": [
13
+ "name"
14
+ ],
15
+ "additionalProperties": false
16
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "code": {
6
+ "type": "string",
7
+ "pattern": "^\\d{6}$",
8
+ "description": "A code the new authenticator shows now. It proves the secret was copied correctly before anything depends on it."
9
+ }
10
+ },
11
+ "required": [
12
+ "code"
13
+ ],
14
+ "additionalProperties": false
15
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "recovery_codes": {
6
+ "anyOf": [
7
+ {
8
+ "minItems": 10,
9
+ "maxItems": 10,
10
+ "type": "array",
11
+ "items": {
12
+ "type": "string",
13
+ "pattern": "^[a-z2-7]{5}-[a-z2-7]{5}$"
14
+ }
15
+ },
16
+ {
17
+ "type": "null"
18
+ }
19
+ ],
20
+ "description": "The ten recovery codes, shown once, when this is the account's first second factor; `null` otherwise."
21
+ }
22
+ },
23
+ "required": [
24
+ "recovery_codes"
25
+ ],
26
+ "additionalProperties": false
27
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "status": {
6
+ "type": "string",
7
+ "enum": [
8
+ "two_factor_required",
9
+ "two_factor_setup_required"
10
+ ],
11
+ "description": "`two_factor_required`: ask for the authenticator code. `two_factor_setup_required`: the app requires two-factor and the person has none yet, so set one up before any session exists."
12
+ },
13
+ "challenge": {
14
+ "type": "string",
15
+ "minLength": 1,
16
+ "description": "The handle the next step spends. Valid five minutes; afterwards it answers `410 token_spent` and the sign-in starts over."
17
+ }
18
+ },
19
+ "required": [
20
+ "status",
21
+ "challenge"
22
+ ],
23
+ "additionalProperties": false
24
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "secret": {
6
+ "type": "string",
7
+ "minLength": 1,
8
+ "description": "The shared secret, base32, for an authenticator app that cannot scan a QR code. Shown once; the cloud stores it encrypted."
9
+ },
10
+ "otpauth_url": {
11
+ "type": "string",
12
+ "pattern": "^otpauth:\\/\\/totp\\/.*",
13
+ "description": "The same secret as an `otpauth://totp/` URL, to render as a QR code. It carries the secret: never log it."
14
+ }
15
+ },
16
+ "required": [
17
+ "secret",
18
+ "otpauth_url"
19
+ ],
20
+ "additionalProperties": false
21
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "options": {
6
+ "type": "object",
7
+ "propertyNames": {
8
+ "type": "string"
9
+ },
10
+ "additionalProperties": {},
11
+ "description": "The `PublicKeyCredentialCreationOptionsJSON` or `PublicKeyCredentialRequestOptionsJSON` to pass to the browser. Its challenge is single-use and short-lived."
12
+ }
13
+ },
14
+ "required": [
15
+ "options"
16
+ ],
17
+ "additionalProperties": false
18
+ }
@@ -14,19 +14,22 @@ import { z } from 'zod';
14
14
  * So there are now **two identity spaces and nothing joins them**:
15
15
  *
16
16
  * - *Fleetless users* (`identity.ts`) — the org's team. Email globally unique,
17
- * tier `owner | developer`, Fleetless password, console access.
17
+ * tier `owner | developer`, sign-in by emailed code or passkey, console
18
+ * access.
18
19
  * - *app users* (this file) — one app each. Email unique **per app**,
19
20
  * case-insensitively. The same address may exist in several apps of one org
20
21
  * as unrelated accounts, and a Fleetless user who wants to use an app
21
22
  * registers or is invited like anybody else.
22
23
  *
23
- * **Fleetless shows an app user no page**. The developer's own UI owns
24
- * every screen and calls the JSON client-auth API (`client-auth.ts`). The one
25
- * Fleetless-rendered surface an app user can reach is the problem page for an
26
- * OIDC callback whose state no longer resolves to a redirect URI — every other
27
- * error is redirected to the app to render. That is why the four URLs on
28
- * `appAuthConfig` exist: Fleetless mails a link, and the link points into the
29
- * app.
24
+ * **The developer's own UI owns every screen it wants to own**, and calls the
25
+ * JSON client-auth API (`client-auth.ts`). Fleetless mails a link, and the
26
+ * link points into the app — at the four URLs on `appAuthConfig`. **A URL the
27
+ * app leaves unset falls back to a Fleetless-hosted page** on the auth portal
28
+ * (`hosted_pages`), carrying the app's name, its optional logo and accent
29
+ * colour, so an app works from its first minute: with no web UI of its own,
30
+ * and while its pages point nowhere yet. A set URL always wins. The hosted
31
+ * pages cover mailed links and MCP sign-in only; they are not a hosted login
32
+ * for the app's own web UI.
30
33
  */
31
34
  /** App-user display names share the Fleetless-user bound, so a rename cannot be legal in one space and refused in the other. */
32
35
  export declare const APP_USER_DISPLAY_NAME_MAX = 120;
@@ -84,6 +87,11 @@ export declare const appUser: z.ZodObject<{
84
87
  has_password: z.ZodBoolean;
85
88
  providers: z.ZodArray<z.ZodString>;
86
89
  last_login_at: z.ZodNullable<z.ZodISODateTime>;
90
+ two_factor: z.ZodObject<{
91
+ enabled: z.ZodBoolean;
92
+ enabled_at: z.ZodNullable<z.ZodISODateTime>;
93
+ recovery_codes_left: z.ZodNumber;
94
+ }, z.core.$strip>;
87
95
  created_at: z.ZodISODateTime;
88
96
  }, z.core.$strip>;
89
97
  export type AppUser = z.infer<typeof appUser>;
@@ -103,6 +111,11 @@ export declare const appUserListResponse: z.ZodObject<{
103
111
  has_password: z.ZodBoolean;
104
112
  providers: z.ZodArray<z.ZodString>;
105
113
  last_login_at: z.ZodNullable<z.ZodISODateTime>;
114
+ two_factor: z.ZodObject<{
115
+ enabled: z.ZodBoolean;
116
+ enabled_at: z.ZodNullable<z.ZodISODateTime>;
117
+ recovery_codes_left: z.ZodNumber;
118
+ }, z.core.$strip>;
106
119
  created_at: z.ZodISODateTime;
107
120
  }, z.core.$strip>>;
108
121
  }, z.core.$strip>;
@@ -162,12 +175,11 @@ export type CreateAppInvitationRequest = z.infer<typeof createAppInvitationReque
162
175
  /**
163
176
  * The invitation as issued.
164
177
  *
165
- * **`accept_url` is nullable, and that is a policy rather than a convenience.**
166
- * The link points into the developer's app, at their configured `invite_url`.
167
- * An app that has configured none has nowhere for it to point, so there is no
168
- * link to hand back — `null` says that outright, where an absent key would be
169
- * indistinguishable from a mapper that dropped the field and a fabricated
170
- * Fleetless-hosted URL would name a page this product does not serve.
178
+ * The link points into the developer's app, at their configured `invite_url`,
179
+ * or at the Fleetless-hosted invitation page (`appAuthConfig.hosted_pages`)
180
+ * when the app has configured none. **`accept_url` stays nullable** so a
181
+ * reader written against the earlier shape keeps parsing; the cloud fills it
182
+ * in every case now that a hosted page always exists.
171
183
  */
172
184
  export declare const appInvitation: z.ZodObject<{
173
185
  id: z.ZodUUID;
@@ -371,6 +383,65 @@ export declare const allowedOrigin: z.ZodString;
371
383
  * name limit.
372
384
  */
373
385
  export declare const emailDomain: z.ZodString;
386
+ /**
387
+ * **How an app's users sign in: by password, by emailed code, or both.**
388
+ *
389
+ * At least one is on — an app with neither would have no door but its
390
+ * identity providers, and a provider can be disabled. Identity providers are
391
+ * not part of this choice; they stay on top of whichever methods are on. A
392
+ * method turned off refuses its routes with `method_not_allowed`; a stored
393
+ * password stays stored, so turning the method back on restores it.
394
+ */
395
+ export declare const appSignInMethods: z.ZodObject<{
396
+ password: z.ZodBoolean;
397
+ email_code: z.ZodBoolean;
398
+ }, z.core.$strict>;
399
+ export type AppSignInMethods = z.infer<typeof appSignInMethods>;
400
+ /**
401
+ * **Whether an app asks its users for a second factor**, an authenticator
402
+ * app (TOTP) with ten single-use recovery codes.
403
+ *
404
+ * - `off` — nobody is asked to set one up, and nobody can.
405
+ * - `optional` — people turn it on in the app's own account settings.
406
+ * - `required` — a person without one sets it up at their next sign-in,
407
+ * before any session exists. Nobody is signed out when it is switched on.
408
+ *
409
+ * Whatever the policy, a person who **has** a confirmed authenticator is asked
410
+ * for a code at every sign-in that yields a session. A sign-in through an
411
+ * identity provider is never asked: the provider owns that sign-in.
412
+ */
413
+ export declare const appTwoFactorPolicy: z.ZodEnum<{
414
+ optional: "optional";
415
+ required: "required";
416
+ off: "off";
417
+ }>;
418
+ export type AppTwoFactorPolicy = z.infer<typeof appTwoFactorPolicy>;
419
+ /**
420
+ * **The app's own home page**: https, or http on `localhost`/`127.0.0.1` —
421
+ * the host rule `appUrlTemplate` keeps — and no placeholder, because nothing
422
+ * is substituted into it. The hosted pages link to it as `Open <app>` when a
423
+ * flow is done.
424
+ */
425
+ export declare const appHomeUrl: z.ZodURL;
426
+ /** The accent colour of the hosted pages: `#rrggbb`, lowercase, the one spelling the renderer compares against. */
427
+ export declare const hostedAccent: z.ZodString;
428
+ /** The largest logo the hosted pages take, in bytes: 100 KB. */
429
+ export declare const HOSTED_LOGO_MAX_BYTES = 102400;
430
+ /** The logo types the hosted pages take. An SVG is served sandboxed and embedded only as an image. */
431
+ export declare const HOSTED_LOGO_TYPES: readonly ["image/png", "image/svg+xml"];
432
+ /**
433
+ * **The Fleetless-hosted pages an unset URL falls back to**, one per URL
434
+ * field, as templates with the same placeholder the field takes. Read-only:
435
+ * they are minted by the cloud from the auth portal's base URL and the app's
436
+ * identifier.
437
+ */
438
+ export declare const appHostedPages: z.ZodObject<{
439
+ invite_url: z.ZodURL;
440
+ verify_url: z.ZodURL;
441
+ reset_url: z.ZodURL;
442
+ mcp_login_url: z.ZodURL;
443
+ }, z.core.$strip>;
444
+ export type AppHostedPages = z.infer<typeof appHostedPages>;
374
445
  /**
375
446
  * **The app's auth settings: one row per app, configured by a Fleetless user.**
376
447
  *
@@ -380,11 +451,11 @@ export declare const emailDomain: z.ZodString;
380
451
  * a developer inviting somebody by hand has already made the decision the
381
452
  * whitelist automates.
382
453
  *
383
- * The four URLs are what makes that work: Fleetless mails a link, and the link
384
- * points into the developer's app. An app that has configured none of them
385
- * still works for password login — it simply cannot send a mail that leads
386
- * anywhere, and `send_mail` is refused rather than silently sending a dead
387
- * link.
454
+ * The four URLs point Fleetless's mails and the MCP sign-in into the
455
+ * developer's app. **Each is optional**: an unset one falls back to the
456
+ * Fleetless-hosted page in `hosted_pages`, so nothing is refused for a
457
+ * missing URL — self-registration, mailed invitations, resets and MCP sign-in
458
+ * all work before the app has a page of its own.
388
459
  */
389
460
  export declare const appAuthConfig: z.ZodObject<{
390
461
  self_registration: z.ZodBoolean;
@@ -395,6 +466,24 @@ export declare const appAuthConfig: z.ZodObject<{
395
466
  verify_url: z.ZodNullable<z.ZodString>;
396
467
  reset_url: z.ZodNullable<z.ZodString>;
397
468
  mcp_login_url: z.ZodNullable<z.ZodString>;
469
+ app_url: z.ZodNullable<z.ZodURL>;
470
+ sign_in_methods: z.ZodObject<{
471
+ password: z.ZodBoolean;
472
+ email_code: z.ZodBoolean;
473
+ }, z.core.$strict>;
474
+ two_factor: z.ZodEnum<{
475
+ optional: "optional";
476
+ required: "required";
477
+ off: "off";
478
+ }>;
479
+ hosted_logo_url: z.ZodNullable<z.ZodURL>;
480
+ hosted_accent: z.ZodNullable<z.ZodString>;
481
+ hosted_pages: z.ZodObject<{
482
+ invite_url: z.ZodURL;
483
+ verify_url: z.ZodURL;
484
+ reset_url: z.ZodURL;
485
+ mcp_login_url: z.ZodURL;
486
+ }, z.core.$strip>;
398
487
  oidc_callback_url: z.ZodURL;
399
488
  updated_at: z.ZodISODateTime;
400
489
  }, z.core.$strip>;
@@ -403,8 +492,8 @@ export type AppAuthConfig = z.infer<typeof appAuthConfig>;
403
492
  * `PUT /api/apps/:id/auth-config/registration` — who may get in, and from
404
493
  * where.
405
494
  *
406
- * Three slices rather than one document, and each still a **replace** with
407
- * every field of its slice required: three screens carving up one
495
+ * Several slices rather than one document, and each still a **replace** with
496
+ * every field of its slice required: several screens carving up one
408
497
  * all-required request is how a field nobody's screen shows becomes a field
409
498
  * somebody's save clears. The slice states its own ownership, so a new field
410
499
  * lands in one schema and one screen.
@@ -418,33 +507,59 @@ export declare const putAppAuthRegistrationRequest: z.ZodObject<{
418
507
  allowed_origins: z.ZodArray<z.ZodString>;
419
508
  }, z.core.$strict>;
420
509
  export type PutAppAuthRegistrationRequest = z.infer<typeof putAppAuthRegistrationRequest>;
421
- /** `PUT /api/apps/:id/auth-config/urls` — the three pages Fleetless's mails point at. */
510
+ /** `PUT /api/apps/:id/auth-config/sign-in` — how the app's users sign in, and whether they give a second factor. */
511
+ export declare const putAppAuthSignInRequest: z.ZodObject<{
512
+ two_factor: z.ZodEnum<{
513
+ optional: "optional";
514
+ required: "required";
515
+ off: "off";
516
+ }>;
517
+ sign_in_methods: z.ZodObject<{
518
+ password: z.ZodBoolean;
519
+ email_code: z.ZodBoolean;
520
+ }, z.core.$strict>;
521
+ }, z.core.$strict>;
522
+ export type PutAppAuthSignInRequest = z.infer<typeof putAppAuthSignInRequest>;
523
+ /**
524
+ * `PUT /api/apps/:id/auth-config/urls` — the app's home page and the four
525
+ * pages Fleetless's mails and the MCP sign-in point at. One slice, because
526
+ * the console's Pages section owns all five; each `null` falls back to the
527
+ * hosted page.
528
+ */
422
529
  export declare const putAppAuthUrlsRequest: z.ZodObject<{
423
530
  invite_url: z.ZodNullable<z.ZodString>;
424
531
  verify_url: z.ZodNullable<z.ZodString>;
425
532
  reset_url: z.ZodNullable<z.ZodString>;
533
+ mcp_login_url: z.ZodNullable<z.ZodString>;
534
+ app_url: z.ZodNullable<z.ZodURL>;
426
535
  }, z.core.$strict>;
427
536
  export type PutAppAuthUrlsRequest = z.infer<typeof putAppAuthUrlsRequest>;
428
537
  /**
429
- * `PUT /api/apps/:id/auth-config/mcp` — the switch and the login URL, which
430
- * belong together: on without a URL refuses every sign-in, in the MCP
431
- * client's browser mid-OAuth, where no console screen ever sees it.
538
+ * `PUT /api/apps/:id/auth-config/mcp` — the switch alone. Its login URL moved
539
+ * to the `urls` slice: on without a URL no longer refuses anything, because
540
+ * the hosted MCP sign-in stands in for it.
432
541
  */
433
542
  export declare const putAppAuthMcpRequest: z.ZodObject<{
434
543
  mcp_enabled: z.ZodBoolean;
435
- mcp_login_url: z.ZodNullable<z.ZodString>;
436
544
  }, z.core.$strict>;
437
545
  export type PutAppAuthMcpRequest = z.infer<typeof putAppAuthMcpRequest>;
546
+ /** `PUT /api/apps/:id/auth-config/look` — the hosted pages' accent colour. The logo is its own write, a raw image body. */
547
+ export declare const putAppAuthLookRequest: z.ZodObject<{
548
+ hosted_accent: z.ZodNullable<z.ZodString>;
549
+ }, z.core.$strict>;
550
+ export type PutAppAuthLookRequest = z.infer<typeof putAppAuthLookRequest>;
438
551
  /**
439
- * The three mails a developer may replace with their own template.
440
- * Mails to *Fleetless* users — a team invitation, a console password reset —
441
- * stay Fleetless default and are deliberately not customisable: they are
442
- * about this platform, not about the developer's product.
552
+ * The four mails a developer may replace with their own template: the
553
+ * invitation, the address confirmation, the password reset and the sign-in
554
+ * code. Mails to *Fleetless* users — a team invitation, a console sign-in
555
+ * code — stay Fleetless default and are deliberately not customisable: they
556
+ * are about this platform, not about the developer's product.
443
557
  */
444
558
  export declare const mailTemplateKind: z.ZodEnum<{
445
559
  invite: "invite";
446
560
  verify: "verify";
447
561
  reset: "reset";
562
+ login_code: "login_code";
448
563
  }>;
449
564
  export type MailTemplateKind = z.infer<typeof mailTemplateKind>;
450
565
  /**
@@ -456,9 +571,9 @@ export type MailTemplateKind = z.infer<typeof mailTemplateKind>;
456
571
  * where the renderer, the console's completion and the docs all read the same
457
572
  * one.
458
573
  */
459
- export declare const MAIL_TEMPLATE_VARIABLES: readonly ["app.name", "org.name", "user.email", "user.display_name", "role.name", "link", "expires_in_hours"];
574
+ export declare const MAIL_TEMPLATE_VARIABLES: readonly ["app.name", "org.name", "user.email", "user.display_name", "role.name", "link", "expires_in_hours", "code", "expires_in_minutes"];
460
575
  /**
461
- * **The Fleetless default text for the three app mails.**
576
+ * **The Fleetless default text for the four app mails.**
462
577
  *
463
578
  * It lives here rather than in the cloud because two products send the same
464
579
  * words: the cloud renders these when an app has no template of its own, and
@@ -492,12 +607,15 @@ export declare const MAIL_TEMPLATE_VARIABLES: readonly ["app.name", "org.name",
492
607
  * that one is written by an authenticated developer about somebody they
493
608
  * invited.
494
609
  *
495
- * **`expires_in_hours` is the only lifetime variable a template gets**, and
610
+ * **`expires_in_hours` is the lifetime variable of the three link mails**, and
496
611
  * the three values are 1, 24 and 168. "The next 168 hours" is not how a person
497
612
  * says a week, so each default converts: 48 and up reads in days, exactly one
498
613
  * reads "1 hour", everything else reads in hours. The conversion is in the
499
614
  * template rather than in a new variable because a custom template has the
500
- * same problem and this is the spelling it can copy.
615
+ * same problem and this is the spelling it can copy. The sign-in code mail
616
+ * carries no link: it names the `code` and its lifetime in
617
+ * `expires_in_minutes` (10), and greets nobody, since whoever asked for it
618
+ * typed the address unauthenticated.
501
619
  *
502
620
  * **What contracts does NOT assert about these.** That they compile as Liquid
503
621
  * is the cloud's business — contracts has no renderer and adding one to check
@@ -520,6 +638,7 @@ export declare const appMailTemplate: z.ZodObject<{
520
638
  invite: "invite";
521
639
  verify: "verify";
522
640
  reset: "reset";
641
+ login_code: "login_code";
523
642
  }>;
524
643
  subject: z.ZodString;
525
644
  text: z.ZodString;
@@ -534,6 +653,7 @@ export declare const appMailTemplateListResponse: z.ZodObject<{
534
653
  invite: "invite";
535
654
  verify: "verify";
536
655
  reset: "reset";
656
+ login_code: "login_code";
537
657
  }>;
538
658
  subject: z.ZodString;
539
659
  text: z.ZodString;