@pikku/skills 0.12.52 → 0.12.55

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 (112) hide show
  1. package/README.md +1 -1
  2. package/dist/skills.gen.js +2 -2
  3. package/dist/snippets.js +1 -1
  4. package/package.json +1 -1
  5. package/skills/pikku-a11y/SKILL.md +22 -15
  6. package/skills/pikku-addon/SKILL.md +5 -5
  7. package/skills/pikku-addon/references/addon-package-manifest.md +7 -0
  8. package/skills/pikku-addon/references/openapi.md +11 -0
  9. package/skills/pikku-agent/SKILL.md +6 -6
  10. package/skills/pikku-agent/references/agents.md +8 -1
  11. package/skills/pikku-agent/references/runner-vercel.md +2 -2
  12. package/skills/pikku-agent/references/voice.md +7 -1
  13. package/skills/pikku-architect/SKILL.md +2 -2
  14. package/skills/pikku-architect/references/plan-defects.md +9 -7
  15. package/skills/pikku-auth/SKILL.md +15 -36
  16. package/skills/pikku-auth/references/better-auth.md +106 -133
  17. package/skills/pikku-auth/references/jose.md +6 -0
  18. package/skills/pikku-auth/references/machine-auth.md +6 -0
  19. package/skills/pikku-auth/references/sessions.md +6 -0
  20. package/skills/pikku-build/SKILL.md +37 -34
  21. package/skills/pikku-build/references/app.md +113 -99
  22. package/skills/pikku-build/references/design.md +43 -31
  23. package/skills/pikku-build/references/feature.md +17 -3
  24. package/skills/pikku-build/references/mocks.md +29 -0
  25. package/skills/pikku-build/references/multi-app.md +25 -18
  26. package/skills/pikku-build/references/native-app.md +9 -0
  27. package/skills/pikku-build/references/openapi.md +41 -34
  28. package/skills/pikku-build/references/platform.md +23 -14
  29. package/skills/pikku-build/references/post-clone.md +1 -1
  30. package/skills/pikku-build/references/quick.md +76 -39
  31. package/skills/pikku-build/references/scenarios.md +28 -16
  32. package/skills/pikku-build/references/ship.md +19 -16
  33. package/skills/pikku-build/references/theming.md +76 -127
  34. package/skills/pikku-changes/SKILL.md +7 -10
  35. package/skills/pikku-concepts/SKILL.md +34 -44
  36. package/skills/pikku-concepts/references/concept-mapping.md +18 -0
  37. package/skills/pikku-concepts/references/language.md +6 -6
  38. package/skills/pikku-convert-screen/SKILL.md +45 -0
  39. package/skills/pikku-deploy/references/cloudflare.md +8 -0
  40. package/skills/pikku-deploy/references/lambda.md +8 -0
  41. package/skills/pikku-deploy/references/nextjs.md +1 -1
  42. package/skills/pikku-deploy/references/uws.md +1 -1
  43. package/skills/pikku-deploy/references/ws.md +3 -3
  44. package/skills/pikku-emails/SKILL.md +3 -9
  45. package/skills/pikku-guide/SKILL.md +28 -27
  46. package/skills/pikku-i18n/SKILL.md +7 -7
  47. package/skills/pikku-i18n/references/enum-labels.md +9 -0
  48. package/skills/pikku-i18n/references/messages.md +21 -9
  49. package/skills/pikku-i18n/references/rtl.md +18 -38
  50. package/skills/pikku-knowledge/SKILL.md +12 -13
  51. package/skills/pikku-kysely/SKILL.md +3 -21
  52. package/skills/pikku-list-query/SKILL.md +2 -2
  53. package/skills/pikku-meta/references/audit.md +10 -1
  54. package/skills/pikku-meta/references/meta.md +6 -0
  55. package/skills/pikku-meta/references/versioning.md +10 -0
  56. package/skills/pikku-middleware/SKILL.md +7 -16
  57. package/skills/pikku-n8n-import/SKILL.md +9 -1
  58. package/skills/pikku-n8n-import/references/addon-mapping.md +6 -0
  59. package/skills/pikku-n8n-import/references/code-translation.md +7 -0
  60. package/skills/pikku-permissions/SKILL.md +41 -10
  61. package/skills/pikku-permissions/references/scopes.md +59 -0
  62. package/skills/pikku-react/SKILL.md +20 -22
  63. package/skills/pikku-react/references/client.md +2 -70
  64. package/skills/pikku-react/references/react-query.md +11 -0
  65. package/skills/pikku-react/references/workflows.md +11 -0
  66. package/skills/pikku-realtime/SKILL.md +9 -3
  67. package/skills/pikku-report/SKILL.md +17 -16
  68. package/skills/pikku-scenario/SKILL.md +72 -81
  69. package/skills/pikku-scenario/references/browser.md +5 -5
  70. package/skills/pikku-scenario/references/coverage.md +4 -4
  71. package/skills/pikku-scenario/references/steps.md +31 -30
  72. package/skills/pikku-scenario/references/{persona-run.md → virtual-user-run.md} +47 -42
  73. package/skills/pikku-scenario/references/virtual-users.md +114 -0
  74. package/skills/pikku-seo/SKILL.md +12 -16
  75. package/skills/pikku-service-backends/SKILL.md +0 -10
  76. package/skills/pikku-service-backends/references/aws.md +6 -0
  77. package/skills/pikku-service-backends/references/mongodb.md +7 -0
  78. package/skills/pikku-service-backends/references/redis.md +6 -0
  79. package/skills/pikku-services/references/audit-wire-service.md +2 -2
  80. package/skills/pikku-services/references/audit.md +10 -1
  81. package/skills/pikku-services/references/config.md +6 -0
  82. package/skills/pikku-services/references/services.md +9 -2
  83. package/skills/pikku-software-archaeology/SKILL.md +21 -27
  84. package/skills/pikku-software-archaeology/references/blueprint.schema.json +16 -16
  85. package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
  86. package/skills/pikku-software-archaeology/references/second-opinion.md +13 -1
  87. package/skills/pikku-tailwind/SKILL.md +111 -0
  88. package/skills/pikku-theme/SKILL.md +187 -0
  89. package/skills/pikku-theme/references/from-a-reference.md +44 -0
  90. package/skills/pikku-theme/references/presets.md +40 -0
  91. package/skills/pikku-ui/SKILL.md +41 -0
  92. package/skills/pikku-wiring/SKILL.md +13 -12
  93. package/skills/pikku-wiring/references/channel.md +7 -1
  94. package/skills/pikku-wiring/references/cli.md +7 -1
  95. package/skills/pikku-wiring/references/gateway-slack.md +6 -0
  96. package/skills/pikku-wiring/references/http.md +10 -3
  97. package/skills/pikku-wiring/references/mcp.md +8 -0
  98. package/skills/pikku-wiring/references/queue.md +6 -0
  99. package/skills/pikku-wiring/references/realtime.md +34 -4
  100. package/skills/pikku-wiring/references/rpc.md +6 -0
  101. package/skills/pikku-wiring/references/scheduler.md +6 -0
  102. package/skills/pikku-wiring/references/trigger.md +8 -0
  103. package/skills/pikku-workflow/SKILL.md +9 -7
  104. package/skills/pikku-admin-to-fabric/SKILL.md +0 -212
  105. package/skills/pikku-auth/references/permissions.md +0 -261
  106. package/skills/pikku-blueprint-to-fabric/SKILL.md +0 -377
  107. package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +0 -367
  108. package/skills/pikku-fabric/SKILL.md +0 -619
  109. package/skills/pikku-fabric/references/debugging.md +0 -110
  110. package/skills/pikku-mantine/SKILL.md +0 -80
  111. package/skills/pikku-scenario/references/personas.md +0 -117
  112. package/skills/pikku-wiring/references/realtime-other-routes.md +0 -23
