@pikku/core 0.12.141 → 0.12.143

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 (251) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/errors/errors.d.ts +1 -1
  3. package/dist/errors/errors.js +1 -1
  4. package/dist/function/function-meta.types.d.ts +6 -6
  5. package/dist/function/functions.types.d.ts +2 -2
  6. package/dist/services/audit-service.d.ts +4 -4
  7. package/dist/services/feature-flag-service.d.ts +3 -3
  8. package/dist/services/{http-personas.d.ts → http-virtual-users.d.ts} +25 -47
  9. package/dist/services/{http-personas.js → http-virtual-users.js} +29 -53
  10. package/dist/services/in-memory-queue-service.d.ts +8 -1
  11. package/dist/services/in-memory-queue-service.js +67 -0
  12. package/dist/services/incoming-webhook-service.d.ts +2 -0
  13. package/dist/services/incoming-webhook-service.js +4 -0
  14. package/dist/services/index.d.ts +2 -2
  15. package/dist/services/index.js +1 -1
  16. package/dist/services/meta-service.d.ts +6 -6
  17. package/dist/services/meta-service.js +8 -8
  18. package/dist/{wirings/workflow → services}/scenario-cookie-jar.d.ts +2 -0
  19. package/dist/{wirings/workflow → services}/scenario-cookie-jar.js +18 -14
  20. package/dist/services/scheduler-service.d.ts +3 -0
  21. package/dist/services/scheduler-service.js +4 -0
  22. package/dist/services/system-role-guard.d.ts +1 -1
  23. package/dist/services/virtual-user-sign-in.d.ts +52 -0
  24. package/dist/services/virtual-user-sign-in.js +80 -0
  25. package/dist/services/virtual-user-statement.d.ts +31 -0
  26. package/dist/services/virtual-user-statement.js +24 -0
  27. package/dist/services/{personas-service.d.ts → virtual-users-service.d.ts} +25 -23
  28. package/dist/services/{personas-service.js → virtual-users-service.js} +3 -3
  29. package/dist/types/core.types.d.ts +11 -11
  30. package/dist/wirings/queue/index.d.ts +1 -1
  31. package/dist/wirings/queue/queue.types.d.ts +21 -0
  32. package/dist/wirings/queue/signed-queue-service.d.ts +4 -1
  33. package/dist/wirings/queue/signed-queue-service.js +3 -0
  34. package/dist/wirings/role/define-system-role.d.ts +1 -1
  35. package/dist/wirings/role/define-system-role.js +1 -1
  36. package/dist/wirings/scheduler/index.d.ts +1 -1
  37. package/dist/wirings/scheduler/scheduler.types.d.ts +12 -0
  38. package/dist/wirings/{actor-flow/actor-flow.types.d.ts → virtual-user/conversation/conversation.types.d.ts} +5 -5
  39. package/dist/wirings/virtual-user/conversation/index.d.ts +2 -0
  40. package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +36 -0
  41. package/dist/wirings/{actor-flow → virtual-user/conversation}/run-conversation.js +30 -30
  42. package/dist/wirings/virtual-user/define-virtual-users.d.ts +32 -0
  43. package/dist/wirings/{persona/define-personas.js → virtual-user/define-virtual-users.js} +9 -9
  44. package/dist/wirings/virtual-user/index.d.ts +30 -7
  45. package/dist/wirings/virtual-user/index.js +23 -4
  46. package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +8 -8
  47. package/dist/wirings/virtual-user/prepare-virtual-user-run.js +9 -9
  48. package/dist/wirings/virtual-user/run-virtual-user.d.ts +6 -6
  49. package/dist/wirings/virtual-user/run-virtual-user.js +10 -10
  50. package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts} +11 -11
  51. package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js} +24 -24
  52. package/dist/wirings/virtual-user/virtual-user-agents.d.ts +7 -7
  53. package/dist/wirings/virtual-user/virtual-user-agents.js +2 -2
  54. package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts} +10 -10
  55. package/dist/wirings/{persona/persona-app-scopes.js → virtual-user/virtual-user-app-scopes.js} +12 -12
  56. package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +3 -3
  57. package/dist/wirings/virtual-user/virtual-user-catalogue.js +2 -2
  58. package/dist/wirings/{persona/persona.types.d.ts → virtual-user/virtual-user-declaration.types.d.ts} +24 -24
  59. package/dist/wirings/virtual-user/virtual-user-derive.js +13 -7
  60. package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +1 -1
  61. package/dist/wirings/{persona/persona-email.d.ts → virtual-user/virtual-user-email.d.ts} +11 -11
  62. package/dist/wirings/{persona/persona-email.js → virtual-user/virtual-user-email.js} +19 -19
  63. package/dist/wirings/{persona/persona-environments.d.ts → virtual-user/virtual-user-environments.d.ts} +10 -10
  64. package/dist/wirings/{persona/persona-environments.js → virtual-user/virtual-user-environments.js} +21 -21
  65. package/dist/wirings/virtual-user/virtual-user-intents.d.ts +3 -3
  66. package/dist/wirings/virtual-user/virtual-user-intents.js +3 -3
  67. package/dist/wirings/{persona/persona-mailbox.d.ts → virtual-user/virtual-user-mailbox.d.ts} +4 -4
  68. package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +4 -4
  69. package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +42 -61
  70. package/dist/wirings/virtual-user/virtual-user-scaffold.js +69 -117
  71. package/dist/wirings/virtual-user/virtual-user-schedule-store.d.ts +15 -15
  72. package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +6 -6
  73. package/dist/wirings/virtual-user/virtual-user-schedule.js +11 -11
  74. package/dist/wirings/virtual-user/virtual-user-target.d.ts +6 -6
  75. package/dist/wirings/virtual-user/virtual-user-target.js +5 -5
  76. package/dist/wirings/virtual-user/virtual-user.types.d.ts +11 -11
  77. package/dist/wirings/virtual-user/virtual-user.types.js +1 -1
  78. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -9
  79. package/dist/wirings/workflow/pikku-scenario-service.d.ts +28 -28
  80. package/dist/wirings/workflow/pikku-scenario-service.js +77 -77
  81. package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
  82. package/dist/wirings/workflow/pikku-workflow-service.js +4 -4
  83. package/dist/wirings/workflow/scenario-prose.d.ts +8 -8
  84. package/dist/wirings/workflow/scenario-prose.js +12 -8
  85. package/dist/wirings/workflow/scenario-run.types.d.ts +13 -13
  86. package/dist/wirings/workflow/scenario-step.types.d.ts +23 -23
  87. package/dist/wirings/workflow/workflow.types.d.ts +2 -2
  88. package/knowledge/decisions/index.md +0 -2
  89. package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +29 -0
  90. package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +29 -0
  91. package/knowledge/decisions/internals/a-latency-budget-is-asserted-by-its-cause.md +32 -0
  92. package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +22 -0
  93. package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +28 -0
  94. package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +32 -0
  95. package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +12 -12
  96. package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +24 -0
  97. package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +34 -0
  98. package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +2 -2
  99. package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +3 -0
  100. package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +27 -0
  101. package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +26 -0
  102. package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +22 -0
  103. package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +22 -0
  104. package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +28 -0
  105. package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +24 -0
  106. package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +3 -3
  107. package/knowledge/decisions/internals/index.md +47 -31
  108. package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +10 -10
  109. package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +30 -0
  110. package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +23 -19
  111. package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +2 -2
  112. package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +12 -12
  113. package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +22 -0
  114. package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +24 -0
  115. package/knowledge/decisions/internals/{the-actor-prompt-says-json-because-of-json-object-mode.md → the-virtual-user-prompt-says-json-because-of-json-object-mode.md} +4 -4
  116. package/knowledge/decisions/internals/{the-persona-runtime-is-exported-from-the-persona-entry-point.md → the-virtual-user-runtime-is-exported-from-the-virtual-user-entry-point.md} +8 -8
  117. package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +43 -0
  118. package/knowledge/decisions/internals/{actor-flow-drives-the-target-through-a-transport-seam.md → virtual-user-conversation-drives-the-target-through-a-transport-seam.md} +6 -6
  119. package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +25 -0
  120. package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +23 -0
  121. package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +30 -0
  122. package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +5 -5
  123. package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +2 -2
  124. package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +4 -4
  125. package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
  126. package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +2 -2
  127. package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +1 -1
  128. package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +1 -1
  129. package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +26 -0
  130. package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +20 -0
  131. package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +23 -0
  132. package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +16 -0
  133. package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +26 -0
  134. package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +32 -0
  135. package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +27 -0
  136. package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +33 -0
  137. package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +25 -0
  138. package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +10 -5
  139. package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +27 -0
  140. package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +26 -0
  141. package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +25 -0
  142. package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +29 -0
  143. package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +25 -0
  144. package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +24 -0
  145. package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +29 -0
  146. package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +30 -0
  147. package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +38 -0
  148. package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +23 -0
  149. package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +27 -0
  150. package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +41 -0
  151. package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +25 -0
  152. package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +32 -0
  153. package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +20 -0
  154. package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +26 -0
  155. package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +33 -0
  156. package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +16 -0
  157. package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +26 -0
  158. package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +38 -0
  159. package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +31 -0
  160. package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +18 -0
  161. package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +32 -0
  162. package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +24 -0
  163. package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +26 -0
  164. package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +27 -0
  165. package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +25 -0
  166. package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +18 -0
  167. package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +29 -0
  168. package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +22 -0
  169. package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +29 -0
  170. package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +27 -0
  171. package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +25 -0
  172. package/knowledge/decisions/security/index.md +88 -24
  173. package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +21 -0
  174. package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +38 -0
  175. package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +18 -0
  176. package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +22 -0
  177. package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +26 -0
  178. package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +24 -0
  179. package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +35 -0
  180. package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +31 -0
  181. package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +22 -0
  182. package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +34 -0
  183. package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +26 -0
  184. package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +24 -0
  185. package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +16 -0
  186. package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +21 -0
  187. package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +24 -0
  188. package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +34 -0
  189. package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +16 -0
  190. package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +29 -0
  191. package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +23 -0
  192. package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +25 -0
  193. package/knowledge/decisions/security/{actor-flow-missing-approval-decisions-default-to-denied.md → virtual-user-conversation-missing-approval-decisions-default-to-denied.md} +7 -7
  194. package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +18 -0
  195. package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +25 -0
  196. package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +42 -0
  197. package/knowledge/decisions/security/{actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md → virtual-user-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md} +10 -8
  198. package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +29 -0
  199. package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +18 -0
  200. package/knowledge/decisions/security/{workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md → workflow-scenario-sessions-are-isolated-per-virtual-user-and-per-scenario.md} +6 -6
  201. package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +5 -5
  202. package/knowledge/decisions/security/{workflow-actor-steps-always-use-the-real-transport.md → workflow-virtual-user-steps-always-use-the-real-transport.md} +11 -11
  203. package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +18 -0
  204. package/knowledge/index.md +0 -2
  205. package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +24 -0
  206. package/knowledge/questions/dev-quick-login-as-impersonation.md +25 -0
  207. package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +26 -0
  208. package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +27 -0
  209. package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +26 -0
  210. package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +26 -0
  211. package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +34 -0
  212. package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +20 -0
  213. package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +24 -0
  214. package/knowledge/questions/does-the-store-session-match-the-other-two.md +19 -0
  215. package/knowledge/questions/index.md +25 -2
  216. package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +21 -0
  217. package/knowledge/questions/live-scheduled-tests-may-write.md +19 -0
  218. package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +23 -0
  219. package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +32 -0
  220. package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +25 -0
  221. package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +23 -0
  222. package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +35 -0
  223. package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +23 -0
  224. package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +27 -0
  225. package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +26 -0
  226. package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +20 -0
  227. package/knowledge/questions/staging-impersonation-rules.md +27 -0
  228. package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +33 -0
  229. package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +20 -0
  230. package/knowledge/questions/what-an-impersonated-session-carries.md +22 -0
  231. package/package.json +2 -3
  232. package/src/public-surface.json +38 -47
  233. package/dist/services/persona-actor-secret.d.ts +0 -38
  234. package/dist/services/persona-actor-secret.js +0 -39
  235. package/dist/services/persona-sign-in.d.ts +0 -117
  236. package/dist/services/persona-sign-in.js +0 -154
  237. package/dist/wirings/actor-flow/index.d.ts +0 -2
  238. package/dist/wirings/actor-flow/run-conversation.d.ts +0 -36
  239. package/dist/wirings/persona/define-personas.d.ts +0 -32
  240. package/dist/wirings/persona/index.d.ts +0 -23
  241. package/dist/wirings/persona/index.js +0 -19
  242. package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +0 -23
  243. package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +0 -25
  244. package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +0 -43
  245. package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +0 -31
  246. package/knowledge/decisions/security/an-actor-credential-is-derived-per-persona.md +0 -42
  247. package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +0 -26
  248. /package/dist/wirings/{actor-flow/actor-flow.types.js → virtual-user/conversation/conversation.types.js} +0 -0
  249. /package/dist/wirings/{actor-flow → virtual-user/conversation}/index.js +0 -0
  250. /package/dist/wirings/{persona/persona.types.js → virtual-user/virtual-user-declaration.types.js} +0 -0
  251. /package/dist/wirings/{persona/persona-mailbox.js → virtual-user/virtual-user-mailbox.js} +0 -0
