@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,18 @@
1
+ ---
2
+ type: decision
3
+ title: Workflow helper functions are registered sessionless by declaration
4
+ description: Queue-worker helpers and the workflow sleeper get meta with sessionless true, so the runner requires a session for them only where their own auth flag says so
5
+ tags: core, workflow, queue, sessions
6
+ ---
7
+
8
+ # Workflow helper functions are registered sessionless by declaration
9
+
10
+ `helperFunctionMeta` in `packages/core/src/wirings/workflow/workflow-queue-wiring.ts` returns the function meta for internal workflow helpers. It sets `sessionless: true` and `functionType: 'helper'`, and sets no `expose` flag (`workflow-queue-wiring.ts:19-25`).
11
+
12
+ It is applied to each queue worker function that has no meta yet (`workflow-queue-wiring.ts:66-68`), and to the built-in `pikkuWorkflowSleeper` (`workflow-queue-wiring.ts:122-129`).
13
+
14
+ With `sessionless` set, the runner requires a session only when the wiring, the function config, or the addon says `auth: true` (`packages/core/src/function/function-runner.ts:385-395`). The helpers therefore run without a session unless one of those says otherwise.
15
+
16
+ The helpers are internal. Their safety rests on the absence of `expose`, and on the scenario and workflow rules that keep such functions off the network surface ([scenario-step functions are never externally invocable](scenario-step-functions-are-never-externally-invocable.md)).
17
+
18
+ **What this rules out:** declaring a function that acts on a user's behalf as a workflow helper to skip its session requirement, and exposing a helper over RPC by adding `expose` to its meta.
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  type: decision
3
- title: Scenario sessions are isolated per actor and reset between scenarios
4
- description: One jar per actor keeps two personas from sharing a session; a browser reset keeps one scenario from leaving the next signed in as somebody else
3
+ title: Scenario sessions are isolated per virtual user and reset between scenarios
4
+ description: One jar per virtual user keeps two virtual users from sharing a session; a browser reset keeps one scenario from leaving the next signed in as somebody else
5
5
  tags: workflow
6
6
  ---
7
7
 
8
- # Scenario sessions are isolated per actor and reset between scenarios
8
+ # Scenario sessions are isolated per virtual user and reset between scenarios
9
9
 
10
- `createCookieJar` in `scenario-cookie-jar.ts` holds its `Map` as a closure
10
+ `createCookieJar` in `services/scenario-cookie-jar.ts` holds its `Map` as a closure
11
11
  local, so two jars can never share a session. That is precisely what lets one
12
12
  scenario sign in as several people without one of them inheriting another's
13
13
  session. Every response is read, not just the sign-in, so a cookie the target
@@ -22,11 +22,11 @@ without establishing a session. Anything needing to know whether a sign-in
22
22
  happened has to track the sign-in itself.
23
23
 
24
24
  The browser side has the same requirement. `ScenarioBrowserProvider.reset`
25
- discards every actor's per-scenario state — cookies, storage, open pages — while
25
+ discards every virtual user's per-scenario state — cookies, storage, open pages — while
26
26
  keeping the browser, and is called between scenarios so one scenario cannot
27
27
  leave the next signed in as somebody else.
28
28
 
29
29
  **What this rules out:** hoisting the jar to a module-level or per-service map,
30
- sharing one jar across actors, reading `Set-Cookie` only from the sign-in
30
+ sharing one jar across virtual users, reading `Set-Cookie` only from the sign-in
31
31
  response, treating `empty === false` as "signed in", or skipping `reset` between
32
32
  scenarios as an optimisation.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  type: decision
3
3
  title: A scenario step is never registered as a callable RPC and never dispatched on the queue
4
- description: A step drives a browser and holds an actor's session, so exposing it as an RPC would put that reach on the network
4
+ description: A step drives a browser and holds a virtual user's session, so exposing it as an RPC would put that reach on the network
5
5
  tags: workflow
6
6
  ---
7
7
 
@@ -11,21 +11,21 @@ tags: workflow
11
11
  `RpcStepMeta` on purpose. A scenario step runs locally through `runPikkuFunc`
12
12
  and must never be treated as dispatchable on the queue or replay path, nor
13
13
  registered as a callable RPC — a browser-driving step must not be
14
- network-invocable. It executes with a browser handle and an actor's
14
+ network-invocable. It executes with a browser handle and a virtual user's
15
15
  authenticated session in hand; exposed as an RPC it would hand a remote caller
16
16
  that reach.
17
17
 
18
18
  `PikkuScenarioService.scenarioStep` reflects this at runtime: it always runs
19
19
  through `engine.inlineStep` with `runPikkuFunc`, never through `rpcStep`, so no
20
20
  transport is involved. A step declaring `browser: true` is refused outright
21
- unless both a browser provider is registered and an actor was supplied, with an
21
+ unless both a browser provider is registered and a virtual user was supplied, with an
22
22
  error saying which is missing.
23
23
 
24
24
  Scenario steps are also modelled as sessionless pikku functions (see
25
- `scenario-step.test.ts`) because a step is driven by an actor, not by a wire
25
+ `scenario-step.test.ts`) because a step is driven by a virtual user, not by a wire
26
26
  session.