@@ -101,9 +101,9 @@ export const auth = pikkuBetterAuth(async ({ secrets }) => {
101
101
 
102
102
  ## ⚠️ Stateless session — ALWAYS enable `cookieCache` for deployed apps
103
103
 
104
- By default the CLI wires the **stateful** `betterAuthSession` bridge globally — it calls `services.auth()`, so EVERY unit/worker bundles the full better-auth server (~2.5MB each). On per-unit deploy targets (Fabric/Cloudflare) that bloats every bundle and the serial upload phase.
104
+ By default the CLI wires the **stateful** `betterAuthSession` bridge globally — it calls `services.auth()`, so EVERY unit/worker bundles the full better-auth server (~2.5MB each). On per-unit deploy targets (Pikku Studio Cloud/Cloudflare) that bloats every bundle and the serial upload phase.
105
105
 
106
- Enabling `session: { cookieCache: { enabled: true } }` makes the CLI split out a lean `betterAuthStatelessSession` (`src/scaffold/auth-middleware.gen.ts`) that verifies the signed session cookie using only `BETTER_AUTH_SECRET` — no `services.auth()`, no server bundled. Non-auth units drop from ~2.5MB to ~20KB. Only the auth unit carries the server. `pikku fabric validate` warns (`better-auth-stateless-session-disabled`) when it's off.
106
+ Enabling `session: { cookieCache: { enabled: true } }` makes the CLI split out a lean `betterAuthStatelessSession` (`src/scaffold/auth-middleware.gen.ts`) that verifies the signed session cookie using only `BETTER_AUTH_SECRET` — no `services.auth()`, no server bundled. Non-auth units drop from ~2.5MB to ~20KB. Only the auth unit carries the server. `pikku validate` warns (`better-auth-stateless-session-disabled`) when it's off.
107
107
 
108
108
  **Tradeoff:** server-side session revocation isn't seen until the cookie cache expires (sign-out is still immediate — it deletes the cookie).
109
109
 
@@ -238,21 +238,20 @@ never needs to know about scopes or roles.
238
238
 
239
239
  ### The plugins `@pikku/better-auth` ships
240
240
 
241
- Five, all imported from the package root and passed to `betterAuth({ plugins })`
241
+ Four, all imported from the package root and passed to `betterAuth({ plugins })`
242
242
  like any other. None is automatic — an app wires the ones it needs.
243
243
 
244
244
  | Plugin | Plugin `id` | Adds | Use it when |
245
245
  | ------------------------ | ------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------- |
246
246
  | `pikkuBan()` | `pikku-ban` | `user.banned/banReason/banExpires` | You ban users (the schema + enforcement half of the above) |
247
- | `pikkuActor()` | `actor` | `POST /sign-in/actor`, `user.actor` | Scenarios or a dev switcher sign in as a persona |
247
+ | `pikkuVirtualUser()` | `virtualUser` | `POST /sign-in/virtual-user`, `user.virtualUser` | Scenarios sign in as a virtual user |
248
248
  | `pikkuCredentialOAuth()` | `credential-oauth` | `POST /credential-oauth/link`, `/credential-oauth/callback/:providerId` | An app links OAuth2 **API credentials** for a user |
249
249
  | `pikkuDelegatedAuth()` | `delegated-auth` | `POST /sign-in/delegated` | An imported upstream API is the system of record for identity |
250
- | `pikkuFabric()` | `fabric` | `POST /sign-in/fabric` | A Fabric-deployed app lets a control-plane operator in |
251
250
 
252
251
  Every one carries a `pikku` prefix, because a `plugins: [...]` array mixes these
253
- with better-auth's own and a bare `actor()` next to `organization()` says
254
- nothing about where it came from. The unprefixed names — `ban`, `actor`,
255
- `credentialOAuth`, `delegatedAuth`, `fabric` — are still exported as deprecated
252
+ with better-auth's own and a bare `virtualUser()` next to `organization()` says
253
+ nothing about where it came from. The unprefixed names — `ban`, `virtualUser`,
254
+ `credentialOAuth`, `delegatedAuth` — are still exported as deprecated
256
255
  aliases, so existing apps keep working.
257
256
 
258
257
  The plugin's `id` is what better-auth stores; the **export name** is what the
@@ -343,26 +342,11 @@ an existing user row, so it cannot take over someone else's account.
343
342
  For an addon generated from an OpenAPI spec, none of this is written by hand:
344
343
  `pikku new addon --openapi … --auth-config <file>` generates
345
344
  `authenticate<Name>Upstream` in the addon and wires this plugin, the stored
346
- credential and the actor credentials into `src/auth.ts`. The config format is
345
+ credential and the virtual user credentials into `src/auth.ts`. The config format is
347
346
  in the `pikku-build` skill's `references/openapi.md`. When the upstream later
348
347
  refuses the stored token, the addon throws `CredentialRejectedError` (403,
349
348
  `reauth: 'sign-in'`): the UI shows the sign-in again.
350
349
 
351
- #### `pikkuFabric()` — control-plane operator sign-in
352
-
353
- ```typescript
354
- pikkuFabric({ publicKey: FABRIC_AUTH_PUBLIC_KEY, scopeService, logger })
355
- ```
356
-
357
- `POST /sign-in/fabric` verifies a short-lived RS256 token that the Fabric
358
- control plane signed for an operator session, then signs them into a synthetic
359
- `fabric-<id>@fabric.internal` row holding the `admin` scope. Asymmetric on
360
- purpose: the app holds only the public key, so it can never forge an operator
361
- login, and the same `FABRIC_AUTH_PUBLIC_KEY` is distributed to every stage with
362
- no per-environment secret. A missing or empty key disables the endpoint, and a
363
- token whose `purpose` claim is not `fabric-admin` is rejected. Without a
364
- `ScopeService` the operator signs in holding nothing.
365
-
366
350
  ### 2. Production database adapter
367
351
 
368
352
  For real deployments swap `memoryAdapter` for the Kysely adapter backed by an injected DB. Better Auth owns its own tables (`user`, `session`, `account`, `verification`, plus plugin tables) — generate its schema with `npx @better-auth/cli generate` and apply it as a migration.
@@ -471,177 +455,166 @@ hostname check — because a one-request path to an admin session is exactly the
471
455
  thing that must not survive a deploy. An app that has not declared the `admin`
472
456
  scope still gets a session, with a warning, since a scopeless dev user is useful.
473
457
 
474
- ### Actor sign-in (`actor` plugin)
458
+ ### Virtual user sign-in (`virtualUser` plugin)
475
459
 
476
460
  A different thing from dev quick login, and the one to reach for when "sign in as
477
461
  someone" means **a particular kind of user** rather than one fixed admin.
478
462
  Register it explicitly — it is not automatic:
479
463
 
480
464
  ```typescript
481
- import { ACTOR_SIGN_IN_OPT_IN_ENV, pikkuActor } from '@pikku/better-auth'
465
+ import {
466
+ VIRTUAL_USER_SIGN_IN_OPT_IN_ENV,
467
+ pikkuVirtualUser,
468
+ } from '@pikku/better-auth'
482
469
 
483
470
  plugins: [
484
- pikkuActor({
485
- secret: SCENARIO_ACTOR_SECRET,
486
- allowSignIn: await variables.get(ACTOR_SIGN_IN_OPT_IN_ENV),
471
+ pikkuVirtualUser({
472
+ verifyKey: await variables.get('VIRTUAL_USER_VERIFY_KEY'),
473
+ allowSignIn: await variables.get(VIRTUAL_USER_SIGN_IN_OPT_IN_ENV),
487
474
  }),
488
475
  ]
489
476
  ```
490
477
 
491
- `POST ${basePath}/sign-in/actor` `{ email, secret, name? }` → 200 + the normal
492
- session cookie. The plugin's `secret` is the **root**, and it may be a (possibly
493
- async) function so it can come off the secrets service instead of a captured
494
- value.
495
-
496
- **What a caller presents is not the root.** It is
497
- `deriveActorSecret(root, email)` from `@pikku/core/services` — HKDF-expanded
498
- HMAC-SHA256 over the lowercased address. The endpoint re-derives the expected
499
- value for the address being signed in as and compares, so a credential minted
500
- for one persona is refused for every other, and the root itself is never a valid
501
- credential. A root under 32 characters refuses the endpoint outright rather than
502
- deriving weak credentials from it (the server log names the problem; the client
503
- is not told which). Callers rarely derive by hand — `pikku persona secret <id>`
504
- mints them for a run, and the two `PersonaSignIn` implementations derive on the
505
- fly. The browser switcher holds none: it signs in through `/sign-in/persona`.
506
-
507
- **Which command is running decides whether it works, not whether a secret is
508
- set.** `pikku dev` sets `PIKKU_DEV_ACTOR_SIGN_IN` and mints an ephemeral
509
- `SCENARIO_ACTOR_SECRET` for the run, so local development needs no configuration
510
- at all. Everywhere else the endpoint refuses (`Actor sign-in is disabled outside
511
- \`pikku dev\``) — `pikku serve` clears the marker outright, so a secret that
512
- leaked into a production environment enables nothing and gets a warning naming
513
- itself instead.
478
+ There is one door, and two ways through it.
479
+
480
+ - **A run signs in from inside the app.** A scheduled virtual user run calls
481
+ `auth.api.signInVirtualUserInternal`, which is `SERVER_ONLY`: it exists on
482
+ `auth.api` and answers 404 through `auth.handler`, so no HTTP caller can reach
483
+ it. The run takes the cookie and then drives the app over HTTP.
484
+ - **An outside caller presents one signed statement.** `pikku dev`, `pikku
485
+ scenario run`, `pikku virtual-user run`, `pikku pages screenshot --as` and the
486
+ Playwright provider `POST ${basePath}/sign-in/virtual-user` `{ statement }` →
487
+ 200 + the normal session cookie. The statement is an EdDSA JWT with `sub` (the
488
+ virtual user id), `aud` (the app's origin), a short expiry (180 seconds by
489
+ default, 300 at most) and a one-time `jti`. The plugin verifies it against
490
+ `verifyKey` with a verify-only service; a replayed `jti` is refused. The replay
491
+ record is in memory, so it is per process.
492
+
493
+ **Keys.** `pikku dev` generates a key pair under
494
+ `<runtimeDir>/virtual-user/signing.key` (mode 0600, next to a `.gitignore` of
495
+ `*`) and gives the app the public half, so local development needs no
496
+ configuration. A deployed stage has one key pair: the deployer holds the private
497
+ key (`VIRTUAL_USER_SIGNING_KEY`) and the app reads the public key from
498
+ `VIRTUAL_USER_VERIFY_KEY`. A stage without a verify key refuses statements and
499
+ logs why; it never falls back to anything weaker.
500
+
501
+ **Which command is running decides whether it works.** `pikku dev` sets
502
+ `PIKKU_DEV_VIRTUAL_USER_SIGN_IN`. Everywhere else the endpoint refuses
503
+ (`VirtualUser sign-in is disabled outside \`pikku dev\``) — `pikku serve` clears the
504
+ marker outright. In production neither endpoint is registered at all.
514
505
 
515
506
  A stage that genuinely must run scenarios opts in on purpose, with
516
- `PIKKU_ALLOW_ACTOR_SIGN_IN=passwordless-actor-sign-in`. Any other value is
507
+ `PIKKU_ALLOW_VIRTUAL_USER_SIGN_IN=passwordless-virtual-user-sign-in`. Any other value is
517
508
  ignored and warned about, so the hatch cannot be opened by copying a `true` from
518
509
  the line above.
519
510
 
520
511
  **Pass it in on any runtime without a populated `process.env`.** The gate reads
521
512
  the environment by default, which is enough for Node but not for a Worker: there
522
513
  the opt-in arrives as a binding and reaches user code through the variables
523
- service, so a gate left to `process.env` stays shut on exactly the stages a
524
- deployment targets. `allowSignIn` takes the value the caller already read —
525
- `await variables.get(ACTOR_SIGN_IN_OPT_IN_ENV)` — and is checked against the same
526
- literal, near-miss warning included. It is deliberately a value and not a flag:
527
- what opens the gate is still something the deployment set and an operator can
528
- read back out of it, never something compiled into the bundle. A value passed
529
- here is the one consulted, so the environment cannot quietly override what the
530
- stage was configured with.
514
+ service. `allowSignIn` takes the value the caller already read —
515
+ `await variables.get(VIRTUAL_USER_SIGN_IN_OPT_IN_ENV)` — and is checked against the same
516
+ literal, near-miss warning included. A value passed here is the one consulted, so
517
+ the environment cannot quietly override what the stage was configured with.
531
518
 
532
519
  **Signing in and provisioning are separate powers.** An unknown address becomes
533
- an `actor: true` row only under `pikku dev`. With the opt-in set, a stage signs
534
- in as the personas the deployment provisioned when it started and refuses
535
- everything else (`No actor account exists for that address`), so holding the
536
- secret on such a stage does not let anyone invent identities. Those rows are
537
- written by the fabric plugin when an operator asks to act as an address the
538
- stage has no account for, so provisioning needs no actor secret and works on a
539
- stage whose endpoint is shut.
540
-
541
- **`SCENARIO_ACTOR_SECRET` is a credential as powerful as the most privileged
542
- persona.** Provisioning grants declared roles to actor accounts, so an
543
- `admin` persona is an actor holding real admin — anyone with the secret _and_ the
544
- opt-in can take a session as one. Do not treat "actors only" as a licence to open
545
- the hatch in production.
520
+ a `virtualUser: true` row only under `pikku dev`. With the opt-in set, a stage signs
521
+ in as the virtual users the deployment provisioned when it started and refuses
522
+ everything else (`No virtual user account exists for that address`), so holding the
523
+ signing key on such a stage does not let anyone invent identities.
524
+
525
+ **The signing key is as powerful as the most privileged virtual user.** Provisioning
526
+ grants declared roles to virtual user accounts, so an `admin` virtual user holds
527
+ real admin — anyone with the private key _and_ the opt-in can take a session as one.
528
+ Keep it with the deployer; the app only ever needs the public half.
546
529
 
547
530
  Within that boundary, three properties bound the damage:
548
531
 
549
- - **It only ever signs in actors.** The plugin adds a `user.actor` boolean
532
+ - **It only ever signs in virtual users.** The plugin adds a `user.virtualUser` boolean
550
533
  column; an email matching a row without it is refused with `User is not an
551
- actor`. So the secret cannot take over a **real user's** account — the blast
552
- radius is the actor accounts and whatever roles they were granted.
553
- - **Unknown emails are created only under `pikku dev`**, flagged `actor: true`,
554
- so a local scenario declaring a new persona needs no seed step. Anywhere else
534
+ virtual user`. So a statement cannot take over a **real user's** account.
535
+ - **Unknown emails are created only under `pikku dev`**, flagged `virtualUser: true`,
536
+ so a local scenario declaring a new virtual user needs no seed step. Anywhere else
555
537
  the account has to have been provisioned at boot first.
556
- - **A credential is bound to one address**, so a leaked one is one synthetic
557
- account rather than the whole actor population; only the root is worth the
558
- paragraph above. The comparison is constant-time and length-hiding, so a wrong
559
- credential leaks neither the length nor a prefix of the right one.
538
+ - **A statement is bound to one virtual user, one app and a few minutes**, and can
539
+ be used once, so a leaked one is a single short-lived session.
560
540
 
561
- This is the endpoint `pikku scenario` signs its actors in through. The frontend
562
- switcher does not use it: it lists from `/sign-in/personas` and posts a persona
563
- id to `/sign-in/persona`, both served by
564
- `pikkuActor({ personaSignIn: { personas, featureFlags } })` with no credential — see
565
- `pikku-scenario` for the setup and `pikku-react` for `useDevActors()`.
541
+ This is the endpoint `pikku scenario` signs its virtual users in through. The frontend
542
+ does not use it: it posts a virtual user
543
+ id to `/sign-in/switch-virtual-user`, served by
544
+ `pikkuVirtualUser({ virtualUserSignIn: { virtualUsers } })` with no credential — see
545
+ `pikku-scenario` for the setup.
566
546
 
567
- ### Provisioning personas
547
+ ### Provisioning virtual users
568
548
 
569
- Anywhere but `pikku dev`, the accounts have to exist before anyone signs in. The
570
- stage creates them itself, from the personas you hand `pikkuFabric`:
549
+ Anywhere but `pikku dev`, the accounts have to exist before anyone signs in.
550
+ `provisionVirtualUsers` creates them from the generated virtual users:
571
551
 
572
552
  ```ts
573
- import { pikkuFabric } from '@pikku/better-auth'
553
+ import { provisionVirtualUsers } from '@pikku/better-auth'
574
554
  import {
575
- personaConfigs,
576
- personaEnvironments,
577
- } from '#pikku/pikku-personas.gen.js'
578
-
579
- pikkuFabric({
580
- publicKey,
581
- audience,
582
- scopeService,
583
- personas: {
584
- personas: personaConfigs,
585
- environments: personaEnvironments,
586
- },
587
- })
555
+ virtualUserConfigs,
556
+ virtualUserEnvironments,
557
+ } from '#pikku/pikku-virtual-users.gen.js'
558
+
559
+ await provisionVirtualUsers(
560
+ { auth, scopeService, logger },
561
+ { virtualUsers: virtualUserConfigs, environments: virtualUserEnvironments }
562
+ )
588
563
  ```
589
564
 
590
- There is nothing else to call and nothing to schedule. The plugin's operator
591
- endpoint resolves the address the caller wants to act as; a miss provisions the
592
- declaration and looks again. On a stage that already holds the persona that is
593
- one query, and the pass only runs when there is genuinely something absent to
594
- create.
565
+ `auth` only needs to resolve `{ $context }`, so the singleton `services.auth`
566
+ works as is. Call it from whatever route resolves the address a caller wants to
567
+ act as: a miss provisions the declaration and looks again. On a stage that
568
+ already holds the virtual user that is one query, and the pass only runs when there
569
+ is genuinely something absent to create.
595
570
 
596
571
  **Do not reach for `pikkuServerLifecycle`'s `afterStart` for this.** That hook is
597
572
  invoked by `pikku serve` and `pikku dev` and by nothing else — no deploy runtime
598
573
  calls it — so a stage on Workers or a serverless target that provisioned from
599
- `afterStart` provisioned nothing, and every persona signed in holding no roles.
574
+ `afterStart` provisioned nothing, and every virtual user signed in holding no roles.
600
575
 
601
576
  Provisioning runs where the database already is, which is the point: the CLI has
602
577
  no connection to a deployed environment's database — it resolves one from the
603
- local project config — so a `pikku persona sync staging` that wrote rows would
604
- write them to whatever database the checkout happened to point at. `pikku persona
578
+ local project config — so a `pikku virtual-user sync staging` that wrote rows would
579
+ write them to whatever database the checkout happened to point at. `pikku virtual-user
605
580
  sync <environment>` still exists, and reports who that environment will provision