@@ -0,0 +1,35 @@
1
+ ---
2
+ type: question
3
+ title: Should a Bearer token that does not verify be anonymous or refused?
4
+ description: authBearer refuses a malformed scheme with InvalidSessionError but lets a well-formed wrong token through as anonymous, and no comment says why
5
+ tags: core, auth, middleware
6
+ ---
7
+
8
+ # Should a Bearer token that does not verify be anonymous or refused?
9
+
10
+ In `authBearer` (`packages/core/src/middleware/auth-bearer.ts`) the code splits
11
+ the header into scheme and token. A scheme other than `Bearer`, or an empty
12
+ token, throws `InvalidSessionError`. A `Bearer` token that then fails the static
13
+ constant-time compare, or decodes to nothing, does not throw. It calls `next()`
14
+ with no session, and the function gate decides what happens next.
15
+
16
+ Tests back both halves: "should throw InvalidSessionError when scheme is not
17
+ Bearer" and "should not set session when static token does not match". Neither
18
+ says why a malformed header is refused while a wrong credential is anonymous.
19
+
20
+ The JWT branch is different. When no static token is configured and a `jwtService`
21
+ is wired, `authBearer` calls `jwtService.decode(bearerToken)` with no try/catch
22
+ (`auth-bearer.ts`, around line 72). Core ships only the `JWTService` interface
23
+ (`packages/core/src/services/jwt-service.ts`). The implementation in this repo is
24
+ `JoseJWTService` (`packages/services/jose/src/jose-jwt-service.ts`). Its `decode`
25
+ calls `jose.jwtVerify` with HS256 only, so a bad signature or an expired token
26
+ throws, and so does an unknown key id (`Missing secret for id`). A token with no
27
+ key id is verified against the current secret. Because `authBearer` does not
28
+ catch that throw, with this implementation a bad JWT is a request error, not an
29
+ anonymous request. Other `JWTService` implementations are not in this repo, so
30
+ they are not covered by this.
31
+
32
+ What is asked: is an unverifiable Bearer token meant to be treated as no session,
33
+ so that a public function still answers it, or should it be refused at the
34
+ middleware the way a malformed header is? The two cases differ only in the
35
+ credential being well-formed, and the code makes no stated choice about it.
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: question
3
+ title: Should an outgoing webhook go out unsigned when its configured signing secret resolves empty?
4
+ description: When config.webhook.secret names a secret that resolves to nothing, the delivery is logged as unsigned and enqueued without a signature
5
+ tags: core, webhooks, signing
6
+ ---
7
+
8
+ # Should an outgoing webhook go out unsigned when its configured signing secret resolves empty?
9
+
10
+ `prepareDelivery` in `packages/core/src/services/queue-webhook-service.ts:52-66`
11
+ reads the signing secret from `config.webhook.secret` when the caller passed none
12
+ (`:53-55`). If that secret resolves to nothing, the code logs an error that says
13
+ the outgoing webhooks "will be sent UNSIGNED" (`:56-60`) and carries on. The
14
+ signature header is added only when a secret is present (`:62-66`), so the job
15
+ is enqueued without one.
16
+
17
+ The existing note `webhook-bodies-are-signed-before-they-are-enqueued.md` covers
18
+ signing at enqueue time and the key staying out of the queue payload. It does not
19
+ cover this fallback.
20
+
21
+ **Question:** should a configured but empty signing secret send the delivery
22
+ unsigned with a log line, or refuse to enqueue it? The code takes the first
23
+ option. Nothing in the file records a reason, and the only signal is the log.
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: question
3
+ title: Should HTTP virtual user sessions reset between scenarios in one CLI run?
4
+ description: The CLI builds HTTP virtual users once and reuses them for every scenario, and only the browser side is reset between scenarios
5
+ tags: core, cli, scenario, virtual-users
6
+ ---
7
+
8
+ # Should HTTP virtual user sessions reset between scenarios in one CLI run?
9
+
10
+ In `packages/cli/src/functions/commands/scenario.ts`, `createHttpVirtualUsers`
11
+ runs once (around line 314), and that same `virtualUsers` object is passed to
12
+ every `workflowService.startWorkflow` call for each scenario (around line 745).
13
+ Each `HttpVirtualUser` holds its own cookie jar and its `signedIn` flag for the
14
+ life of the process.
15
+
16
+ Before each scenario the command calls `browserLifecycle.reset()` (around line
17
+ 723), which discards the browser's per-scenario state. Nothing resets the HTTP
18
+ jars: `scenario.ts` never calls `clear` or `signOut`, and
19
+ `PikkuScenarioService.detachRunContext` (`packages/core/src/wirings/workflow/pikku-scenario-service.ts`)
20
+ drops the run's map entry but not the jars inside the shared object.
21
+
22
+ So an HTTP virtual user that signed in during one scenario is still signed in for
23
+ the next, unless a 401 forces a re-login. The browser side is reset to keep one
24
+ scenario from leaving the next signed in as somebody else, as
25
+ [Scenario sessions are isolated per virtual user and reset between scenarios](../decisions/security/workflow-scenario-sessions-are-isolated-per-virtual-user-and-per-scenario.md)
26
+ records. What is asked: is the HTTP side meant to carry sessions across scenarios
27
+ as a speed measure, or should it be reset the same way?
@@ -0,0 +1,26 @@
1
+ ---
2
+ type: question
3
+ title: Should the scenario cookie jar scope cookies to the API origin?
4
+ description: createCookieJar sends every held cookie to whatever URL it is given, keeps only name and value from Set-Cookie, and stamps the API origin on all calls
5
+ tags: core, scenario, cookies
6
+ ---
7
+
8
+ # Should the scenario cookie jar scope cookies to the API origin?
9
+
10
+ `createCookieJar` in `packages/core/src/services/scenario-cookie-jar.ts`
11
+ takes one `apiUrl` and reads its origin once. Its `fetch` ignores the host of
12
+ `input`. It sets the `Origin` header to that origin on every call, and it adds
13
+ every held cookie to every call whatever URL the call goes to.
14
+
15
+ From `Set-Cookie` it keeps only the `name=value` pair. `Path`, `Domain`,
16
+ `Secure`, `Max-Age` and `Expires` are discarded. A cookie is removed only when its
17
+ value is empty, so `session=x; Max-Age=0` would be stored as a live cookie.
18
+
19
+ The tests in `scenario-cookie-jar.test.ts` send every request to one local
20
+ server, so none of this is exercised. The `Origin` stamp is tested on purpose
21
+ ("stamps the origin Better Auth checks on state-changing calls").
22
+
23
+ What is asked: is the jar meant only to talk to its own API, in which case the
24
+ cross-host behaviour does not matter, or should it honour cookie scope? The code
25
+ does not say, and the browser-side equivalent in `packages/playwright/src/provider.ts`
26
+ plants cookies on `appUrl` instead.
@@ -0,0 +1,20 @@
1
+ ---
2
+ type: question
3
+ title: Should voiceInput fetch audio URLs when no host allowlist is configured?
4
+ description: With no allowedAudioHosts, the audio URL is fetched through safeFetch with no allowlist, so any public host is reachable
5
+ tags: core, agent, voice, ssrf
6
+ ---
7
+
8
+ # Should voiceInput fetch audio URLs when no host allowlist is configured?
9
+
10
+ `fetchAsUint8Array` in `packages/core/src/wirings/agent/voice-input.ts:57-61` calls
11
+ `safeFetch` with `{ allowedHosts: allowedAudioHosts }`. That value is optional.
12
+ The caller passes `config.allowedAudioHosts` for a URL audio part (`:136-138`).
13
+
14
+ In `packages/core/src/utils/safe-fetch.ts:269-273`, with no allowlist the only
15
+ host check is the private-host refusal. So a URL that is not on a list, but is
16
+ public, is fetched when no list is configured.
17
+
18
+ **Question:** should `voiceInput` require `allowedAudioHosts` before it fetches a
19
+ URL, or is the private-host block meant to be enough? The code takes the second
20
+ option. No note or comment in the file says which is intended.
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: question
3
+ title: What rules govern impersonation on staging and live?
4
+ description: Staged impersonation by a signed-in admin is wanted; the switch, read-only default, expiry and logging rules are proposed but not agreed
5
+ tags: auth, services
6
+ ---
7
+
8
+ # What rules govern impersonation on staging and live?
9
+
10
+ Wanted: a signed-in admin on a deployed environment can impersonate a virtual user,
11
+ using the header on the normal cookie session. The code already applies
12
+ impersonation on that path (`auth-session.ts`, the human branch).
13
+
14
+ Not yet agreed:
15
+
16
+ - **Per-environment switch.** On for staging; off for production. The request
17
+ is that it is off unless turned on for that environment.
18
+ - **Read-only by default.** Writes made while impersonating change real data.
19
+ - **Expiry.** An impersonation ends after a short time.
20
+ - **Logging.** Each impersonated request records the real admin and the target.
21
+ Today the impersonation line is `info` and does not record the admin against
22
+ each request.
23
+ - **Banner.** The panel shows "viewing as X".
24
+
25
+ Also unverified: the default impersonation gate reads the owner's scopes
26
+ (`userHoldsScopes(result.user.id, …)`), not the scopes of the key or session that
27
+ made the request.
@@ -0,0 +1,33 @@
1
+ ---
2
+ type: question
3
+ title: Pikku Studio Cloud has to create, hold and deliver the stage virtual user keys
4
+ description: This repo defines the variables and verifies statements; generating a pair per stage and signing for stage callers is Pikku Studio Cloud work that has not been done or checked
5
+ tags: services
6
+ ---
7
+
8
+ # Pikku Studio Cloud has to create, hold and deliver the stage virtual user keys
9
+
10
+ The Pikku Studio Cloud repository is not on the machine this was built on, so none of the
11
+ following is built or confirmed. Pikku Studio Cloud must:
12
+
13
+ 1. Generate one Ed25519 key pair per stage at creation and keep the private key in its
14
+ backend, never on the stage.
15
+ 2. Set the stage variable `VIRTUAL_USER_VERIFY_KEY` to the public key (SPKI DER,
16
+ base64), and accept that name, which starts with neither `PIKKU_` nor `PIKKUSTUDIO_`.
17
+ 3. Set `VIRTUAL_USER_API_URL` on every stage that runs virtual users, to the public
18
+ URL including any HTTP prefix.
19
+ 4. Sign statements with the stage's private key for callers acting on a stage, or hand
20
+ the key to the CLI as `VIRTUAL_USER_SIGNING_KEY` through the channel it already uses
21
+ for stage credentials.
22
+ 5. Stop sending the operator token field in `runVirtualUser` requests; the field no longer
23
+ exists.
24
+ 6. For the stage switcher, sign a statement for the chosen virtual user and pass it to
25
+ `/sign-in/switch-virtual-user` as `statement`.
26
+
27
+ Unverified: that Pikku Studio Cloud accepts an arbitrary unreserved variable per stage, that a
28
+ Worker can fetch its own public hostname (the run needs this on Workers), and that the
29
+ `audience` plugin option copes with a proxy that rewrites the origin. A run without a
30
+ sign-in path reads roles from the `getMyScopes` RPC and falls back to
31
+ `/auth/get-session`.
32
+
33
+ **What would settle it:** Pikku Studio Cloud landing the six items, and one stage run end to end.
@@ -0,0 +1,20 @@
1
+ ---
2
+ type: question
3
+ title: A virtual user statement can be replayed once per instance
4
+ description: The one-time id store is an in-memory map, so on several instances or Worker isolates each accepts a statement once
5
+ tags: services
6
+ ---
7
+
8
+ # A virtual user statement can be replayed once per instance
9
+
10
+ `JoseVirtualUserStatementVerifier` remembers accepted `jti` values in a per-process
11
+ map. On a single node that makes a statement single-use. On several instances, or
12
+ Cloudflare Workers where isolates share nothing, a captured statement can be accepted
13
+ once per instance within its few-minute life.
14
+
15
+ The lifetime ceiling (300 seconds) and the signed audience bound the damage; neither
16
+ removes it.
17
+
18
+ **What would settle it:** a shared store — the Better Auth `verification` table is
19
+ already there — behind the verifier, or a decision that the bound is acceptable for
20
+ stages. Not built.
@@ -0,0 +1,22 @@
1
+ ---
2
+ type: question
3
+ title: What should an impersonated session carry?
4
+ description: An impersonated session without a mapSession is just { userId }, dropping the orgId and virtualUser fields a normal session has; nobody has said whether that is intended
5
+ tags: better-auth, impersonation, sessions
6
+ ---
7
+
8
+ # What should an impersonated session carry?
9
+
10
+ When impersonation resolves and the app has no `mapSession`,
11
+ `resolveImpersonatedSession` returns `{ userId: target }`
12
+ (`packages/services/better-auth/src/auth-session-impersonation.ts`). A normal
13
+ session from the same middleware carries more: the `orgId` `defaultSession` reads
14
+ from the active organization, the `virtualUser` flag `stampVirtualUserFlag` adds,
15
+ and whatever `mapSession` adds.
16
+
17
+ So an impersonated request can behave differently from the same user signing in
18
+ directly. The code makes the choice; no comment or note defends it.
19
+
20
+ Open: should the impersonated session copy the target's `orgId` and `virtualUser`
21
+ stamp, or is a bare id intended? Answering it decides whether org-scoped reads
22
+ work under impersonation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.12.141",
3
+ "version": "0.12.143",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/pikkujs/pikku.git",
@@ -42,7 +42,7 @@
42
42
  "./scenario/coverage": "./dist/wirings/workflow/scenario-coverage.js",