27
27
 
28
28
  **What this rules out:** collapsing `ScenarioStepMeta` into `RpcStepMeta`,
29
29
  registering step functions in the RPC map so they "can be tested directly",
30
30
  routing a scenario step through `rpcStep`/`dispatchStep`, or defaulting a
31
- `browser: true` step to run without an actor.
31
+ `browser: true` step to run without a virtual user.
@@ -1,34 +1,34 @@
1
1
  ---
2
2
  type: decision
3
- title: A scenario actor step always goes over the real transport and never through internal dispatch
3
+ title: A scenario virtual user step always goes over the real transport and never through internal dispatch
4
4
  description: Internal dispatch would bypass auth middleware and permissions, turning a scenario into a green health check that proves nothing
5
5
  tags: workflow
6
6
  ---
7
7
 
8
- # A scenario actor step always goes over the real transport and never through internal dispatch
8
+ # A scenario virtual user step always goes over the real transport and never through internal dispatch
9
9
 
10
- When a workflow step carries `options.actor`, `rpcStep` in
11
- `pikku-workflow-service.ts` calls `actor.invoke(rpcName, data)` — the actor's
10
+ When a workflow step carries `options.virtualUser`, `rpcStep` in
11
+ `pikku-workflow-service.ts` calls `virtualUser.invoke(rpcName, data)` — the virtual user's
12
12
  authenticated client, over the real network transport — and never falls back to
13
13
  internal dispatch. Internal dispatch skips auth middleware and permission
14
14
  checks, so a scenario that "passed" that way would prove only that the function
15
- body runs, not that the persona is allowed to run it. That is a green health
15
+ body runs, not that the virtual user is allowed to run it. That is a green health
16
16
  check for a system that may be wide open.
17
17
 
18
- For the same reason, actor steps never queue: `rpcStep` short-circuits
19
- `dispatchStep` to `false` whenever `options.actor` is set. An actor step is an
20
- outbound HTTP call made by the runner itself, and the actor's session lives in
18
+ For the same reason, virtual user steps never queue: `rpcStep` short-circuits
19
+ `dispatchStep` to `false` whenever `options.virtualUser` is set. A virtual user step is an
20
+ outbound HTTP call made by the runner itself, and the virtual user's session lives in
21
21
  this process — a queued worker in another process would have no session to make
22
22
  it with. The step is still recorded durably like any RPC step, so replay and
23
23
  reporting are unaffected.
24
24
 
25
- Actor identity is process-local by nature. `PikkuScenarioService.runActors` maps
25
+ Virtual user identity is process-local by nature. `PikkuScenarioService.runVirtualUsers` maps
26
26
  run id to live authenticated clients (cookie jars), and those ride that map
27
27
  only, never the persisted run wire — a serialized session on a durable run row
28
28
  would be a credential at rest.
29
29
 
30
30
  **What this rules out:** adding an "internal dispatch is faster" fast path for
31
- actor steps, routing an actor step through the queue, serializing actor sessions
32
- onto the run wire, or letting `actor.invoke` degrade to `rpcWithWire` when the
31
+ virtual user steps, routing a virtual user step through the queue, serializing virtual user sessions
32
+ onto the run wire, or letting `virtualUser.invoke` degrade to `rpcWithWire` when the
33
33
  transport is unavailable. Each turns an end-to-end authorization test into one
34
34
  that cannot fail.
@@ -0,0 +1,18 @@
1
+ ---
2
+ type: decision
3
+ title: Writing a virtual user schedule is gated by its own scope, separate from running one
4
+ description: setVirtualUserSchedule carries virtualUser:schedule, because a schedule write spends money on every future tick while a run spends it once with a caller watching
5
+ tags: core, cli, virtual-users, scopes, security
6
+ ---
7
+
8
+ # Writing a virtual user schedule is gated by its own scope, separate from running one
9
+
10
+ `packages/core/src/wirings/virtual-user/virtual-user-schedule-store.ts` defines the store that keeps a virtual user's cadence. Its SECURITY note says a written row "spends money on every future tick, without a caller present to see it happen", and that the scaffold therefore "gates writes behind a scope of their own" (`virtual-user-schedule-store.ts:60-63`). Reading findings is "a much smaller permission than deciding they should keep going" (`virtual-user-schedule-store.ts:62-63`).
11
+
12
+ The gate is generated by the CLI. `packages/cli/src/functions/wirings/virtual-user/serialize-virtual-user-functions.ts` emits `setVirtualUserSchedule` with `scopes: ['virtualUser:schedule']` (line 456), with a comment saying a run "spends money once with a caller present to see it, while writing a schedule spends it repeatedly with nobody there". `listVirtualUserSchedules` needs only `virtualUser:read` (line 477). The tests assert the scope in `virtual-user-functions.test.ts:66`.
13
+
14
+ A scheduled tick has no caller. It runs as the reserved `pikku-platform` principal, whose session carries only `virtualUser:run` (`packages/cli/src/functions/wirings/virtual-user/serialize-virtual-user-functions.ts:510-519`), so a tick can start a run and cannot write a schedule.
15
+
16
+ The store's own design is related. `claim` is a compare-and-set on `nextRunAt`, so two processes on the same cron cannot both dispatch the same virtual user (`virtual-user-schedule-store.ts:78-91`). Its doc says a second dispatch would mean "the same user acting twice over, at twice the budget" (`virtual-user-schedule-store.ts:82-86`).
17
+
18
+ **What this rules out:** letting a caller who can read runs also create or change a schedule, and treating a schedule write as the same permission as a run.
@@ -19,8 +19,6 @@ meta files, the export surface. A note that repeats them is a copy that will
19
19
  drift.