606
581
  and why anyone was skipped, which is what you run _before_ the deploy.
607
582
 
608
- It creates missing accounts as `actor: true`, applies the roles each persona
583
+ It creates missing accounts as `virtualUser: true`, applies the roles each virtual user
609
584
  declares, and is additive — it never revokes. `PIKKU_ENV` (or an explicit
610
585
  `environment`) selects who is eligible, through the same rule that decides who
611
- may run there; an address already held by a real, non-actor user throws rather
612
- than being granted the persona's roles.
586
+ may run there; an address already held by a real, non-virtual-user user throws rather
587
+ than being granted the virtual user's roles.
613
588
 
614
- **Deleting a persona does not delete its account.** Being additive leaves a hole:
615
- the account keeps every role it was granted, and the actor endpoint authenticates
616
- on the `actor` column alone without consulting the declaration — so an `admin`
617
- persona nobody declares any more is still a live way in wherever that endpoint is
589
+ **Deleting a virtual user does not delete its account.** Being additive leaves a hole:
590
+ the account keeps every role it was granted, and the virtual user endpoint authenticates
591
+ on the `virtualUser` column alone without consulting the declaration — so an `admin`
592
+ virtual user nobody declares any more is still a live way in wherever that endpoint is
618
593
  open. By default provisioning warns about those accounts and changes nothing.
