@jskit-ai/agent-docs 0.1.238 → 0.1.240

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 (51) hide show
  1. package/guide/agent/app-extras/assistant-conversation.md +11 -6
  2. package/guide/agent/app-setup/authentication.md +28 -2
  3. package/guide/agent/app-setup/existing-application-migration.md +5 -3
  4. package/guide/agent/app-setup/initial-scaffolding.md +4 -1
  5. package/guide/agent/app-setup/run-an-accounts-hub.md +152 -0
  6. package/guide/agent/app-setup/sign-in-through-oidc-issuer.md +148 -0
  7. package/guide/agent/app-setup/upgrading-jskit.md +13 -0
  8. package/guide/agent/framework/application-operations.md +3 -0
  9. package/guide/agent/framework/ui-operations.md +24 -0
  10. package/guide/agent/index.md +2 -0
  11. package/package.json +1 -1
  12. package/patterns/feature-package/example/booking-engine/package.json +1 -1
  13. package/patterns/minimal-foundation/example/package.json +3 -3
  14. package/patterns/shell-foundation/example/package.json +4 -4
  15. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  16. package/reference/autogen/KERNEL_MAP.md +1 -0
  17. package/reference/autogen/PATTERN_INDEX.md +57 -35
  18. package/reference/autogen/README.md +2 -0
  19. package/reference/autogen/packages/auth-core.md +1 -0
  20. package/reference/autogen/packages/auth-provider-oidc-core.md +144 -0
  21. package/reference/autogen/packages/auth-web.md +16 -0
  22. package/reference/autogen/packages/kernel.md +6 -1
  23. package/reference/autogen/packages/oidc-issuer-core.md +201 -0
  24. package/reference/autogen/packages/shell-web.md +3 -0
  25. package/reference/autogen/tooling/jskit-release.md +1 -1
  26. package/skills/jskit/references/app-operations.md +3 -0
  27. package/skills/jskit/references/existing-application-migration.md +5 -3
  28. package/skills/jskit/references/pattern-index.md +57 -35
  29. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +3 -3
  30. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +4 -4
  31. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  32. package/skills/jskit/references/patterns/auth/accounts-hub/PATTERN.md +97 -0
  33. package/skills/jskit/references/patterns/auth/accounts-hub/example/.env.example +10 -0
  34. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +8 -0
  35. package/skills/jskit/references/patterns/auth/accounts-hub/example/server/hubPaths.js +7 -0
  36. package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +33 -11
  37. package/skills/jskit/references/patterns/auth/oidc-sign-in/PATTERN.md +87 -0
  38. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/.env.example +13 -0
  39. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +8 -0
  40. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  41. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
  42. package/skills/jskit/references/patterns/connectors/oauth-connection/PATTERN.md +25 -0
  43. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
  44. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
  45. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
  46. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  47. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
  48. package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +9 -0
  49. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
  50. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
  51. package/skills/jskit/references/ui-operations.md +24 -0
@@ -485,10 +485,14 @@ and the existing queuing behavior for a runtime that advertises steering.
485
485
  Applications may opt into `deferWhileWorking: true` with
486
486
  `queueWhileSending: true` for a conversation that cannot steer its active turn.
487
487
  Typed and retained voice submissions become pending in the original serial