20
20
 
21
21
  <!-- pikku:knowledge-index -->
22
-
23
22
  - [decisions](decisions/index.md) — a rule that was chosen, and what it rules out
24
23
  - [questions](questions/index.md) — something asked and not yet answered
25
-
26
24
  <!-- /pikku:knowledge-index -->
@@ -0,0 +1,24 @@
1
+ ---
2
+ type: question
3
+ title: Does a bearer JWT with no identity claim count as a session?
4
+ description: The cookie middleware refuses an identity-less JWT, but authBearer sets whatever decode returns, so the two paths disagree
5
+ tags: core, auth, middleware
6
+ ---
7
+
8
+ # Does a bearer JWT with no identity claim count as a session?
9
+
10
+ `authCookie` (`packages/core/src/middleware/auth-cookie.ts`) only sets a session
11
+ when `hasSessionIdentity` is true, meaning the decoded payload has a key that is
12
+ not a registered JWT claim. Its test comment says a JWT of an absent session
13
+ still decodes to a truthy `{ iat, exp }` that "the runner accepts", and it guards
14
+ against that.
15
+
16
+ `authBearer` (`packages/core/src/middleware/auth-bearer.ts`) has no such check.
17
+ In JWT mode it calls `jwtService.decode(bearerToken)` and calls `setSession` on
18
+ any truthy result. Its test only covers a `null` decode.
19
+
20
+ What is asked: should a bearer token that decodes to `{ iat, exp }` alone be
21
+ treated as anonymous, as the cookie path does, or as an authenticated session?
22
+ Current behaviour: it becomes the request session. Whether a signed token of that
23
+ shape can be minted is not checked in core, because the `JWTService`
24
+ implementation is not in this package.
@@ -0,0 +1,25 @@
1
+ ---
2
+ type: question
3
+ title: Can dev quick login become impersonation?
4
+ description: Dev quick login signs in a fixed admin account; making it an impersonation step needs an existing admin session it does not have, so it would need a new bypass
5
+ tags: auth, services
6
+ ---
7
+
8
+ # Can dev quick login become impersonation?
9
+
10
+ Today `/dev/quick-login` (`packages/services/better-auth/src/dev-quick-login.ts`)
11
+ provisions a fixed `admin@pikku.dev` account with a fixed password and grants it
12
+ the admin and console roots. It is refused outside loopback, behind a proxy, and
13
+ in production.
14
+
15
+ The proposal is that quick login should just be impersonation. Impersonation
16
+ (`resolveImpersonatedSession`) only applies on top of an existing session, and
17
+ the caller must hold `admin:impersonate`. Quick login has no session before it
18
+ runs, so making it impersonation needs one of:
19
+
20
+ - an unauthenticated dev-only route that accepts a user id, which is a new
21
+ bypass of the check that impersonation exists to keep, or
22
+ - quick login keeps minting the dev admin session, and virtual users are
23
+ impersonated from there.
24
+
25
+ The second keeps one mechanism for virtual users. Undecided.
@@ -0,0 +1,26 @@
1
+ ---
2
+ type: question
3
+ title: Does a delayed RPC run as the session it was scheduled with?
4
+ description: The in-memory scheduler stores the session it is given and returns it from getTask, but the delayed run invokes the RPC with no session
5
+ tags: core, scheduler, sessions
6
+ ---
7
+
8
+ # Does a delayed RPC run as the session it was scheduled with?
9
+
10
+ The contract takes an optional session. `SchedulerService.scheduleRPC` in
11
+ `packages/core/src/services/scheduler-service.ts:19-24` accepts `session`, and
12
+ `ScheduledTaskInfo` exposes it (`:9-13`).
13
+
14
+ The in-memory implementation in `packages/schedule/src/in-memory-scheduler-service.ts`
15
+ accepts it (`:41-46`), stores it on the delayed task (`:85-92`), and returns it from
16
+ `getTask` (`:107-118`). The timer callback runs `rpc.invoke(rpcName, data)` on a
17
+ context built with an empty wire (`:69-75`). The session is not passed to that
18
+ call, and the file does not read it on the run path.
19
+
20
+ The comment at `:67-68` says the BullMQ and pg-boss schedulers enqueue
21
+ `{ rpcName, data, session }` and invoke the RPC with it. This file does not do so.
22
+ The caller in the workflow sleeper passes no session (`packages/core/src/wirings/workflow/pikku-workflow-service.ts:852-856`).
23
+
24
+ **Question:** should a delayed RPC run under the session captured at scheduling
25
+ time, as the comment describes for the other schedulers, or run with none? The
26
+ code stores the session, reports it, and does not use it.
@@ -0,0 +1,27 @@
1
+ ---
2
+ type: question
3
+ title: Can a user id containing a colon read another user's credentials?
4
+ description: LocalCredentialService joins user id and name with a colon and matches users by prefix, so an id containing a colon may collide with another user's keys
5
+ tags: core, credentials, local-dev, isolation
6
+ ---
7
+
8
+ # Can a user id containing a colon read another user's credentials?
9
+
10
+ `packages/core/src/services/local-credential-service.ts` stores each credential
11
+ under the key `userId:name`, or just `name` when there is no user
12
+ (`local-credential-service.ts:6-8`).
13
+
14
+ `getAll(userId)` returns every key that starts with `${userId}:`, and strips that
15
+ prefix to get the credential name (`local-credential-service.ts:31-39`). If one
16
+ user's id contains a colon, its keys also start with the prefix of the id before
17
+ the colon. For example, user `a` would list the key of user `a:b`, credential `c`
18
+ (stored as `a:b:c`), under the name `b:c`.
19
+
20
+ `getUsersWithCredential` (`local-credential-service.ts:42-51`) and `getAllUsers`
21
+ (`local-credential-service.ts:53-62`) both split on the first colon, so they would
22
+ report user `a:b` as `a`.
23
+
24
+ Nothing in this file validates a user id. What is asked: can a user id contain a
25
+ colon in practice? If it can, is a user with id `a` meant to see keys belonging
26
+ to `a:b`? The code currently allows it, and no comment or test in this file says
27
+ which is intended.
@@ -0,0 +1,26 @@
1
+ ---
2
+ type: question
3
+ title: Does a wireCLI command need a session when it sets no auth?
4
+ description: The channel default is documented as requiring a session, but a CLI command has no default of its own; its session need comes from whether the function is sessionless
5
+ tags: core, cli, auth
6
+ ---
7
+
8
+ # Does a wireCLI command need a session when it sets no auth?
9
+
10
+ For channels, an absent `auth` requires a session. `packages/core/src/wirings/channel/channel.types.ts:67-76`
11
+ and `packages/core/src/wirings/channel/channel-runner.ts:142` both say so.
12
+
13
+ `wireCLI` in `packages/core/src/wirings/cli/cli-runner.ts` copies `auth` and
14
+ `permissions` from the function (`:89-90`, `:105`). It merges a command-level
15
+ `auth` onto the registration as `commandAuth ?? existing.auth` (`:176`, `:190`,
16
+ `:201`). No default is set anywhere in that path.
17
+
18
+ When the command runs, `runPikkuFunc` is called with `auth: false` (`:393-397`).
19
+ In `packages/core/src/function/function-runner.ts:385-404`, a sessionless function
20
+ requires a session only when the wiring or the function sets `auth: true` (`:386-392`).
21
+ A function that is not sessionless always requires one and throws when there is none
22
+ (`:401-403`). `auth: false` on such a function only logs a warning (`:396-400`).
23
+
24
+ **Question:** should a CLI command with no `auth` require a session as a channel
25
+ does? The code does not set that default for CLI commands. Whether a command needs
26
+ one depends on whether the function is declared sessionless.
@@ -0,0 +1,26 @@
1
+ ---
2
+ type: question
3
+ title: Does an adversarial user's in-scope success count as a finding?
4
+ description: The disposition doc says a 2xx is a finding for an inverted oracle, but the engine only flags successes outside the virtual user's scopes, and in-scope 2xx depends on the app's classifier
5
+ tags: core, virtual-user, authorization
6
+ ---
7
+
8
+ # Does an adversarial user's in-scope success count as a finding?
9
+
10
+ `dispositionProfile` in `packages/core/src/wirings/virtual-user/virtual-user-dispositions.ts`
11
+ documents `invertedOracle` as meaning "a 2xx is a finding rather than a success".
12
+ The comment on the field says the engine "flags the success and leaves ownership
13
+ to the app's classifier".
14
+
15
+ In `run-virtual-user.ts` the flag does one thing: at line 302 it drops the
16
+ scope narrowing, so an adversarial user is offered the whole catalogue. The
17
+ finding that fires on a 2xx is the `unexpected-success` check around lines
18
+ 627-645. It requires `response.ok` and a scope the virtual user does not hold. A
19
+ 2xx on an endpoint the virtual user is entitled to produces no engine finding. Only the
20
+ app's `classify` hook can flag it, and that is the ownership check the comment
21
+ describes.
22
+
23
+ So the flag's name and doc describe a stronger oracle than the engine applies.
24
+ What is asked: should `invertedOracle` make every 2xx a finding in the engine, as
25
+ its doc says, or is the classifier meant to be the only place a 2xx becomes a
26
+ finding, with the doc corrected?
@@ -0,0 +1,34 @@
1
+ ---
2
+ type: question
3
+ title: Does approveStep need the run owner's session, as its doc says, or only what the gate's approvers policy allows?
4
+ description: The WorkflowService doc says the session must belong to the run's owner, but the implementation judges the session against the gate's published policy, whose default is any
5
+ tags: workflow, approvals, sessions, comment-vs-code
6
+ ---
7
+
8
+ # Does approveStep need the run owner's session, as its doc says, or only what the gate's approvers policy allows?
9
+
10
+ **The comment.** The doc on `approveStep` in `packages/core/src/services/workflow-service.ts` says: "The session
11
+ must belong to the run's owner — see `assertWorkflowRunOwner`" (line 52). The method takes the session as
12
+ optional (`session?: CoreUserSession`, lines 54-58).
13
+
14
+ **The code.** `PikkuWorkflowService.approveStep` in `packages/core/src/wirings/workflow/pikku-workflow-service.ts`
15
+ (lines 1804-1817) does not call `assertWorkflowRunOwner`. It passes `approvalDeciderFrom(session)` to
16
+ `recordApprovalDecision`. That helper reduces the session to `{ userId, scopes }`, or `undefined` when there is
17
+ no session (`packages/core/src/wirings/workflow/workflow-approval-policy.ts:21-24`).
18
+
19
+ `recordApprovalDecision` (`packages/core/src/wirings/workflow/workflow-approval.ts:98-104`) judges the decider only
20
+ when the gate has already published a policy (`workflow-approval.ts:125-141`). The policy check is
21
+ `approvalPolicyRefusal`, which defaults `approvers` to `'any'` (`workflow-approval-policy.ts:46`, `:65-66`).
22
+ Only `'owner'` compares the decider with the run's owner (`:47-54`), and it lets everyone through when the run
23
+ has no recorded owner (`:48-50`). `'not-initiator'` refuses the owner (`:56-63`). A decision recorded before the
24
+ gate publishes its policy is not judged at that point (`workflow-approval.ts:125`).
25
+
26
+ The decision note
27
+ [a workflow run is read by its owner, and answered by whoever its gate declares](../decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md)
28
+ records the design as the gate's `approvers`. The comment at `workflow-service.ts:52` still describes the earlier
29
+ rule that note says it replaced. The reference at `packages/core/src/wirings/workflow/workflow-run-ownership.ts:19`
30
+ already points to `approvers`.
31
+
32
+ **Question:** should the comment at `workflow-service.ts:52` be rewritten to describe the gate's policy, or is any
33
+ caller meant to be held to the owner's session regardless of the policy? As the code stands, a gate with the
34
+ default policy accepts an answer from no session at all.
@@ -0,0 +1,20 @@
1
+ ---
2
+ type: question
3
+ title: Does cors origin: true reflect any origin when credentials are on?
4
+ description: The credentials guard only refuses the wildcard origin, so origin: true with credentials reflects whichever origin asks
5
+ tags: core, middleware, cors
6
+ ---
7
+
8
+ # Does cors origin: true reflect any origin when credentials are on?
9
+
10
+ In `packages/core/src/middleware/cors.ts:55-56`, `origin: true` sets the allowed
11
+ origin to the request's `Origin` header, or to `*` when none is sent.
12
+
13
+ The guard at `:36-40` throws only when the origin is the string `'*'` and
14
+ `credentials` is true. `origin: true` is not that string, so the combination is
15
+ accepted with no throw. The file does not check that the reflected origin is in a
16
+ list.
17
+
18
+ **Question:** is `origin: true` with `credentials: true` meant to reflect any caller
19
+ origin, or should it be refused like the wildcard? The guard and its message cover
20
+ only the wildcard. The `origin: true` option has no note in this folder.
@@ -0,0 +1,24 @@
1
+ ---
2
+ type: question
3
+ title: Does skipUserSession do anything on a channel?
4
+ description: The local channel runner accepts and forwards skipUserSession, but the channel open path never reads it, unlike the HTTP runner which refuses it on a session-requiring route
5
+ tags: core, channel, http, sessions
6
+ ---
7
+
8
+ # Does skipUserSession do anything on a channel?
9
+
10
+ `runLocalChannel` in `packages/core/src/wirings/channel/local/local-channel-runner.ts`
11
+ takes `skipUserSession = false` (`:66`) and passes it to `openChannel` (`:104`).
12
+
13
+ `openChannel` in `packages/core/src/wirings/channel/channel-runner.ts:121-130` has
14
+ `RunChannelOptions` in its parameter type, which includes `skipUserSession`
15
+ (`channel.types.ts:31`). The function destructures only `route`,
16
+ `coerceDataFromSchema` and `request`. A search of `packages/core/src` finds no
17
+ read of the option in the channel code.
18
+
19
+ The HTTP runner handles the same option differently. `packages/core/src/wirings/http/http-runner.ts:211-213`
20
+ throws when `skipUserSession` is set on a route that requires a session.
21
+
22
+ **Question:** on a channel that requires a session, is `skipUserSession: true`
23
+ meant to be refused like the HTTP case, or is it intentionally ignored? The code
24
+ accepts it and does nothing with it.
@@ -0,0 +1,19 @@
1
+ ---
2
+ type: question
3
+ title: Should betterAuthStoreSession warn about unhonoured impersonation?
4
+ description: The cookie and stateless middlewares warn once when the impersonation header is ignored; the store middleware does not, and nothing says whether that is an omission
5
+ tags: better-auth, impersonation, logging
6
+ ---
7
+
8
+ # Should betterAuthStoreSession warn about unhonoured impersonation?
9
+
10
+ `betterAuthSession` and `betterAuthStatelessSession` both call
11
+ `warnImpersonationUnconfigured` when a request names a target and no
12
+ `impersonation` option is set. `betterAuthStoreSession` does not.
13
+
14
+ Without the warning, a store-backed app that forgets the option runs every
15
+ scenario virtual user as the signed-in caller and fails only on assertions. The code gives
16
+ no reason for the difference.
17
+
18
+ Open: is the store middleware's silence an omission, or was it left out because
19
+ it is used only for header-token traffic where impersonation is never wanted?
@@ -11,8 +11,31 @@ currently makes a choice that nobody has defended — the choice stays, the doub
11
11
  gets recorded.