619
594
  `orphans: 'ban'` shuts them:
620
595
 
621
596
  ```ts
622
- pikkuFabric({
623
- publicKey,
624
- audience,
625
- scopeService,
626
- personas: {
627
- personas: personaConfigs,
628
- environments: personaEnvironments,
597
+ await provisionVirtualUsers(
598
+ { auth, scopeService, logger },
599
+ {
600
+ virtualUsers: virtualUserConfigs,
601
+ environments: virtualUserEnvironments,
629
602
  orphans: 'ban',
630
- },
631
- })
603
+ }
604
+ )
632
605
  ```
633
606
 
634
607
  It writes the same `banned` column the console's ban RPC writes (so it needs the
635
608
  `pikkuBan()` plugin wired, and says so if it isn't), revokes the account's sessions,
636
609
  and leaves the row, its grants and its history intact — provisioning lifts the
637
- ban again by itself if the persona comes back. Deleting is deliberately not
638
- offered: an actor row is referenced by whatever those scenarios did while it
610
+ ban again by itself if the virtual user comes back. Deleting is deliberately not
611
+ offered: a virtual user row is referenced by whatever those scenarios did while it
639
612
  existed.
640
613
 
641
614
  `report` is the default because a rolling deploy runs the new replica's
642
615
  provisioning while the old replica is still serving, so for the length of that