43
43
  "./workflow/timeline": "./dist/wirings/workflow/run-timeline.js",
44
44
  "./workflow/types": "./dist/wirings/workflow/workflow.types.js",
45
- "./actor-flow": "./dist/wirings/actor-flow/index.js",
45
+ "./virtual-user/conversation": "./dist/wirings/virtual-user/conversation/index.js",
46
46
  "./virtual-user": "./dist/wirings/virtual-user/index.js",
47
47
  "./channel/local": "./dist/wirings/channel/local/index.js",
48
48
  "./channel/serverless": "./dist/wirings/channel/serverless/index.js",
@@ -67,7 +67,6 @@
67
67
  "./credential": "./dist/wirings/credential/index.js",
68
68
  "./scope": "./dist/wirings/scope/index.js",
69
69
  "./role": "./dist/wirings/role/index.js",
70
- "./persona": "./dist/wirings/persona/index.js",
71
70
  "./secret": "./dist/wirings/secret/index.js",
72
71
  "./variable": "./dist/wirings/variable/index.js",
73
72
  "./webhook": "./dist/wirings/webhook/index.js",
@@ -84,12 +84,12 @@
84
84
  "./scenario": [
85
85
  "PikkuScenarioService",
86
86
  "SCENARIO_SURFACES",
87
- "ScenarioActorRequired",
88
87
  "ScenarioBrowserUnavailable",
89
88
  "ScenarioHookError",
90
89
  "ScenarioNoSurfaceBinding",
91
90
  "ScenarioNoWitness",
92
91
  "ScenarioUnwitnessedAssertion",
92
+ "ScenarioVirtualUserRequired",
93
93
  "ScenarioWitnessDisagreement",
94
94
  "addFeature",
95
95
  "composeStepProse",
@@ -111,44 +111,64 @@
111
111
  "reconstructStateAt"
112
112
  ],