12
12
 
13
13
  <!-- pikku:knowledge-index -->
14
-
14
+ - [Does a bearer JWT with no identity claim count as a session?](a-bearer-jwt-with-no-identity-becomes-a-session.md) — The cookie middleware refuses an identity-less JWT, but authBearer sets whatever decode returns, so the two paths disagree
15
15
  - [A channel's middleware list accepts bare factories that nothing ever resolves](channel-middleware-accepts-bare-factories-that-nothing-resolves.md) — CoreChannel.channelMiddleware admits CorePikkuChannelMiddlewareFactory, but no runner calls one, so a bare factory would stall the chain
16
+ - [Can dev quick login become impersonation?](dev-quick-login-as-impersonation.md) — Dev quick login signs in a fixed admin account; making it an impersonation step needs an existing admin session it does not have, so it would need a new bypass
17
+ - [Does a delayed RPC run as the session it was scheduled with?](does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md) — The in-memory scheduler stores the session it is given and returns it from getTask, but the delayed run invokes the RPC with no session
18
+ - [Can a user id containing a colon read another user's credentials?](does-a-user-id-with-a-colon-read-another-users-credentials.md) — LocalCredentialService joins user id and name with a colon and matches users by prefix, so an id containing a colon may collide with another user's keys
19
+ - [Does a wireCLI command need a session when it sets no auth?](does-a-wirecli-command-need-a-session-by-default.md) — The channel default is documented as requiring a session, but a CLI command has no default of its own; its session need comes from whether the function is sessionless
20
+ - [Does an adversarial user's in-scope success count as a finding?](does-an-adversarial-in-scope-success-count-as-a-finding.md) — The disposition doc says a 2xx is a finding for an inverted oracle, but the engine only flags successes outside the virtual user's scopes, and in-scope 2xx depends on the app's classifier
21
+ - [Does approveStep need the run owner's session, as its doc says, or only what the gate's approvers policy allows?](does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md) — The WorkflowService doc says the session must belong to the run's owner, but the implementation judges the session against the gate's published policy, whose default is any
22
+ - [Does cors origin: true reflect any origin when credentials are on?](does-cors-origin-true-reflect-any-origin-with-credentials.md) — The credentials guard only refuses the wildcard origin, so origin: true with credentials reflects whichever origin asks
23
+ - [Does skipUserSession do anything on a channel?](does-skip-user-session-do-anything-on-a-channel.md) — The local channel runner accepts and forwards skipUserSession, but the channel open path never reads it, unlike the HTTP runner which refuses it on a session-requiring route
24
+ - [Should betterAuthStoreSession warn about unhonoured impersonation?](does-the-store-session-match-the-other-two.md) — The cookie and stateless middlewares warn once when the impersonation header is ignored; the store middleware does not, and nothing says whether that is an omission
25
+ - [Is the agent score worker reachable without a session?](is-the-agent-score-worker-reachable-without-a-session.md) — The score worker is registered as sessionless with the helper function type, and this file wires no route or expose for it
26
+ - [May a scheduled test on live change data?](live-scheduled-tests-may-write.md) — Scheduled runs against a live app may only read, or may write with an explicit per-schedule approval; the choice is not made
27
+ - [Who calls provisionVirtualUsers?](provision-virtual-users-has-no-caller-in-this-repo.md) — provisionVirtualUsers is additive and can ban orphans, but no caller exists in this repo outside tests and skill docs, and the route its doc comment names no longer exists
28
+ - [Does a non-remote request need a remote token once the secret is set?](remote-secret-refuses-requests-without-a-remote-token-on-every-path.md) — With PIKKU_REMOTE_SECRET set, pikkuRemoteAuthMiddleware refuses any request lacking a Bearer header, not only /remote/rpc paths; the no-secret branch is path-scoped
29
+ - [Should a session set by middleware be persisted and re-issued like a function's?](should-a-middleware-set-session-persist-to-the-store.md) — Middleware setSession calls setInitial, which marks no change and writes no store entry, while a function's setSession does both; the reason is not recorded
30
+ - [Should a scoped secret view throw from hasSecret for a disallowed key?](should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md) — The SecretService contract says hasSecret answers for any key and must not throw, but the scoped view throws for keys outside its set
31
+ - [Should a Bearer token that does not verify be anonymous or refused?](should-a-wrong-bearer-token-be-anonymous-or-refused.md) — authBearer refuses a malformed scheme with InvalidSessionError but lets a well-formed wrong token through as anonymous, and no comment says why
32
+ - [Should an outgoing webhook go out unsigned when its configured signing secret resolves empty?](should-an-empty-webhook-signing-secret-send-unsigned.md) — When config.webhook.secret names a secret that resolves to nothing, the delivery is logged as unsigned and enqueued without a signature
33
+ - [Should HTTP virtual user sessions reset between scenarios in one CLI run?](should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md) — The CLI builds HTTP virtual users once and reuses them for every scenario, and only the browser side is reset between scenarios
34
+ - [Should the scenario cookie jar scope cookies to the API origin?](should-the-cookie-jar-scope-cookies-to-the-api-origin.md) — 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
35
+ - [Should voiceInput fetch audio URLs when no host allowlist is configured?](should-voice-input-fetch-audio-urls-without-a-host-allowlist.md) — With no allowedAudioHosts, the audio URL is fetched through safeFetch with no allowlist, so any public host is reachable
36
+ - [What rules govern impersonation on staging and live?](staging-impersonation-rules.md) — Staged impersonation by a signed-in admin is wanted; the switch, read-only default, expiry and logging rules are proposed but not agreed
16
37
  - [An unauthorized channel reply is sent outside the channel's declared Out type](unauthorized-channel-replies-escape-the-declared-out-type.md) — processMessageHandlers sends a bare string on auth failure, which no generated client type admits — nobody has decided what the typed shape should be
17
-
38
+ - [Pikku Studio Cloud has to create, hold and deliver the stage virtual user keys](virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md) — 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
39
+ - [A virtual user statement can be replayed once per instance](virtual-user-statement-replay-is-per-process.md) — The one-time id store is an in-memory map, so on several instances or Worker isolates each accepts a statement once
40
+ - [What should an impersonated session carry?](what-an-impersonated-session-carries.md) — 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
18
41
  <!-- /pikku:knowledge-index -->
@@ -0,0 +1,21 @@
1
+ ---
2
+ type: question
3
+ title: Is the agent score worker reachable without a session?
4
+ description: The score worker is registered as sessionless with the helper function type, and this file wires no route or expose for it
5
+ tags: core, agent, scorer, security
6
+ ---
7
+
8
+ # Is the agent score worker reachable without a session?
9
+
10
+ `packages/core/src/wirings/agent-scorer/agent-scorer-worker.ts:44-56` registers the
11
+ worker `pikkuAgentScoreWorker` with `sessionless: true` and `functionType: 'helper'`.
12
+ The file adds no HTTP or RPC wiring and no `expose` for it.
13
+
14
+ Core's meta type lists `'helper'` as one of the function types (`packages/core/src/function/function-meta.types.ts:147`).
15
+ The scenario coverage code skips non-user functions (`packages/core/src/wirings/workflow/scenario-coverage.ts:195`).
16
+ I did not trace whether a helper is routed over RPC or HTTP, or what gates a
17
+ sessionless function on those routes.
18
+
19
+ **Question:** should the score worker be reachable from outside the process without
20
+ a session, and is `helper` what keeps it off the public surface? That is not
21
+ settled by this file.
@@ -0,0 +1,19 @@
1
+ ---
2
+ type: question
3
+ title: May a scheduled test on live change data?
4
+ description: Scheduled runs against a live app may only read, or may write with an explicit per-schedule approval; the choice is not made
5
+ tags: workflow, services
6
+ ---
7
+
8
+ # May a scheduled test on live change data?
9
+
10
+ A scheduled test runs in a live app as a test account that has the same access as
11
+ a real role, so it can change real data. Options proposed, none chosen:
12
+ - read-only by default, with writes allowed only by an explicit approval recorded
13
+ on each schedule;
14
+ - writes allowed on schedules the admin marks as writing, with a warning shown
15
+ before saving;
16
+ - no writes on live at all, with the same tests run only on staging.
17
+
18
+ The answer also decides what the failure notice must say when a write test stops
19
+ partway.
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: question
3
+ title: Who calls provisionVirtualUsers?
4
+ description: provisionVirtualUsers is additive and can ban orphans, but no caller exists in this repo outside tests and skill docs, and the route its doc comment names no longer exists
5
+ tags: better-auth, virtual-users, provisioning
6
+ ---
7
+
8
+ # Who calls provisionVirtualUsers?
9
+
10
+ `provisionVirtualUsers` (`packages/services/better-auth/src/provision-virtual-users.ts`)
11
+ creates virtual user accounts, grants their roles, and handles orphans. Orphans
12
+ are reported by default, or banned when `orphans: 'ban'` is set and the ban
13
+ plugin exists. It never deletes or revokes.
14
+
15
+ A search of this repo finds callers only in tests and skill documentation. Its
16
+ doc comment says it is meant to be called by an operator sign-in route on a
17
+ handshake that names an unknown address, but that operator sign-in was removed
18
+ when virtual user callers moved to signed statements, so the comment names a
19
+ caller that does not exist.
20
+
21
+ Open: which deployment path runs it now, and when? Until that is known, the
22
+ orphan policy and the refusal rules (a real, non-virtual-user account at a
23
+ virtual user's email throws) are untested in production.
@@ -0,0 +1,32 @@
1
+ ---
2
+ type: question
3
+ title: Does a non-remote request need a remote token once the secret is set?
4
+ description: With PIKKU_REMOTE_SECRET set, pikkuRemoteAuthMiddleware refuses any request lacking a Bearer header, not only /remote/rpc paths; the no-secret branch is path-scoped
5
+ tags: core, auth, remote, middleware
6
+ ---
7
+
8
+ # Does a non-remote request need a remote token once the secret is set?
9
+
10
+ `pikkuRemoteAuthMiddleware` (`packages/core/src/middleware/remote-auth.ts`) has
11
+ two branches. With no secret, only a path under `/remote/rpc/` is refused, and
12
+ the test "requires authorization for a case-varied remote path when no secret is
13
+ set" pins that. With the secret set, the code reaches
14
+ `if (!authHeader) throw new UnauthorizedError()` for every path, because that
15
+ check is not gated by `isRemoteRpcPath`.
16
+
17
+ So once the secret is configured, a request to any route the middleware sees and
18
+ that carries no Bearer header is refused. The tests in `remote-auth.test.ts`
19
+ only use `/remote/rpc` paths with the secret set, so they do not show whether
20
+ this is meant.
21
+
22
+ Mounting, checked: in this repo nothing in a template or example mounts
23
+ `pikkuRemoteAuthMiddleware`. The Pikku Studio Cloud app, which lives outside this
24
+ repo, mounts it as wire middleware on one route only, `POST /remote/rpc/:rpcName`,
25
+ not globally. So for that app the question above does not arise, because the
26
+ middleware never sees its ordinary routes. The question stands for any app that mounts the middleware globally, which
27
+ this repo does not show.
28
+
29
+ What is asked: should a deployment that sets `PIKKU_REMOTE_SECRET` refuse its
30
+ ordinary public routes as well, or should the secret branch be path-scoped the
31
+ way the no-secret branch is? The code does not say which the app expects, and
32
+ how the app mounts the middleware is outside this package.
@@ -0,0 +1,25 @@
1
+ ---
2
+ type: question
3
+ title: Should a session set by middleware be persisted and re-issued like a function's?
4
+ description: Middleware setSession calls setInitial, which marks no change and writes no store entry, while a function's setSession does both; the reason is not recorded
5
+ tags: core, auth, session
6
+ ---
7
+
8
+ # Should a session set by middleware be persisted and re-issued like a function's?
9
+
10
+ Two wire helpers in `packages/core/src/services/user-session-service.ts` set the
11
+ session differently. `createMiddlewareSessionWireProps` and the HTTP runner's
12
+ middleware wire (`packages/core/src/wirings/http/http-runner.ts`) bind
13
+ `setSession` to `setInitial`. That sets the session without marking
14
+ `sessionChanged` and without calling the session store. `createFunctionSessionWireProps`
15
+ binds `setSession` to `set`, which marks the change and writes the store.
16
+
17
+ The effect is that a session established by `authBearer`, `authAPIKey` or
18
+ `authCookie` is never written to the store and never triggers a cookie
19
+ re-issue in `authCookie`, which checks `hasSessionChanged`. A function that calls
20
+ `setSession` does both. The naming suggests the middleware path is meant to
21
+ describe the request's starting session, but no comment, test or note says so.
22
+
23
+ What is asked: is it intended that a middleware-authenticated session stays
24
+ request-scoped, or should middleware-set sessions reach the store like function-set
25
+ ones?
@@ -0,0 +1,23 @@
1
+ ---
2
+ type: question
3
+ title: Should a scoped secret view throw from hasSecret for a disallowed key?
4
+ description: The SecretService contract says hasSecret answers for any key and must not throw, but the scoped view throws for keys outside its set
5
+ tags: core, secrets, scoping
6
+ ---
7
+
8
+ # Should a scoped secret view throw from hasSecret for a disallowed key?
9
+
10
+ The `SecretService` interface in `packages/core/src/services/secret-service.ts:13-14`
11
+ says `hasSecret` "answers for any key, including a disallowed one — it must not
12
+ throw."
13
+
14
+ `ScopedSecretService.hasSecret` in `packages/core/src/services/scoped-secret-service.ts:21-24`
15
+ calls `assertAllowed` first, and that throws `Access denied to secret key` for a key
16
+ outside the view's set (`:10-14`).
17
+
18
+ The note `a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md` records
19
+ that the view's `hasSecret` throws, and treats that as intended.
20
+
21
+ **Question:** which governs? The view's throw follows its read rule. The contract
22
+ says an existence check must not throw for any key. The two cannot both hold for a
23
+ disallowed key.