643
- overlap "no persona claims this" is a statement about the newer declaration only.
644
- A persona pinned to another environment counts as unclaimed here — it has no
616
+ overlap "no virtual user claims this" is a statement about the newer declaration only.
617
+ A virtual user pinned to another environment counts as unclaimed here — it has no
645
618
  business holding a signable account in an environment its own rule refuses it.
646
619
 
647
620
  ---
@@ -1,5 +1,11 @@
1
1
  # Pikku Jose (JWT Service)
2
2
 
3
+ ## Contents
4
+
5
+ - [Installation](#installation)
6
+ - [API Reference](#api-reference)
7
+ - [Usage Patterns](#usage-patterns)
8
+
3
9
 
4
10
  ## Installation
5
11
 
@@ -1,5 +1,11 @@
1
1
  # Pikku Machine Auth
2
2
 
3
+ ## Contents
4
+
5
+ - [Human path — `pikku login`](#human-path-pikku-login)
6
+ - [Machine path — API keys](#machine-path-api-keys)
7
+ - [Gotchas](#gotchas)
8
+
3
9
  Unified authentication for humans **and** machines against a Pikku + better-auth
4
10
  server. Two paths, two headers, one resolver:
5
11
 
@@ -1,5 +1,11 @@
1
1
  # Pikku Security (Authentication & Sessions)
2
2
 
3
+ ## Contents
4
+
5
+ - [Session Management](#session-management)
6
+ - [Built-in Auth Strategies](#built-in-auth-strategies)
7
+ - [Complete Example](#complete-example)
8
+
3
9
 
4
10
  ## Session Management
5
11
 
@@ -3,14 +3,16 @@ name: pikku-build
3
3
  description: >-
4
4
  Use to build on Pikku — turning a fresh scaffold into a working app (quick spike, real product,
5
5
  or a showcase that exercises every surface), adding a feature to an app that already exists, and
6
- the one-off cleanup right after a template is cloned. Covers the knowledge base, personas and
7
- roles, filing the work as changes and planning each changeset, the scenario that proves each one, theming, multi-app layouts and
8
- deploying. TRIGGER when: the user asks for an app to be built on Pikku, a freshly scaffolded
9
- project needs turning into a product, the user asks to add a feature or wire up a new endpoint
10
- in a working app, or a template was just cloned or scaffolded. DO NOT TRIGGER when: the user
11
- asks for a one-off edit to an existing function, asks about Pikku concepts (use pikku-concepts),
12
- or wants one specific surface explained rather than built (use that surface's skill).
13
- allowed-tools: Bash(yarn pikku meta *), Bash(yarn pikku all *), Bash(yarn tsc), Bash(git status *), Bash(git diff *), Bash(git switch *), Bash(git checkout *), Bash(git checkout -b *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *), Bash(git branch *), Bash(yarn pikku fabric report *), Bash(npx --no pikku fabric report *)
6
+ the one-off cleanup right after a template is cloned. Covers the knowledge base, virtual users and
7
+ roles, filing the work as changes, the scenario that proves each one, theming, multi-app
8
+ layouts and deploying. TRIGGER when: the user asks for an app to be built on
9
+ Pikku, a freshly scaffolded project needs turning into a product, the user asks to add a feature
10
+ or wire up a new endpoint in a working app, or a template was just cloned or scaffolded. DO NOT
11
+ TRIGGER when: the user asks for a one-off edit to an existing function, asks about Pikku concepts
12
+ (use pikku-concepts), wants one specific surface explained rather than built (use that surface's
13
+ skill), only needs the technical plan for one claimed changeset (use pikku-architect), or is
14
+ working items off a deployed stage's changes queue (use pikku-changes).
15
+ allowed-tools: Bash(yarn pikku meta *), Bash(yarn pikku all *), Bash(yarn tsc), Bash(git status *), Bash(git diff *), Bash(git switch *), Bash(git checkout *), Bash(git checkout -b *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *), Bash(git branch *), Bash(yarn pikku cloud report *), Bash(npx --no pikku cloud report *)
14
16
  argument-hint: '[feature description]'
15
17
  installGroups: [core]
16
18
  agent:
@@ -30,29 +32,30 @@ agent:
30
32
 
31
33
  ## Which mode
32
34
 
33
- | The situation | Read |
34
- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
35
- | A template was just cloned or scaffolded, and the tree still looks like one | `references/post-clone.md` first, then come back |
36
- | A real product, meant to be picked up by someone else | `references/app.md` — the default |
37
- | A spike, a throwaway demo, an idea nobody has committed to | `references/quick.md` |
38
- | A showcase meant to exercise every Pikku surface | `references/platform.md`, which is a delta on top of `references/app.md` |
39
- | A feature added to an app that already has its knowledge base | `references/feature.md` |
35
+ | The situation | Read |
36
+ | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
37
+ | A template was just cloned or scaffolded, and the tree still looks like one | `references/post-clone.md` first, then come back |
38
+ | A real product, meant to be picked up by someone else | `references/app.md` — the default |
39
+ | A spike, a throwaway demo, an idea nobody has committed to | `references/quick.md` |
40
+ | A showcase meant to exercise every Pikku surface | `references/platform.md`, read together with `references/app.md` |
41
+ | A feature added to an app that already has its knowledge base | `references/feature.md` |
42
+ | Writing or fixing the scenarios that prove a changeset (app §7, §7a) | `references/scenarios.md` |
43
+ | Before the first screen is built: a picture of the screens, a design direction | `references/design.md` |
44
+ | Authoring or changing the theme (app §8a) | `references/theming.md` |
45
+ | A screen needs data no function returns yet | `references/mocks.md` |
46
+ | Adding a second frontend (app §4), at the changeset that needs it | `references/multi-app.md` |
47
+ | Shipping an app as a desktop or Android app | `references/native-app.md` |
48
+ | Deploying, and the deploy-readiness contract (app §9) | `references/ship.md` |
49
+ | The request is built on an OpenAPI spec (auth mode, sign-in or connect screen) | `references/openapi.md` |
40
50
 
41
51
  **App is the default.** A small or toy-sounding app does not make it Quick;
42
52
  only an explicit signal of speed or throwaway-ness does. Platform is not "App
43
- plus more effort" — it is App plus a deliberate surface checklist, so read the
44
- base first and follow it in full rather than blending the two into one plan.
45
-
46
- The supporting references belong to whichever mode sends you to them:
47
- `references/multi-app.md` (a second frontend), `references/native-app.md`
48
- (shipping an app as a desktop or Android app), `references/design.md` (showing
49
- a picture of the screens first, committing to a design direction, and judging
50
- whether the screens realise it — read before the first screen is built, not
51
- after the last), `references/theming.md`
52
- (authoring the theme),
53
- `references/ship.md` (deploying, and the Fabric-readiness contract),
54
- `references/openapi.md` (an app on an OpenAPI spec: the auth mode, the
55
- auth-config format, and the sign-in or connect screen it implies).
53
+ plus more effort" — it is App plus a deliberate surface checklist: read both
54
+ files, follow the base in full, and do not blend the two into one plan.
55
+
56
+ Each reference names the topics it leans on but never tells you to open
57
+ another file; this table is the only routing. Open a reference when its row
58
+ matches, not up front.
56
59
 
57
60
  ## Bootstrap before anything else
58
61
 
@@ -152,11 +155,11 @@ generated functions through `ref()`.
152
155
 
153
156
  Whenever pikku or a skill costs you time — a command that failed on a fresh
154
157
  tree, a skill that described a flag the CLI does not have, generated code you
155
- had to fix by hand — file it then with `pikku fabric report --stdin`. The
156
- `pikku-report` skill has the fields. Leave out secrets, tokens and customer data.
158
+ had to fix by hand — file it then, the way the
159
+ `pikku-report` skill describes. That skill has the fields. Leave out secrets, tokens and customer data.
157
160
 
158
161
  Findings are held until hand-over. The last thing in the hand-over, after the
159
- app runs (and is deployed, if they chose to), is `pikku fabric report`: if it
162
+ app runs (and is deployed, if they chose to), is the `pikku-report` send step: if it
160
163
  lists findings and asks, ask the user — Yes, No, Always or Never — and run it
161
164
  again with `--consent <answer>`. If it says reporting is off, do not ask.
162
165
 
@@ -168,8 +171,8 @@ build makes or changes something the console can show, give the
168
171
  `http://localhost:<port>/console/...` link instead of describing it.
169
172
 
170
173
  Until the app is deployed that is the local open-source console, on the port
171
- `pikku dev` printed. Once it is on Fabric, link the Fabric console for the stage
172
- you are talking about instead; the `pikku-fabric` skill says which.
174
+ `pikku dev` printed. Once it is deployed, link the hosted console for the stage
175
+ you are talking about instead.
173
176
 
174
177
  | Level | Links | Code in the conversation |
175
178
  | ------------------ | --------------------------- | ------------------------ |
@@ -185,7 +188,7 @@ words and link to where they can see it.
185
188
  | Shows | Path |
186
189
  | --------------------- | -------------------------------------------------------------------------- |
187
190
  | Knowledge, plans | `/console/knowledge`, `/console/knowledge?id=<note path>` |
188
- | Personas | `/console/personas`, `/console/virtual-users?persona=<id>` |
191
+ | Virtual users | `/console/virtual-users`, `/console/virtual-users?virtual user=<id>` |
189
192
  | Roles and permissions | `/console/roles`, `/console/scopes` (only with `@pikku/addon-admin` wired) |
190
193
  | Scenarios and runs | `/console/scenarios?id=<id>`, `/console/scenarios?view=runs&run=<run id>` |
191
194
  | Workflows | `/console/workflow?id=<id>` |