113
113
  "./workflow/types": [],
114
- "./actor-flow": [
114
+ "./virtual-user/conversation": [
115
115
  "runConversation"
116
116
  ],
117
117
  "./virtual-user": [
118
+ "APP_SCOPE_ROOT",
118
119
  "DEFAULT_MAX_INTERVAL_MS",
119
120
  "DEFAULT_MIN_INTERVAL_MS",
120
121
  "DISPOSITIONS",
122
+ "HttpVirtualUser",
121
123
  "IntentStack",
122
124
  "PRODUCTION_DISPOSITION",
123
125
  "STALE_RUN_AFTER_MS",
124
126
  "VIRTUAL_USER_VARIABLES",
127
+ "appScopeId",
128
+ "authMount",
129
+ "buildAppScopeDefinition",
125
130
  "catalogueClassification",
126
131
  "catalogueLookup",
132
+ "createCookieJar",
133
+ "createHttpVirtualUsers",
134
+ "defineVirtualUsers",
127
135
  "deriveCatalogue",
128
136
  "deriveIntents",
129
137
  "dispositionProfile",
130
138
  "executeVirtualUserRun",
131
- "intentsForPersona",
139
+ "inProcessSignIn",
140
+ "intentsForVirtualUser",
132
141
  "isDue",
133
142
  "isReadOnly",
143
+ "isRunnableVirtualUser",
134
144
  "logVirtualUserTick",
135
145
  "nextRunAt",
136
- "personaScopes",
137
- "personaVirtualUserTarget",
146
+ "postScenarioJson",
138
147
  "prepareVirtualUserRun",
139
148
  "reachableCatalogue",
149
+ "readScenarioHttpResponse",
140
150
  "requireVirtualUserRunStore",
141
151
  "requireVirtualUserScheduleStore",
152
+ "roleMismatchMessage",
142
153
  "runVirtualUser",
143
- "runnablePersona",
154
+ "runnableVirtualUser",
155
+ "runnableVirtualUsers",
144
156
  "serializeVirtualUserRun",
145
157
  "serializeVirtualUserSchedule",
146
158
  "serializeVirtualUserSteps",
147
- "signInPathFor",
148
159
  "startVirtualUserRun",
160
+ "statementSignIn",
149
161
  "tickVirtualUserSchedules",
150
162
  "unreachableCatalogue",
163
+ "validateAndBuildVirtualUsersMeta",
164
+ "verifyVirtualUserRoles",
165
+ "virtualUserEmail",
166
+ "virtualUserEmails",
167
+ "virtualUserEnvironmentErrors",
168
+ "virtualUserEnvironmentRefusal",
151
169
  "virtualUserScheduleRunInput",
170
+ "virtualUserScopes",
171
+ "virtualUserTarget",
152
172
  "writeVirtualUserSchedule"
153
173
  ],
154
174
  "./channel/local": [
@@ -352,36 +372,6 @@
352
372
  "flattenSystemRoleDefinitions",
353
373
  "validateAndBuildSystemRoleDefinitionsMeta"
354
374
  ],
355
- "./persona": [
356
- "ACTOR_ROOT_SECRET_MIN_LENGTH",
357
- "ACTOR_SECRET_INFO",
358
- "ACTOR_SECRET_NAME",
359
- "APP_SCOPE_ROOT",
360
- "ActorSignIn",
361
- "HttpPersona",
362
- "IMPERSONATE_USER_ID_HEADER",
363
- "OperatorSignIn",
364
- "actorSecretSubject",
365
- "appScopeId",
366
- "authMount",
367
- "buildAppScopeDefinition",
368
- "createHttpPersonas",
369
- "definePersonas",
370
- "deriveActorSecret",
371
- "establishOperatorSession",
372
- "isRunnablePersona",
373
- "personaEmail",
374
- "personaEmails",
375
- "personaEnvironmentErrors",
376
- "personaEnvironmentRefusal",
377
- "postScenarioJson",
378
- "readScenarioHttpResponse",
379
- "roleMismatchMessage",
380
- "runnablePersonas",
381
- "validateAndBuildPersonasMeta",
382
- "verifyActorSecret",
383
- "verifyPersonaRoles"
384
- ],
385
375
  "./secret": [
386
376
  "defineSecret",
387
377
  "deriveOAuth2AppSecrets",
@@ -447,9 +437,6 @@
447
437
  "isExpectedError"
448
438
  ],
449
439
  "./services": [
450
- "ACTOR_ROOT_SECRET_MIN_LENGTH",
451
- "ACTOR_SECRET_INFO",
452
- "ACTOR_SECRET_NAME",
453
440
  "ConsoleLogger",
454
441
  "DEFAULT_WEBHOOK_RETRIES",
455
442
  "InMemoryAgentRunStateService",
@@ -488,13 +475,18 @@
488
475
  "TypedCredentialService",
489
476
  "TypedSecretService",
490
477
  "TypedVariablesService",
478
+ "VIRTUAL_USER_IN_PROCESS_ENDPOINT",
479
+ "VIRTUAL_USER_SIGNING_KEY_VARIABLE",
480
+ "VIRTUAL_USER_STATEMENT_ALGORITHM",
481
+ "VIRTUAL_USER_STATEMENT_MAX_TTL_SECONDS",
482
+ "VIRTUAL_USER_STATEMENT_TTL_SECONDS",
483
+ "VIRTUAL_USER_VERIFY_KEY_VARIABLE",
484
+ "VirtualUserStatementError",
491
485
  "WebhookService",
492
- "actorSecretSubject",
493
486
  "assertSecretAllowedForHost",
494
487
  "createInvocationAudit",
495
488
  "createMiddlewareSessionWireProps",
496
489
  "createStubProxy",
497
- "deriveActorSecret",
498
490
  "getStubTracker",
499
491
  "holdLease",
500
492
  "isTestRun",
@@ -504,7 +496,6 @@
504
496
  "renderEmail",
505
497
  "spy",
506
498
  "stub",
507
- "verifyActorSecret",
508
499
  "withoutSecrets"
509
500
  ],
510
501
  "./flag": [
@@ -545,15 +536,15 @@
545
536
  "createLocalContentRequestHandler",
546
537
  "verifySignedContentRequest"
547
538
  ],
539
+ "./services/temporary-file-service": [
540
+ "TemporaryFileInstance",
541
+ "TemporaryFileService"
542
+ ],
548
543
  "./services/file-scenario-run-store": [
549
544
  "FileScenarioRunStore",
550
545
  "scenarioArtifactContentType",
551
546
  "scenarioRunSummary"
552
547
  ],
553
- "./services/temporary-file-service": [
554
- "TemporaryFileInstance",
555
- "TemporaryFileService"
556
- ],
557
548
  "./crypto-utils": [
558
549
  "MIN_KEY_MATERIAL_LENGTH",
559
550
  "REMOTE_SESSION_INFO",
@@ -1,38 +0,0 @@
1
- /** The name the root secret is held under, used only in error messages. */
2
- export declare const ACTOR_SECRET_NAME = "SCENARIO_ACTOR_SECRET";
3
- /**
4
- * Namespaces the derivation so the same root secret used for anything else
5
- * produces different values. See knowledge/crypto.md.
6
- */
7
- export declare const ACTOR_SECRET_INFO = "pikku:actor-sign-in";
8
- /** The root must be strong: every persona's credential is derived from it. */
9
- export declare const ACTOR_ROOT_SECRET_MIN_LENGTH = 32;
10
- /**
11
- * What the derivation is bound to. Lowercased because the sign-in endpoint
12
- * looks the user up by lowercased address, and a credential that verified
13
- * against a different string than the row it opens is a credential for nothing.
14
- */
15
- export declare const actorSecretSubject: (email: string) => string;
16
- /**
17
- * One persona's actor credential: `HMAC-SHA256(root, email)`, base64url.
18
- *
19
- * The root secret is not itself a valid credential and never travels: what a
20
- * scenario run, a CI job or a virtual user is handed is the derived value for
21
- * the one address it is entitled to. Presenting it for any other address fails,
22
- * so a leaked credential is worth exactly one synthetic account rather than the
23
- * whole actor population.
24
- *
25
- * Deterministic, so nothing is stored and nothing is provisioned — the target
26
- * re-derives the expected value from the address being signed in as. Rotating
27
- * the root invalidates every derived credential at once, which is the property
28
- * a per-persona secret table would have to implement by hand.
29
- */
30
- export declare const deriveActorSecret: (rootSecret: string, email: string) => Promise<string>;
31
- /**
32
- * Whether `presented` is the credential for `email` under `rootSecret`.
33
- *
34
- * False — never throws — for a malformed, truncated or mismatched value, and
35
- * the comparison is WebCrypto's own HMAC verify, so it does not exit early on
36
- * the first differing byte.
37
- */
38
- export declare const verifyActorSecret: (rootSecret: string, email: string, presented: string) => Promise<boolean>;
@@ -1,39 +0,0 @@
1
- import { MIN_KEY_MATERIAL_LENGTH, signWithKeyMaterial, verifyWithKeyMaterial, } from '../crypto-utils.js';
2
- /** The name the root secret is held under, used only in error messages. */
3
- export const ACTOR_SECRET_NAME = 'SCENARIO_ACTOR_SECRET';
4
- /**
5
- * Namespaces the derivation so the same root secret used for anything else
6
- * produces different values. See knowledge/crypto.md.
7
- */
8
- export const ACTOR_SECRET_INFO = 'pikku:actor-sign-in';
9
- /** The root must be strong: every persona's credential is derived from it. */
10
- export const ACTOR_ROOT_SECRET_MIN_LENGTH = MIN_KEY_MATERIAL_LENGTH;
11
- /**
12
- * What the derivation is bound to. Lowercased because the sign-in endpoint
13
- * looks the user up by lowercased address, and a credential that verified
14
- * against a different string than the row it opens is a credential for nothing.
15
- */
16
- export const actorSecretSubject = (email) => email.trim().toLowerCase();
17
- /**
18
- * One persona's actor credential: `HMAC-SHA256(root, email)`, base64url.
19
- *
20
- * The root secret is not itself a valid credential and never travels: what a
21
- * scenario run, a CI job or a virtual user is handed is the derived value for
22
- * the one address it is entitled to. Presenting it for any other address fails,
23
- * so a leaked credential is worth exactly one synthetic account rather than the
24
- * whole actor population.
25
- *
26
- * Deterministic, so nothing is stored and nothing is provisioned — the target
27
- * re-derives the expected value from the address being signed in as. Rotating
28
- * the root invalidates every derived credential at once, which is the property
29
- * a per-persona secret table would have to implement by hand.
30
- */
31
- export const deriveActorSecret = async (rootSecret, email) => signWithKeyMaterial(ACTOR_SECRET_NAME, rootSecret, ACTOR_SECRET_INFO, actorSecretSubject(email));
32
- /**
33
- * Whether `presented` is the credential for `email` under `rootSecret`.
34
- *
35
- * False — never throws — for a malformed, truncated or mismatched value, and
36
- * the comparison is WebCrypto's own HMAC verify, so it does not exit early on
37
- * the first differing byte.
38
- */
39
- export const verifyActorSecret = async (rootSecret, email, presented) => verifyWithKeyMaterial(ACTOR_SECRET_NAME, rootSecret, ACTOR_SECRET_INFO, actorSecretSubject(email), presented);
@@ -1,117 +0,0 @@
1
- import type { ResolvedPersona } from './personas-service.js';
2
- import type { ScenarioCookieJar } from '../wirings/workflow/scenario-cookie-jar.js';
3
- /**
4
- * The header `resolveImpersonatedSession` reads the target user id from.
5
- *
6
- * A wire value rather than a shared import: the reader lives in
7
- * `@pikku/services-better-auth`, which depends on core, so core cannot import
8
- * it back. The two agree by protocol, the way an HTTP header always does.
9
- */
10
- export declare const IMPERSONATE_USER_ID_HEADER = "x-pikku-impersonate-user-id";
11
- /**
12
- * How a persona obtains a session on the target, and what every later request
13
- * needs to carry to keep acting as them.
14
- *
15
- * Two answers exist because the two environments have opposite trust models,
16
- * not because one is a fallback for the other. See {@link ActorSignIn} and
17
- * {@link OperatorSignIn}.
18
- */
19
- export interface PersonaSignIn {
20
- /**
21
- * Establish a session in `jar`. Throws on failure with a message naming the
22
- * persona, since a run that continues unauthenticated fails later and
23
- * somewhere less informative.
24
- */
25
- login(jar: ScenarioCookieJar, persona: ResolvedPersona): Promise<void>;
26
- /** Headers every request after `login` must carry. */
27
- headers(): Record<string, string>;
28
- }
29
- /**
30
- * Yields the credential for one persona, for a caller that holds that persona's
31
- * derived secret and not the root it came from.
32
- */
33
- export type ActorSecretResolver = (persona: ResolvedPersona) => string | Promise<string>;
34
- /**
35
- * Sign a persona in through the Better Auth actor plugin — the local-development
36
- * path.
37
- *
38
- * `POST /auth/sign-in/actor` upserts an `actor: true` row and mints a session
39
- * for it. Passwordless by design and refused for any row not carrying that flag,
40
- * so the secret can never reach a real user's account; the plugin still declines
41
- * to serve the endpoint at all outside `pikku dev`.
42
- *
43
- * What is presented is the persona's own credential, derived from the root and
44
- * bound to their address. A run driving many personas holds the root and
45
- * derives as it goes; a run entitled to one persona is handed that one value
46
- * through a resolver and can sign in as nobody else.
47
- */
48
- export declare class ActorSignIn implements PersonaSignIn {
49
- private readonly apiUrl;
50
- private readonly secret;
51
- private readonly signInPath;
52
- constructor(apiUrl: string, secret: string | ActorSecretResolver, signInPath: string);
53
- login(jar: ScenarioCookieJar, persona: ResolvedPersona): Promise<void>;
54
- headers(): Record<string, string>;
55
- }
56
- /**
57
- * The auth mount a configured sign-in path sits under, or `undefined` when it
58
- * names nothing recognisable.
59
- *
60
- * better-auth serves sign-in, operator sign-in and `get-session` from one
61
- * prefix, so an app that mounts it at `/api/auth` moves all three together and
62
- * says so once through `signInPath`. Reading the other two from a hardcoded
63
- * `/auth` on such an app 404s — and for `get-session` a 404 reads as "this
64
- * stage does not report roles", which silently turns off the check that tells a
65
- * permissions finding from seed drift.
66
- */
67
- export declare const authMount: (signInPath?: string) => string | undefined;
68
- export interface OperatorSignInOptions {
69
- /**
70
- * The short-lived RS256 operator token, or a function that mints one. Prefer
71
- * the function: tokens expire, and a long run re-logs-in after a 401.
72
- */
73
- token: string | (() => string | Promise<string>);
74
- /** Fabric operator sign-in path under apiUrl. Default `/auth/sign-in/fabric`. */
75
- signInPath?: string;
76
- }
77
- /** What an operator handshake yields: the session, and who to act as. */
78
- export interface OperatorSessionResult {
79
- /** `Set-Cookie` values the operator sign-in returned. */
80
- setCookies: string[];
81
- /** The target's own id for the persona, for the impersonation header. */
82
- userId: string;
83
- }
84
- /**
85
- * Establish a Fabric operator session against `apiUrl` and resolve the target's
86
- * own id for `persona`, which is what the impersonation header names.
87
- *
88
- * Takes the fetch to use rather than making one, because the two callers need
89
- * the cookies to land in different places: an HTTP persona keeps them in its
90
- * jar, a browser run plants them on a Playwright context. Both need the same
91
- * handshake, and it is the kind of sequence that quietly diverges once it is
92
- * written twice.
93
- */
94
- export declare const establishOperatorSession: (fetchImpl: typeof fetch, apiUrl: string, persona: ResolvedPersona, options: OperatorSignInOptions, extraHeaders?: Record<string, string>) => Promise<OperatorSessionResult>;
95
- /**
96
- * Sign a persona in on a DEPLOYED stage, by having a Fabric operator act as
97
- * them — the path that needs no test credential to exist anywhere.
98
- *
99
- * `POST /auth/sign-in/fabric` verifies an RS256 token against the stage's
100
- * `FABRIC_AUTH_PUBLIC_KEY` and mints a session for a synthetic operator row
101
- * granted the umbrella `admin` scope. Impersonation is then a header on each
102
- * request rather than a second session, and its gate is that scope — not
103
- * `user.role`, which is why this works without touching the app's roles.
104
- *
105
- * Asymmetric throughout: the stage can verify an operator token and never mint
106
- * one, so nothing in a deployed environment is worth stealing. That is the
107
- * property the actor secret cannot have, and the reason these are two classes
108
- * instead of one with a flag.
109
- */
110
- export declare class OperatorSignIn implements PersonaSignIn {
111
- private readonly apiUrl;
112
- private readonly options;
113
- private userId;
114
- constructor(apiUrl: string, options: OperatorSignInOptions);
115
- login(jar: ScenarioCookieJar, persona: ResolvedPersona): Promise<void>;
116
- headers(): Record<string, string>;
117
- }