@pikku/core 0.12.139 → 0.12.142
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.
- package/CHANGELOG.md +47 -0
- package/dist/errors/errors.d.ts +1 -1
- package/dist/errors/errors.js +1 -1
- package/dist/function/function-meta.types.d.ts +6 -6
- package/dist/function/functions.types.d.ts +2 -2
- package/dist/lifecycle.d.ts +6 -0
- package/dist/lifecycle.js +58 -0
- package/dist/services/audit-service.d.ts +4 -4
- package/dist/services/feature-flag-service.d.ts +3 -3
- package/dist/services/{http-personas.d.ts → http-virtual-users.d.ts} +25 -47
- package/dist/services/{http-personas.js → http-virtual-users.js} +29 -53
- package/dist/services/index.d.ts +2 -2
- package/dist/services/index.js +1 -1
- package/dist/services/meta-service.d.ts +6 -6
- package/dist/services/meta-service.js +8 -8
- package/dist/{wirings/workflow → services}/scenario-cookie-jar.d.ts +2 -0
- package/dist/{wirings/workflow → services}/scenario-cookie-jar.js +18 -14
- package/dist/services/scoped-secret-service.d.ts +1 -0
- package/dist/services/scoped-secret-service.js +3 -0
- package/dist/services/secret-service.d.ts +6 -0
- package/dist/services/system-role-guard.d.ts +1 -1
- package/dist/services/typed-secret-service.d.ts +1 -0
- package/dist/services/typed-secret-service.js +7 -0
- package/dist/services/virtual-user-sign-in.d.ts +52 -0
- package/dist/services/virtual-user-sign-in.js +80 -0
- package/dist/services/virtual-user-statement.d.ts +31 -0
- package/dist/services/virtual-user-statement.js +24 -0
- package/dist/services/{personas-service.d.ts → virtual-users-service.d.ts} +25 -23
- package/dist/services/{personas-service.js → virtual-users-service.js} +3 -3
- package/dist/testing/service-tests/secret-service-tests.js +9 -0
- package/dist/types/core.types.d.ts +11 -11
- package/dist/utils.d.ts +2 -5
- package/dist/utils.js +1 -38
- package/dist/wirings/cli/cli-runner.js +24 -9
- package/dist/wirings/http/http-runner.js +4 -0
- package/dist/wirings/http/http.types.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-response.js +12 -0
- package/dist/wirings/http/web-request.js +7 -0
- package/dist/wirings/role/define-system-role.d.ts +1 -1
- package/dist/wirings/role/define-system-role.js +1 -1
- package/dist/wirings/{actor-flow/actor-flow.types.d.ts → virtual-user/conversation/conversation.types.d.ts} +5 -5
- package/dist/wirings/virtual-user/conversation/index.d.ts +2 -0
- package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +36 -0
- package/dist/wirings/{actor-flow → virtual-user/conversation}/run-conversation.js +30 -30
- package/dist/wirings/virtual-user/define-virtual-users.d.ts +32 -0
- package/dist/wirings/{persona/define-personas.js → virtual-user/define-virtual-users.js} +9 -9
- package/dist/wirings/virtual-user/index.d.ts +30 -7
- package/dist/wirings/virtual-user/index.js +23 -4
- package/dist/wirings/virtual-user/prepare-virtual-user-run.d.ts +8 -8
- package/dist/wirings/virtual-user/prepare-virtual-user-run.js +9 -9
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +6 -6
- package/dist/wirings/virtual-user/run-virtual-user.js +10 -10
- package/dist/wirings/{persona/validate-personas.d.ts → virtual-user/validate-virtual-users.d.ts} +11 -11
- package/dist/wirings/{persona/validate-personas.js → virtual-user/validate-virtual-users.js} +24 -24
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +7 -7
- package/dist/wirings/virtual-user/virtual-user-agents.js +2 -2
- package/dist/wirings/{persona/persona-app-scopes.d.ts → virtual-user/virtual-user-app-scopes.d.ts} +10 -10
- package/dist/wirings/{persona/persona-app-scopes.js → virtual-user/virtual-user-app-scopes.js} +12 -12
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +3 -3
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +2 -2
- package/dist/wirings/{persona/persona.types.d.ts → virtual-user/virtual-user-declaration.types.d.ts} +24 -24
- package/dist/wirings/virtual-user/virtual-user-derive.js +13 -7
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +1 -1
- package/dist/wirings/{persona/persona-email.d.ts → virtual-user/virtual-user-email.d.ts} +11 -11
- package/dist/wirings/{persona/persona-email.js → virtual-user/virtual-user-email.js} +19 -19
- package/dist/wirings/{persona/persona-environments.d.ts → virtual-user/virtual-user-environments.d.ts} +10 -10
- package/dist/wirings/{persona/persona-environments.js → virtual-user/virtual-user-environments.js} +21 -21
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +3 -3
- package/dist/wirings/virtual-user/virtual-user-intents.js +3 -3
- package/dist/wirings/{persona/persona-mailbox.d.ts → virtual-user/virtual-user-mailbox.d.ts} +4 -4
- package/dist/wirings/virtual-user/virtual-user-run-store.d.ts +4 -4
- package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +42 -61
- package/dist/wirings/virtual-user/virtual-user-scaffold.js +69 -117
- package/dist/wirings/virtual-user/virtual-user-schedule-store.d.ts +15 -15
- package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +6 -6
- package/dist/wirings/virtual-user/virtual-user-schedule.js +11 -11
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +6 -6
- package/dist/wirings/virtual-user/virtual-user-target.js +5 -5
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +11 -11
- package/dist/wirings/virtual-user/virtual-user.types.js +1 -1
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +9 -9
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +28 -28
- package/dist/wirings/workflow/pikku-scenario-service.js +77 -77
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +4 -4
- package/dist/wirings/workflow/scenario-prose.d.ts +8 -8
- package/dist/wirings/workflow/scenario-prose.js +12 -8
- package/dist/wirings/workflow/scenario-run.types.d.ts +13 -13
- package/dist/wirings/workflow/scenario-step.types.d.ts +23 -23
- package/dist/wirings/workflow/workflow.types.d.ts +2 -2
- package/knowledge/decisions/index.md +0 -2
- package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +29 -0
- package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +29 -0
- package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +22 -0
- package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +28 -0
- package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +32 -0
- package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +12 -12
- package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +24 -0
- package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +34 -0
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +2 -2
- package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +27 -0
- package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +26 -0
- package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +22 -0
- package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +22 -0
- package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +28 -0
- package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +24 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +3 -3
- package/knowledge/decisions/internals/index.md +46 -31
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +10 -10
- package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +30 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +23 -19
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +2 -2
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +12 -12
- package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +22 -0
- package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +24 -0
- 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
- 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
- package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +43 -0
- 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
- package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +30 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +5 -5
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +2 -2
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +4 -4
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +1 -1
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +2 -2
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +1 -1
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +1 -1
- package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +26 -0
- package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +20 -0
- package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +23 -0
- package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +16 -0
- package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +26 -0
- package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +32 -0
- package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +27 -0
- package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +33 -0
- package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +25 -0
- package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +10 -5
- package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +27 -0
- package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +26 -0
- package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +25 -0
- package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +29 -0
- package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +25 -0
- package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +24 -0
- package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +29 -0
- package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +30 -0
- package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +38 -0
- package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +23 -0
- package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +27 -0
- package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +41 -0
- package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +25 -0
- package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +32 -0
- package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +20 -0
- package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +26 -0
- package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +33 -0
- package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +16 -0
- package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +26 -0
- package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +38 -0
- package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +31 -0
- package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +18 -0
- package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +32 -0
- package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +24 -0
- package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +26 -0
- package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +27 -0
- package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +25 -0
- package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +18 -0
- package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +29 -0
- package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +22 -0
- package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +29 -0
- package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +27 -0
- package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +25 -0
- package/knowledge/decisions/security/index.md +88 -24
- package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +21 -0
- package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +38 -0
- package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +18 -0
- package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +22 -0
- package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +26 -0
- package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +24 -0
- package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +35 -0
- package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +31 -0
- package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +22 -0
- package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +34 -0
- package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +26 -0
- package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +24 -0
- package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +16 -0
- package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +21 -0
- package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +24 -0
- package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +34 -0
- package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +16 -0
- package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +29 -0
- package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +23 -0
- package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +25 -0
- 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
- package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +18 -0
- package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +25 -0
- package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +42 -0
- 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
- package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +29 -0
- package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +18 -0
- 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
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +5 -5
- 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
- package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +18 -0
- package/knowledge/index.md +0 -2
- package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +24 -0
- package/knowledge/questions/dev-quick-login-as-impersonation.md +25 -0
- package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +26 -0
- package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +27 -0
- package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +26 -0
- package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +26 -0
- package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +34 -0
- package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +20 -0
- package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +24 -0
- package/knowledge/questions/does-the-store-session-match-the-other-two.md +19 -0
- package/knowledge/questions/index.md +25 -2
- package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +21 -0
- package/knowledge/questions/live-scheduled-tests-may-write.md +19 -0
- package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +23 -0
- package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +32 -0
- package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +25 -0
- package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +23 -0
- package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +35 -0
- package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +23 -0
- package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +27 -0
- package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +26 -0
- package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +20 -0
- package/knowledge/questions/staging-impersonation-rules.md +27 -0
- package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +34 -0
- package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +20 -0
- package/knowledge/questions/what-an-impersonated-session-carries.md +22 -0
- package/package.json +3 -3
- package/src/public-surface.json +43 -47
- package/dist/services/persona-actor-secret.d.ts +0 -38
- package/dist/services/persona-actor-secret.js +0 -39
- package/dist/services/persona-sign-in.d.ts +0 -117
- package/dist/services/persona-sign-in.js +0 -154
- package/dist/wirings/actor-flow/index.d.ts +0 -2
- package/dist/wirings/actor-flow/run-conversation.d.ts +0 -36
- package/dist/wirings/persona/define-personas.d.ts +0 -32
- package/dist/wirings/persona/index.d.ts +0 -23
- package/dist/wirings/persona/index.js +0 -19
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +0 -23
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +0 -25
- package/knowledge/decisions/internals/validate-checks-personas-through-a-shared-module.md +0 -43
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +0 -31
- package/knowledge/decisions/security/an-actor-credential-is-derived-per-persona.md +0 -42
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +0 -26
- /package/dist/wirings/{actor-flow/actor-flow.types.js → virtual-user/conversation/conversation.types.js} +0 -0
- /package/dist/wirings/{actor-flow → virtual-user/conversation}/index.js +0 -0
- /package/dist/wirings/{persona/persona.types.js → virtual-user/virtual-user-declaration.types.js} +0 -0
- /package/dist/wirings/{persona/persona-mailbox.js → virtual-user/virtual-user-mailbox.js} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: decision
|
|
3
|
-
title: Scenario sessions are isolated per
|
|
4
|
-
description: One jar per
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
31
|
+
`browser: true` step to run without a virtual user.
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: decision
|
|
3
|
-
title: A scenario
|
|
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
|
|
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.
|
|
11
|
-
`pikku-workflow-service.ts` calls `
|
|
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
|
|
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,
|
|
19
|
-
`dispatchStep` to `false` whenever `options.
|
|
20
|
-
outbound HTTP call made by the runner itself, and the
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
onto the run wire, or letting `
|
|
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.
|
package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md
ADDED
|
@@ -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.
|
package/knowledge/index.md
CHANGED
|
@@ -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?
|
package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md
ADDED
|
@@ -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.
|
package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md
ADDED
|
@@ -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.
|