488
- queue with their exact message ID, text, attachments and captured data. The
489
- existing subscription must report ready before each ordinary request reaches
490
- the API; normal server permissions still apply at dispatch. Show **Send** and
491
- the existing pending status, not **Steer**, when the runtime cannot steer.
488
+ queue with their exact message ID, text, attachments and captured data. Each
489
+ waits only while the existing subscription reports a working turn that cannot
490
+ take steering, and its pending status says so ("Pending: Waiting for the
491
+ assistant to finish this step"). When that working turn becomes steerable the
492
+ message is sent as steering, keeping the finished-turn fallback to one ordinary
493
+ Send; when the subscription reports ready it is sent as an ordinary request.
494
+ Normal server permissions still apply at dispatch. Show **Send** and the
495
+ pending status, not **Steer**, when the runtime cannot steer.
492
496
  Native steering and explicit retries keep their captured intent. Stop aborts
493
497
  these local pre-dispatch followers before stopping the original turn; actor
494
498
  retirement and lost access cannot dispatch them later. This option defaults to
@@ -2850,8 +2854,9 @@ Real account, environment, socket and provider recovery still runs normally.
2850
2854
  Native compaction remains the engine's job.
2851
2855
  JSKIT owns the local history adapter used for model-provider changes. It keeps
2852
2856
  native history intact and translates only outgoing requests. For qualified
2853
- DeepSeek and GLM models, an OpenAI encrypted compaction is supplemented with the
2854
- exact readable records from its saved native boundary. Recovery is bounded and
2857
+ models (`codexHistoryRouting`: DeepSeek V4.1 Flash, DeepSeek V4 Pro and GLM 5.3),
2858
+ an OpenAI encrypted compaction is supplemented with the exact readable records
2859
+ from its saved native boundary. Recovery is bounded and
2855
2860
  excludes only exact user-message copies retained by that same native boundary
2856
2861
  and still present before it in the outgoing request, counting each occurrence.
2857
2862
  Unmatched messages, tool results and images retain their original archive order.
@@ -17,6 +17,13 @@ For Supabase authentication:
17
17
  npm install @jskit-ai/auth-provider-supabase-core @jskit-ai/auth-web
18
18
  ```
19
19
 
20
+ To sign in through an accounts hub or another OpenID Connect issuer (see
21
+ [Sign in through an OIDC issuer](/guide/app-setup/sign-in-through-oidc-issuer)):
22
+
23
+ ```bash
24
+ npm install @jskit-ai/auth-provider-oidc-core @jskit-ai/auth-web
25
+ ```
26
+
20
27
  The dependency graph supplies `@jskit-ai/auth-core`. Do not install two
21
28
  providers for the same `auth.service` capability.
22
29
 
@@ -27,8 +34,27 @@ owns permitted login methods, visible OAuth providers, public routes,
27
34
  post-login destinations, surface access policy, and secret injection.
28
35
 
29
36
  Use the `auth/auth-surface` pattern for login, sign-out, reset, guards, profile
30
- placements, and HTTP integration. Use `auth/supabase-auth` for the Supabase
31
- environment and configuration shape.
37
+ placements, HTTP integration, and the managed-preview login. Use
38
+ `auth/supabase-auth` for the Supabase environment and configuration shape, and
39
+ `auth/oidc-sign-in` for an OpenID Connect issuer.
40
+
41
+ ## Managed-preview login is standard
42
+
43
+ An application with accounts always includes the managed-preview login, whether
44
+ or not anyone asked for it. Adding authentication adds both parts from the
45
+ `auth/auth-surface` pattern:
46
+
47
+ - the committed, executable `tools/preview-identity` from its example;
48
+ - the `#### Preview identity` block in the web-presented Vibe64 `Outputs`
49
+ target (Command `tools/preview-identity`, Protocol
50
+ `vibe64.preview-identity.command.v1`, Identity types `email` `user-id`,
51
+ Enabled environment `AUTH_DEV_BYPASS_ENABLED`, Secret environment
52
+ `AUTH_DEV_BYPASS_SECRET`, Runtimes `nodejs`), copied exactly from the pattern.
53
+
54
+ The preview host supplies both environment values per run, so a managed
55
+ preview can sign its browser in as an existing account. Which accounts it may
56
+ use stays the person's choice; the application never creates users for it, and
57
+ the exchange is inert without both values.
32
58
 
33
59
  ## Server boundary
34
60
 
@@ -316,9 +316,11 @@ Do not conflate these identities:
316
316
  application profile and required workspace membership already exist. It does
317
317
  not synthesize any of them as a side effect.
318
318
 
319
- For a managed preview, retain the app-owned `tools/preview-identity`
320
- executable and declare it under `#### Preview identity` in the web-presented
321
- Vibe64 `Outputs` target. JSKIT owns the executable/library pattern, Vibe64 owns
319
+ An application with accounts has the standard managed-preview login: retain
320
+ the app-owned `tools/preview-identity` executable, or add it from
321
+ `auth/auth-surface` when it is missing, and declare it under
322
+ `#### Preview identity` in the web-presented Vibe64 `Outputs` target exactly as
323
+ that pattern shows. JSKIT owns the executable/library pattern, Vibe64 owns
322
324
  the declaration's grammar and runtime behavior, and Genesis transports the
323
325
  consumer-owned section without interpreting it. The executable calls the
324
326
  `@jskit-ai/auth-web` server-side managed-preview library; it does not need a
@@ -22,7 +22,10 @@ browser-product baseline. It includes:
22
22
  - responsive JSKIT shell
23
23
  - surfaces, placements, topology, and settings
24
24
  - lint, unit, client, build, and browser configuration
25
- - managed-preview identity support when the host uses it
25
+
26
+ It has no accounts. Adding authentication through `auth/auth-surface` also adds
27
+ the standard managed-preview login (`tools/preview-identity` and its
28
+ `#### Preview identity` Outputs block).
26
29
 
27
30
  Use it when navigation and settings are real product requirements. Replace its
28
31
  home content and vocabulary; do not preserve the concrete `reading-room` name
@@ -0,0 +1,152 @@
1
+ <!-- Generated by `npm run agent-docs:build` from `packages/agent-docs/site/guide/app-setup/run-an-accounts-hub.md`. Do not edit manually. -->
2
+
3
+ # Run an accounts hub
4
+
5
+ An accounts hub gives one person or company a single sign-in for all of their
6
+ apps. It is an OpenID Connect issuer that runs as its **own** JSKIT project, on a
7
+ domain of its own. Apps sign in through it like they would through Google: the
8
+ hub shows its sign-in page, the app receives an ID token, and the app keeps its
9
+ own session.
10
+
11
+ ```bash
12
+ npm install @jskit-ai/oidc-issuer-core @jskit-ai/database-runtime-mysql
13
+ ```
14
+
15
+ Start from the "JSKIT · Accounts hub" template (seed-jskit branch
16
+ `accounts-hub`) when you can: it already has the pages, the Stack and the tests.
17
+ Use the `auth/accounts-hub` pattern for the composition rules. Apps connect with
18
+ `@jskit-ai/auth-provider-oidc-core`; see
19
+ [Sign in through an OIDC issuer](/guide/app-setup/sign-in-through-oidc-issuer).
20
+
21
+ ## In order
22
+
23
+ 1. Create the hub from the template, with its own database.
24
+ 2. Development: set `OIDC_HUB_OWNER_EMAIL` (ask the person), a random
25
+ `OIDC_HUB_REGISTRATION_TOKEN` and, if the database is shared or kept,
26
+ `OIDC_HUB_SECRET`. Start it, create the owner account and confirm it (the
27
+ link is in the server log without a sender). Run the tests.
28
+ 3. Production: choose the hub's own domain and set `APP_PUBLIC_URL`, a new
29
+ `OIDC_HUB_SECRET`, the same registration token the apps will use, the owner
30
+ email, an email sender and `OIDC_HUB_DEV_ORIGIN_PATTERNS` for the apps'
31
+ development previews.
32
+ 4. Deploy, then sign in as the owner and follow **Setup** until every item is
33
+ ready. Only then connect apps (see [Connecting an app](#connecting-an-app)).
34
+
35
+ Report Setup honestly: do not say email or Google works until its Setup item is
36
+ ready.
37
+
38
+ ## What the hub does
39
+
40
+ - **Sign-in for apps.** `/.well-known/openid-configuration` and `/oidc/*` serve a
41
+ hardened `oidc-provider`: authorization code with PKCE only, exact redirect
42
+ URIs, ES256 ID tokens that include `email`, `email_verified` and `name`, no
43
+ refresh tokens for apps, `iss` in every authorization response, RP-initiated
44
+ and back-channel logout.
45
+ - **Accounts.** Email and password (scrypt v1, the same format as JSKIT local
46
+ accounts), email confirmation, password recovery, and optional "Continue with
47
+ Google" with the owner's own Google client. An account reaches apps only once
48
+ its email is confirmed, because apps trust that address. Apps sign in
49
+ silently only while the browser is signed in to the hub as the same person.
50
+ - **Approval.** Apps register themselves at `/oidc/reg` with the registration
51
+ token. Loopback and development addresses are approved automatically; every
52
+ other address waits until the owner clicks Approve on the hub's Apps page.
53
+ - **Admin pages.** Apps (approve, block, remove, rotate the signing key), People
54
+ (turn accounts off, which signs them out of every app through back-channel
55
+ logout) and Setup (a checklist of what is ready).
56
+
57
+ ## Environment
58
+
59
+ | Name | Required | Meaning |
60
+ |---|---|---|
61
+ | `APP_PUBLIC_URL` | production | The hub's origin, which is its issuer. https outside this computer, no path. |
62
+ | `OIDC_HUB_SECRET` | production | At least 32 random bytes. Protects cookies and the stored signing keys; never change it once set. Development generates one in `.jskit/oidc-hub/secret`, so set it explicitly whenever the database outlives that folder (a hosted editor, several checkouts sharing one database). |
63
+ | `OIDC_HUB_REGISTRATION_TOKEN` | yes | At least 24 characters. Apps present it to register. Give the same value to each app as `AUTH_OIDC_REGISTRATION_TOKEN`. |
64
+ | `OIDC_HUB_OWNER_EMAIL` | yes | The owner's email. That account, once its email is confirmed, opens the admin pages. |
65
+ | `OIDC_HUB_DEV_ORIGIN_PATTERNS` | no | Comma-separated `https://` origin globs approved automatically, for example `https://preview-*.acme.example.dev`. `*` matches inside one DNS label. Loopback is always approved. |
66
+ | `OIDC_HUB_EMAIL_FROM` with `RESEND_API_KEY` or `SMTP_URL` | production | The owner's own sender. In development, without a sender, emails are written to the server log. |
67
+ | `OIDC_HUB_GOOGLE_CLIENT_ID` and `OIDC_HUB_GOOGLE_CLIENT_SECRET` | no | Turn on "Continue with Google". The Google redirect URI is `{APP_PUBLIC_URL}/hub/google/callback`. |
68
+
69
+ Each problem stops the hub with one sentence that names the variable to fix.
70
+
71
+ ## Mounting
72
+
73
+ The installed `OidcIssuerProvider` mounts everything on the app's Fastify
74
+ server. A server that filters paths by surface must also serve the hub's
75
+ paths:
76
+
77
+ ```js
78
+ import { HUB_GLOBAL_PATHS } from "@jskit-ai/oidc-issuer-core/server";
79
+
80
+ registerSurfaceRequestConstraint({
81
+ fastify: app,
82
+ surfaceRuntime,
83
+ serverSurface,
84
+ globalUiPaths: [...runtimeGlobalUiPaths, ...HUB_GLOBAL_PATHS]
85
+ });
86
+ ```
87
+
88
+ The hub's own pages are ordinary Vue pages. They call `/hub/api/*` with
89
+ `x-hub-request: 1` on every state-changing request. An app's sign-in arrives at
90
+ `/hub/interaction/:uid`, which sends the browser to `/auth/sign-in?interaction=…`.
91
+ That page reads `/hub/interaction/:uid/details` (app name, full address and
92
+ status) and finishes with `/hub/interaction/:uid/continue` once the person is
93
+ signed in.
94
+
95
+ ## Registration and approval
96
+
97
+ A registration carries only `client_name` (at most 80 characters),
98
+ `redirect_uris`, `post_logout_redirect_uris`, `backchannel_logout_uri`,
99
+ `token_endpoint_auth_method` (`client_secret_basic`), `grant_types`
100
+ (`["authorization_code"]`) and `response_types` (`["code"]`). Every address must
101
+ be on one origin, https unless it is loopback. Anything else is refused with
102
+ `invalid_client_metadata`.
103
+
104
+ | Address | Status |
105
+ |---|---|
106
+ | Loopback, or matching `OIDC_HUB_DEV_ORIGIN_PATTERNS` | `active-dev`: works at once; removed after 30 idle days |
107
+ | An origin the owner already approved | `active` |
108
+ | Any other origin | `pending`: the person sees "This app is waiting for approval by the hub's owner." |
109
+ | A blocked origin | refused with 403 "This address is blocked by the hub's owner." |
110
+
111
+ The response header `x-hub-client-status` tells the registering app its status.
112
+
113
+ ## Commands
114
+
115
+ Run them from the hub project's folder; they read its `.env` and database
116
+ settings.
117
+
118
+ ```bash
119
+ npx jskit-oidc-issuer keys rotate
120
+ npx jskit-oidc-issuer import-accounts --in accounts.jsonl # dry run
121
+ npx jskit-oidc-issuer import-accounts --in accounts.jsonl --apply
122
+ ```
123
+
124
+ - `keys rotate` makes a new ES256 key active. The previous key keeps verifying
125
+ for two hours, then leaves the JWKS. The Apps page has the same button.
126
+ - `import-accounts` reads the JSON Lines written by
127
+ `jskit-oidc-sign-in export-local-accounts`. It keeps each `usr_` id as the
128
+ hub's `sub` and each scrypt password byte for byte, so people sign in with the
129
+ password they already had. Emails start unconfirmed: each person confirms
130
+ theirs once, at their first app sign-in. Rows that clash with a
131
+ different hub account are refused and listed. A dry run prints only counts.
132
+ - `preview-identity` implements `vibe64.preview-identity.command.v1` for the
133
+ hub, so an editor's preview can sign in as an existing hub account. It works
134
+ only outside production, from loopback, with `AUTH_DEV_BYPASS_ENABLED` and
135
+ `AUTH_DEV_BYPASS_SECRET`.
136
+
137
+ ## Connecting an app
138
+
139
+ 1. Install `@jskit-ai/auth-provider-oidc-core` in the app.
140
+ 2. Give the app `AUTH_OIDC_ISSUER` (the hub's `APP_PUBLIC_URL`) and
141
+ `AUTH_OIDC_REGISTRATION_TOKEN` (the hub's `OIDC_HUB_REGISTRATION_TOKEN`).
142
+ 3. Sign in once from the app. A development address works straight away. After
143
+ the app is deployed, open the hub's Apps page and approve it at its full
144
+ address.
145
+
146
+ ## Verify
147
+
148
+ - Start the hub with one variable missing and read the sentence it prints.
149
+ - A loopback app registers as `active-dev` and signs in.
150
+ - An https app on another domain waits until Approve, then signs in.
151
+ - Setup shows every item ticked once the owner confirms the domain and sends a
152
+ test email.
@@ -0,0 +1,148 @@
1
+ <!-- Generated by `npm run agent-docs:build` from `packages/agent-docs/site/guide/app-setup/sign-in-through-oidc-issuer.md`. Do not edit manually. -->
2
+
3
+ # Sign in through an OIDC issuer
4
+
5
+ Use this when an app should not keep its own passwords: people sign in with
6
+ the account they already have in an accounts hub (see
7
+ [Run an accounts hub](/guide/app-setup/run-an-accounts-hub)) or in any OpenID
8
+ Connect issuer, such as Google Workspace or Okta. The app still keeps its own
9
+ `users` rows and its own session; the issuer only proves who the person is.
10
+
11
+ The pattern is `auth/oidc-sign-in`.
12
+
13
+ ## In order
14
+
15
+ 1. Have the issuer's address and either a registration token (an accounts hub)
16
+ or a client id and secret (any other issuer). Ask the person; never invent them.
17
+ 2. Install the provider and run the migration (below).
18
+ 3. Set or link the Env values (below) for development and production.
19
+ 4. Sign in once in development. Against a hub, a development address works at
20
+ once when it is on this computer or matches the hub's development patterns.
21
+ 5. Deploy, then ask the hub's owner to approve the app on the hub's Apps page at
22
+ the app's full address. Until then sign-in shows the hub's waiting page.
23
+
24
+ ## Install
25
+
26
+ Replace the local provider; an app has exactly one `auth.service` provider.
27
+
28
+ ```bash
29
+ npm uninstall @jskit-ai/auth-provider-local-core
30
+ npm install @jskit-ai/auth-provider-oidc-core @jskit-ai/auth-web
31
+ npm run db:migrate
32
+ ```
33
+
34
+ The migration adds `auth_oidc_clients`, `auth_oidc_sessions` and
35
+ `auth_oidc_logout_tokens`. Existing local tables are left alone; move the
36
+ accounts first (below) if the app already has people.
37
+
38
+ ## Env
39
+
40
+ | Name | Meaning |
41
+ |---|---|
42
+ | `AUTH_OIDC_ISSUER` | The issuer address. Its discovery document supplies everything else. |
43
+ | `AUTH_OIDC_REGISTRATION_TOKEN` | Preferred. The app registers itself with the issuer, once per address. |
44
+ | `AUTH_OIDC_CLIENT_ID` / `AUTH_OIDC_CLIENT_SECRET` | For issuers without self-registration. Use these **instead of** the token, never both. |
45
+ | `AUTH_OIDC_LABEL` | Button label. Default "Continue with your account". |
46
+ | `AUTH_OIDC_SESSION_SECRET` | Required in production, at least 32 characters. In development it is generated in `.jskit/auth/oidc-session.secret`. |
47
+ | `APP_PUBLIC_URL` | The address people use. With it the app registers at start; without it, on the first sign-in from a new address. Required in production behind a proxy that is not on the same machine. |
48
+
49
+ Addresses must be `https`, or `http` on `localhost`/loopback. Without
50
+ `AUTH_OIDC_ISSUER` the app still starts; pressing the button brings the person
51
+ back to the login page with "Sign-in is not set up yet: AUTH_OIDC_ISSUER is
52
+ missing." Every sign-in that cannot start is explained there the same way.
53
+
54
+ Never invent either value or copy it into source. If the host keeps saved
55
+ values (an editor's saved keys, a deployment secret store), link these two
56
+ variables to the hub's saved address and token instead of pasting them, in
57
+ development and in production.
58
+
59
+ The issuer must be reachable from the app's server and from the person's
60
+ browser. A hub that runs only on another computer's `localhost` (for example in
61
+ another project's development preview) is not; connect apps to the deployed hub.
62
+
63
+ ## What the issuer is told
64
+
65
+ When registering, the app sends:
66
+
67
+ | Field | Value |
68
+ |---|---|
69
+ | `redirect_uris` | `<address>/auth/login` (`AUTH_OIDC_REDIRECT_PATH`) |
70
+ | `post_logout_redirect_uris` | `<address>/` |
71
+ | `backchannel_logout_uri` | `<address>/api/auth/oidc/backchannel-logout` |
72
+ | `token_endpoint_auth_method` | `client_secret_basic` |
73
+ | `grant_types` / `response_types` | `authorization_code` / `code` |
74
+
75
+ Scopes are `openid email profile`. A static client must be registered with the
76
+ same redirect URI.
77
+
78
+ An accounts hub decides what a new address may do. Development addresses work
79
+ at once. A production address waits until the hub's owner approves it on the
80
+ hub's Apps page; until then, signing in shows the hub's own waiting page. A
81
+ blocked address cannot register, and the login page says "Your accounts hub has
82
+ blocked this app's address."
83
+
84
+ ## Signing in
85
+
86
+ The login page shows one button. Unframed, it goes to the issuer and comes back
87
+ to `/auth/login`, then to the page the person started from. Inside a frame,
88
+ such as an editor Preview, the issuer cannot be shown, so the button opens a
89
+ pop-up window instead; when the pop-up finishes, it closes and the framed page
90
+ reloads signed in. If the browser blocks the pop-up the page says so.
91
+
92
+ Each attempt is bound to the browser that started it by a sealed ten-minute
93
+ cookie and checked with PKCE, `state`, `nonce` and the issuer's `iss`; a stale
94
+ or foreign attempt reads "This sign-in took too long or started in another
95
+ browser. Start again."
96
+
97
+ The first sign-in creates the `users` row (`auth_provider` `oidc`,
98
+ `auth_provider_user_sid` the issuer's subject) with the email and name.
99
+ Apps trust that email (invitations match it), so when the issuer says
100
+ `email_verified: false` the sign-in stops with "Your account provider has not
101
+ confirmed your email address yet." An accounts hub never sends an unconfirmed
102
+ email; it asks the person to confirm first.
103
+
104
+ ## Signing out
105
+
106
+ - Ordinary sign-out ends the app session only.
107
+ - `/api/auth/oidc/logout?everywhere=1` (a same-site link) also ends the issuer
108
+ session and returns to `/`.
109
+ - The issuer can end app sessions itself: disabling an account in the hub posts
110
+ a signed logout token to the back-channel URI, and the person's sessions in
111
+ this app end at once.
112
+
113
+ ## Preview identity
114
+
115
+ Keep the standard managed-preview login (`tools/preview-identity`, see
116
+ [Authentication](/guide/app-setup/authentication)). It signs the Preview in as
117
+ an existing `users` row without visiting the issuer, so tests do not need a
118
+ hub account. It cannot create users.
119
+
120
+ ## Move existing local accounts
121
+
122
+ Only with the app owner's explicit yes. On the app (local provider still
123
+ installed):
124
+
125
+ ```bash
126
+ npx jskit-oidc-sign-in export-local-accounts --out accounts.jsonl
127
+ ```
128
+
129
+ This writes one JSON line per account, readable only by you, and refuses to
130
+ overwrite a file. Import that file into the hub (`jskit-oidc-issuer
131
+ import-accounts`, see the hub guide). Then switch the provider as above and
132
+ mark the same people as hub accounts:
133
+
134
+ ```bash
135
+ npx jskit-oidc-sign-in adopt-oidc --issuer "$AUTH_OIDC_ISSUER" --in accounts.jsonl
136
+ npx jskit-oidc-sign-in adopt-oidc --issuer "$AUTH_OIDC_ISSUER" --in accounts.jsonl --apply
137
+ ```
138
+
139
+ The first run only counts what would change; `--apply` changes it. Ids stay the
140
+ same, so existing data keeps its owners. Delete `accounts.jsonl` afterwards: it
141
+ contains password hashes.
142
+
143
+ ## Verification
144
+
145
+ - Sign in unframed and in the Preview; refresh stays signed in.
146
+ - Sign out, then sign out everywhere.
147
+ - Disable the account in the hub and confirm the app session ends.
148
+ - Preview identity still signs in.
@@ -189,6 +189,19 @@ Commit `package.json`, every changed workspace manifest, and
189
189
  updater succeeds; select a different published cohort only through a catalog
190
190
  that advertises it.
191
191
 
192
+ ## Host-only production cookies
193
+
194
+ In production (`NODE_ENV=production`, served over HTTPS) the session cookies of
195
+ the local, Supabase and OIDC auth providers and the CSRF secret cookie use the
196
+ `__Host-` prefix, for example `__Host-jskit_local_access_token` and
197
+ `__Host-_csrf`. Browsers accept these only with `Secure`, `Path=/` and no
198
+ `Domain`, so a sibling subdomain cannot plant or overwrite them. The first
199
+ production deploy after updating signs everyone out once. Development and tests
200
+ keep the unprefixed names.
201
+ Setting `AUTH_CSRF_COOKIE_SECURE=false` keeps the unprefixed CSRF cookie for an
202
+ application deliberately served over plain HTTP. CSRF protection is still the
203
+ token check on every unsafe request; the prefix only hardens the cookie.
204
+
192
205
  ## Read cancellation and deadlines
193
206
 
194
207
  The standard endpoint, list, view, form-read, and CRUD read composables pass
@@ -90,6 +90,9 @@ the npm installation.
90
90
 
91
91
  Do not add auth, users, workspaces, console, sample data, databases, or AI
92
92
  capabilities unless the product choice requires them.
93
+ When accounts are required, `auth/auth-surface` includes the managed-preview
94
+ login as standard: add `tools/preview-identity` and its `#### Preview identity`
95
+ Outputs block together with the rest of the surface.
93
96
 
94
97
  ## Author database-backed CRUD
95
98
 
@@ -55,6 +55,17 @@ shared-screen controls; avoid duplicates, drawer-only access, or adding a page
55
55
  heading just to house it. Keep its meaning and accessible name clear at every
56
56
  supported width.
57
57
 
58
+ ### Scroll position
59
+
60
+ `createShellRouter()` from `@jskit-ai/kernel/client` installs
61
+ `shellScrollBehavior` by default: a new screen opens at the top (or at its
62
+ hash target), Back/Forward restore the saved position, and query, hash, tab or
63
+ sub-view changes within the same page keep the current position. A tab or
64
+ sub-view counts as the same page when both locations share a parent page record
65
+ below the surface root. Pass `scrollBehavior` to `createShellRouter()` to
66
+ replace it with your own Vue Router function, or `scrollBehavior: false` to
67
+ disable it. Do not wrap the router or reset scroll in page components.
68
+
58
69
  ## Application-owned files
59
70
 
60
71
  Pattern source is ordinary customizable application source. When replacing a
@@ -93,6 +104,19 @@ icon/label and label/end gaps. The normal 80px rail centres 48px targets;
93
104
  `railWidth` and `drawerWidth` are public density/width controls. Do not override
94
105
  the shell's private implementation CSS.
95
106
 
107
+ The app bar, drawer/rail and compact bottom navigation follow the Vuetify theme
108
+ and expose restyling tokens instead of hard-coded chrome: `--shell-chrome-border-width`
109
+ and `--shell-chrome-border-color` for the shared dividers,
110
+ `--shell-app-bar-border-width`, `--shell-navigation-border-width` and
111
+ `--shell-bottom-navigation-border-width` per edge, and
112
+ `--shell-app-bar-background`, `--shell-navigation-background` and
113
+ `--shell-bottom-navigation-background` for the surfaces. The defaults keep one
114
+ thin divider on each separating edge, because Vuetify's default themes define
115
+ no Material 3 surface-container roles to separate surfaces tonally. A Material 3
116
+ restyle sets the border widths to `0` and gives navigation a tonal surface, for
117
+ example `--shell-navigation-background: rgb(var(--v-theme-surface-container-low))`
118
+ when the theme defines that role.
119
+
96
120
  ## Browser verification
97
121
 
98
122
  Exercise user-facing changes with Playwright at compact, medium, and expanded
@@ -14,6 +14,8 @@ apply or adapt. It does not prescribe an agent host or project orchestrator.
14
14
  - [Migrate an existing application](/guide/app-setup/existing-application-migration)
15
15
  - [A more interesting shell](/guide/app-setup/a-more-interesting-shell)
16
16
  - [Authentication](/guide/app-setup/authentication)
17
+ - [Sign in through an OIDC issuer](/guide/app-setup/sign-in-through-oidc-issuer)
18
+ - [Run an accounts hub](/guide/app-setup/run-an-accounts-hub)
17
19
  - [Database layer](/guide/app-setup/database-layer)
18
20
  - [Users](/guide/app-setup/users)
19
21
  - [Multi-homing](/guide/app-setup/multi-homing)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.238",
3
+ "version": "0.1.240",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -5,7 +5,7 @@
5
5
  "private": true,
6
6
  "type": "module",
7
7
  "dependencies": {
8
- "@jskit-ai/kernel": "0.1.244",
8
+ "@jskit-ai/kernel": "0.1.245",
9
9
  "json-rest-schema": "^1.0.17"
10
10
  },
11
11
  "exports": {
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.1.244",
37
+ "@jskit-ai/kernel": "0.1.245",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
40
  "json-rest-schema": "^1.0.17",
@@ -42,13 +42,13 @@
42
42
  "vue": "^3.5.44",
43
43
  "vue-router": "^5.3.1",
44
44
  "vuetify": "^4.1.2",
45
- "@jskit-ai/http-runtime": "0.1.242",
45
+ "@jskit-ai/http-runtime": "0.1.243",
46
46
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
47
47
  "@vue/devtools-api": "^8.2.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@jskit-ai/config-eslint": "0.1.241",
51
- "@jskit-ai/jskit-catalog": "0.1.291",
51
+ "@jskit-ai/jskit-catalog": "0.1.293",
52
52
  "@playwright/test": "1.61.1",
53
53
  "@vitejs/plugin-vue": "^6.0.7",
54
54
  "eslint": "^10.8.0",
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.1.244",
37
+ "@jskit-ai/kernel": "0.1.245",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
40
  "json-rest-schema": "^1.0.17",
@@ -42,15 +42,15 @@
42
42
  "vue": "^3.5.44",
43
43
  "vue-router": "^5.3.1",
44
44
  "vuetify": "^4.1.2",
45
- "@jskit-ai/http-runtime": "0.1.242",
45
+ "@jskit-ai/http-runtime": "0.1.243",
46
46
  "@mdi/js": "^7.4.47",
47
- "@jskit-ai/shell-web": "0.1.249",
47
+ "@jskit-ai/shell-web": "0.1.250",
48
48
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
49
49
  "@vue/devtools-api": "^8.2.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@jskit-ai/config-eslint": "0.1.241",
53
- "@jskit-ai/jskit-catalog": "0.1.291",
53
+ "@jskit-ai/jskit-catalog": "0.1.293",
54
54
  "@playwright/test": "1.61.1",
55
55
  "@vitejs/plugin-vue": "^6.0.7",
56
56
  "eslint": "^10.8.0",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "description": "App-local runtime composition and lightweight glue.",
11
11
  "dependencies": {
12
- "@jskit-ai/kernel": "0.1.244"
12
+ "@jskit-ai/kernel": "0.1.245"
13
13
  },
14
14
  "jskit": {
15
15
  "kind": "runtime",
@@ -141,6 +141,7 @@ Exports
141
141
  - `resolveAllowedOriginsFromSurfaceDefinitions(surfaceDefinitions = {}, { seedOrigins = [] } = {})`
142
142
  - `resolveAllowedOriginsFromPlacementContext(contextValue = null, { includeWindowOrigin = true } = {})`
143
143
  Local functions
144
+ - `staysOnSameOrigin(path)`
144
145
  - `appendNormalizedOrigin(origins, value)`
145
146
  - `normalizeBlockedPathnames(blockedPathnames = [])`
146
147