@pikku/core 0.12.141 → 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 +33 -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/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/system-role-guard.d.ts +1 -1
- 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/types/core.types.d.ts +11 -11
- 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 +2 -3
- package/src/public-surface.json +38 -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
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: Should a Bearer token that does not verify be anonymous or refused?
|
|
4
|
+
description: authBearer refuses a malformed scheme with InvalidSessionError but lets a well-formed wrong token through as anonymous, and no comment says why
|
|
5
|
+
tags: core, auth, middleware
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Should a Bearer token that does not verify be anonymous or refused?
|
|
9
|
+
|
|
10
|
+
In `authBearer` (`packages/core/src/middleware/auth-bearer.ts`) the code splits
|
|
11
|
+
the header into scheme and token. A scheme other than `Bearer`, or an empty
|
|
12
|
+
token, throws `InvalidSessionError`. A `Bearer` token that then fails the static
|
|
13
|
+
constant-time compare, or decodes to nothing, does not throw. It calls `next()`
|
|
14
|
+
with no session, and the function gate decides what happens next.
|
|
15
|
+
|
|
16
|
+
Tests back both halves: "should throw InvalidSessionError when scheme is not
|
|
17
|
+
Bearer" and "should not set session when static token does not match". Neither
|
|
18
|
+
says why a malformed header is refused while a wrong credential is anonymous.
|
|
19
|
+
|
|
20
|
+
The JWT branch is different. When no static token is configured and a `jwtService`
|
|
21
|
+
is wired, `authBearer` calls `jwtService.decode(bearerToken)` with no try/catch
|
|
22
|
+
(`auth-bearer.ts`, around line 72). Core ships only the `JWTService` interface
|
|
23
|
+
(`packages/core/src/services/jwt-service.ts`). The implementation in this repo is
|
|
24
|
+
`JoseJWTService` (`packages/services/jose/src/jose-jwt-service.ts`). Its `decode`
|
|
25
|
+
calls `jose.jwtVerify` with HS256 only, so a bad signature or an expired token
|
|
26
|
+
throws, and so does an unknown key id (`Missing secret for id`). A token with no
|
|
27
|
+
key id is verified against the current secret. Because `authBearer` does not
|
|
28
|
+
catch that throw, with this implementation a bad JWT is a request error, not an
|
|
29
|
+
anonymous request. Other `JWTService` implementations are not in this repo, so
|
|
30
|
+
they are not covered by this.
|
|
31
|
+
|
|
32
|
+
What is asked: is an unverifiable Bearer token meant to be treated as no session,
|
|
33
|
+
so that a public function still answers it, or should it be refused at the
|
|
34
|
+
middleware the way a malformed header is? The two cases differ only in the
|
|
35
|
+
credential being well-formed, and the code makes no stated choice about it.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: Should an outgoing webhook go out unsigned when its configured signing secret resolves empty?
|
|
4
|
+
description: When config.webhook.secret names a secret that resolves to nothing, the delivery is logged as unsigned and enqueued without a signature
|
|
5
|
+
tags: core, webhooks, signing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Should an outgoing webhook go out unsigned when its configured signing secret resolves empty?
|
|
9
|
+
|
|
10
|
+
`prepareDelivery` in `packages/core/src/services/queue-webhook-service.ts:52-66`
|
|
11
|
+
reads the signing secret from `config.webhook.secret` when the caller passed none
|
|
12
|
+
(`:53-55`). If that secret resolves to nothing, the code logs an error that says
|
|
13
|
+
the outgoing webhooks "will be sent UNSIGNED" (`:56-60`) and carries on. The
|
|
14
|
+
signature header is added only when a secret is present (`:62-66`), so the job
|
|
15
|
+
is enqueued without one.
|
|
16
|
+
|
|
17
|
+
The existing note `webhook-bodies-are-signed-before-they-are-enqueued.md` covers
|
|
18
|
+
signing at enqueue time and the key staying out of the queue payload. It does not
|
|
19
|
+
cover this fallback.
|
|
20
|
+
|
|
21
|
+
**Question:** should a configured but empty signing secret send the delivery
|
|
22
|
+
unsigned with a log line, or refuse to enqueue it? The code takes the first
|
|
23
|
+
option. Nothing in the file records a reason, and the only signal is the log.
|
package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: Should HTTP virtual user sessions reset between scenarios in one CLI run?
|
|
4
|
+
description: The CLI builds HTTP virtual users once and reuses them for every scenario, and only the browser side is reset between scenarios
|
|
5
|
+
tags: core, cli, scenario, virtual-users
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Should HTTP virtual user sessions reset between scenarios in one CLI run?
|
|
9
|
+
|
|
10
|
+
In `packages/cli/src/functions/commands/scenario.ts`, `createHttpVirtualUsers`
|
|
11
|
+
runs once (around line 314), and that same `virtualUsers` object is passed to
|
|
12
|
+
every `workflowService.startWorkflow` call for each scenario (around line 745).
|
|
13
|
+
Each `HttpVirtualUser` holds its own cookie jar and its `signedIn` flag for the
|
|
14
|
+
life of the process.
|
|
15
|
+
|
|
16
|
+
Before each scenario the command calls `browserLifecycle.reset()` (around line
|
|
17
|
+
723), which discards the browser's per-scenario state. Nothing resets the HTTP
|
|
18
|
+
jars: `scenario.ts` never calls `clear` or `signOut`, and
|
|
19
|
+
`PikkuScenarioService.detachRunContext` (`packages/core/src/wirings/workflow/pikku-scenario-service.ts`)
|
|
20
|
+
drops the run's map entry but not the jars inside the shared object.
|
|
21
|
+
|
|
22
|
+
So an HTTP virtual user that signed in during one scenario is still signed in for
|
|
23
|
+
the next, unless a 401 forces a re-login. The browser side is reset to keep one
|
|
24
|
+
scenario from leaving the next signed in as somebody else, as
|
|
25
|
+
[Scenario sessions are isolated per virtual user and reset between scenarios](../decisions/security/workflow-scenario-sessions-are-isolated-per-virtual-user-and-per-scenario.md)
|
|
26
|
+
records. What is asked: is the HTTP side meant to carry sessions across scenarios
|
|
27
|
+
as a speed measure, or should it be reset the same way?
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: Should the scenario cookie jar scope cookies to the API origin?
|
|
4
|
+
description: createCookieJar sends every held cookie to whatever URL it is given, keeps only name and value from Set-Cookie, and stamps the API origin on all calls
|
|
5
|
+
tags: core, scenario, cookies
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Should the scenario cookie jar scope cookies to the API origin?
|
|
9
|
+
|
|
10
|
+
`createCookieJar` in `packages/core/src/services/scenario-cookie-jar.ts`
|
|
11
|
+
takes one `apiUrl` and reads its origin once. Its `fetch` ignores the host of
|
|
12
|
+
`input`. It sets the `Origin` header to that origin on every call, and it adds
|
|
13
|
+
every held cookie to every call whatever URL the call goes to.
|
|
14
|
+
|
|
15
|
+
From `Set-Cookie` it keeps only the `name=value` pair. `Path`, `Domain`,
|
|
16
|
+
`Secure`, `Max-Age` and `Expires` are discarded. A cookie is removed only when its
|
|
17
|
+
value is empty, so `session=x; Max-Age=0` would be stored as a live cookie.
|
|
18
|
+
|
|
19
|
+
The tests in `scenario-cookie-jar.test.ts` send every request to one local
|
|
20
|
+
server, so none of this is exercised. The `Origin` stamp is tested on purpose
|
|
21
|
+
("stamps the origin Better Auth checks on state-changing calls").
|
|
22
|
+
|
|
23
|
+
What is asked: is the jar meant only to talk to its own API, in which case the
|
|
24
|
+
cross-host behaviour does not matter, or should it honour cookie scope? The code
|
|
25
|
+
does not say, and the browser-side equivalent in `packages/playwright/src/provider.ts`
|
|
26
|
+
plants cookies on `appUrl` instead.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: Should voiceInput fetch audio URLs when no host allowlist is configured?
|
|
4
|
+
description: With no allowedAudioHosts, the audio URL is fetched through safeFetch with no allowlist, so any public host is reachable
|
|
5
|
+
tags: core, agent, voice, ssrf
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Should voiceInput fetch audio URLs when no host allowlist is configured?
|
|
9
|
+
|
|
10
|
+
`fetchAsUint8Array` in `packages/core/src/wirings/agent/voice-input.ts:57-61` calls
|
|
11
|
+
`safeFetch` with `{ allowedHosts: allowedAudioHosts }`. That value is optional.
|
|
12
|
+
The caller passes `config.allowedAudioHosts` for a URL audio part (`:136-138`).
|
|
13
|
+
|
|
14
|
+
In `packages/core/src/utils/safe-fetch.ts:269-273`, with no allowlist the only
|
|
15
|
+
host check is the private-host refusal. So a URL that is not on a list, but is
|
|
16
|
+
public, is fetched when no list is configured.
|
|
17
|
+
|
|
18
|
+
**Question:** should `voiceInput` require `allowedAudioHosts` before it fetches a
|
|
19
|
+
URL, or is the private-host block meant to be enough? The code takes the second
|
|
20
|
+
option. No note or comment in the file says which is intended.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: What rules govern impersonation on staging and live?
|
|
4
|
+
description: Staged impersonation by a signed-in admin is wanted; the switch, read-only default, expiry and logging rules are proposed but not agreed
|
|
5
|
+
tags: auth, services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# What rules govern impersonation on staging and live?
|
|
9
|
+
|
|
10
|
+
Wanted: a signed-in admin on a deployed environment can impersonate a virtual user,
|
|
11
|
+
using the header on the normal cookie session. The code already applies
|
|
12
|
+
impersonation on that path (`auth-session.ts`, the human branch).
|
|
13
|
+
|
|
14
|
+
Not yet agreed:
|
|
15
|
+
|
|
16
|
+
- **Per-environment switch.** On for staging; off for production. The request
|
|
17
|
+
is that it is off unless turned on for that environment.
|
|
18
|
+
- **Read-only by default.** Writes made while impersonating change real data.
|
|
19
|
+
- **Expiry.** An impersonation ends after a short time.
|
|
20
|
+
- **Logging.** Each impersonated request records the real admin and the target.
|
|
21
|
+
Today the impersonation line is `info` and does not record the admin against
|
|
22
|
+
each request.
|
|
23
|
+
- **Banner.** The panel shows "viewing as X".
|
|
24
|
+
|
|
25
|
+
Also unverified: the default impersonation gate reads the owner's scopes
|
|
26
|
+
(`userHoldsScopes(result.user.id, …)`), not the scopes of the key or session that
|
|
27
|
+
made the request.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: Pikku Studio Cloud has to create, hold and deliver the stage virtual user keys
|
|
4
|
+
description: This repo defines the variables and verifies statements; generating a pair per stage and signing for stage callers is Pikku Studio Cloud work that has not been done or checked
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Pikku Studio Cloud has to create, hold and deliver the stage virtual user keys
|
|
9
|
+
|
|
10
|
+
The Pikku Studio Cloud repository is not on the machine this was built on, so none of the
|
|
11
|
+
following is built or confirmed. Pikku Studio Cloud must:
|
|
12
|
+
|
|
13
|
+
1. Generate one Ed25519 key pair per stage at creation and keep the private key in its
|
|
14
|
+
backend, never on the stage.
|
|
15
|
+
2. Set the stage variable `VIRTUAL_USER_VERIFY_KEY` to the public key (SPKI DER,
|
|
16
|
+
base64), and accept that name, which starts with neither `PIKKU_` nor `PIKKUSTUDIO_`.
|
|
17
|
+
3. Set `VIRTUAL_USER_API_URL` on every stage that runs virtual users, to the public
|
|
18
|
+
URL including any HTTP prefix.
|
|
19
|
+
4. Replace the operator sign-in route and `PIKKUSTUDIO_OPERATOR_TOKEN` handling: sign
|
|
20
|
+
statements with the stage's private key for callers acting on a stage, or hand the
|
|
21
|
+
key to the CLI as `VIRTUAL_USER_SIGNING_KEY` through the channel it already uses
|
|
22
|
+
for stage credentials.
|
|
23
|
+
5. Stop sending the operator token field in `runVirtualUser` requests; the field no longer
|
|
24
|
+
exists.
|
|
25
|
+
6. For the stage switcher, sign a statement for the chosen virtual user and pass it to
|
|
26
|
+
`/sign-in/switch-virtual-user` as `statement`.
|
|
27
|
+
|
|
28
|
+
Unverified: that Pikku Studio Cloud accepts an arbitrary unreserved variable per stage, that a
|
|
29
|
+
Worker can fetch its own public hostname (the run needs this on Workers), and that the
|
|
30
|
+
`audience` plugin option copes with a proxy that rewrites the origin. A run without a
|
|
31
|
+
sign-in path reads roles from the `getMyScopes` RPC and falls back to
|
|
32
|
+
`/auth/get-session`.
|
|
33
|
+
|
|
34
|
+
**What would settle it:** Pikku Studio Cloud landing the six items, and one stage run end to end.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: A virtual user statement can be replayed once per instance
|
|
4
|
+
description: The one-time id store is an in-memory map, so on several instances or Worker isolates each accepts a statement once
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# A virtual user statement can be replayed once per instance
|
|
9
|
+
|
|
10
|
+
`JoseVirtualUserStatementVerifier` remembers accepted `jti` values in a per-process
|
|
11
|
+
map. On a single node that makes a statement single-use. On several instances, or
|
|
12
|
+
Cloudflare Workers where isolates share nothing, a captured statement can be accepted
|
|
13
|
+
once per instance within its few-minute life.
|
|
14
|
+
|
|
15
|
+
The lifetime ceiling (300 seconds) and the signed audience bound the damage; neither
|
|
16
|
+
removes it.
|
|
17
|
+
|
|
18
|
+
**What would settle it:** a shared store — the Better Auth `verification` table is
|
|
19
|
+
already there — behind the verifier, or a decision that the bound is acceptable for
|
|
20
|
+
stages. Not built.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: question
|
|
3
|
+
title: What should an impersonated session carry?
|
|
4
|
+
description: An impersonated session without a mapSession is just { userId }, dropping the orgId and virtualUser fields a normal session has; nobody has said whether that is intended
|
|
5
|
+
tags: better-auth, impersonation, sessions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# What should an impersonated session carry?
|
|
9
|
+
|
|
10
|
+
When impersonation resolves and the app has no `mapSession`,
|
|
11
|
+
`resolveImpersonatedSession` returns `{ userId: target }`
|
|
12
|
+
(`packages/services/better-auth/src/auth-session-impersonation.ts`). A normal
|
|
13
|
+
session from the same middleware carries more: the `orgId` `defaultSession` reads
|
|
14
|
+
from the active organization, the `virtualUser` flag `stampVirtualUserFlag` adds,
|
|
15
|
+
and whatever `mapSession` adds.
|
|
16
|
+
|
|
17
|
+
So an impersonated request can behave differently from the same user signing in
|
|
18
|
+
directly. The code makes the choice; no comment or note defends it.
|
|
19
|
+
|
|
20
|
+
Open: should the impersonated session copy the target's `orgId` and `virtualUser`
|
|
21
|
+
stamp, or is a bare id intended? Answering it decides whether org-scoped reads
|
|
22
|
+
work under impersonation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.142",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/pikkujs/pikku.git",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"./scenario/coverage": "./dist/wirings/workflow/scenario-coverage.js",
|
|
43
43
|
"./workflow/timeline": "./dist/wirings/workflow/run-timeline.js",
|
|
44
44
|
"./workflow/types": "./dist/wirings/workflow/workflow.types.js",
|
|
45
|
-
"./
|
|
45
|
+
"./virtual-user/conversation": "./dist/wirings/virtual-user/conversation/index.js",
|
|
46
46
|
"./virtual-user": "./dist/wirings/virtual-user/index.js",
|
|
47
47
|
"./channel/local": "./dist/wirings/channel/local/index.js",
|
|
48
48
|
"./channel/serverless": "./dist/wirings/channel/serverless/index.js",
|
|
@@ -67,7 +67,6 @@
|
|
|
67
67
|
"./credential": "./dist/wirings/credential/index.js",
|
|
68
68
|
"./scope": "./dist/wirings/scope/index.js",
|
|
69
69
|
"./role": "./dist/wirings/role/index.js",
|
|
70
|
-
"./persona": "./dist/wirings/persona/index.js",
|
|
71
70
|
"./secret": "./dist/wirings/secret/index.js",
|
|
72
71
|
"./variable": "./dist/wirings/variable/index.js",
|
|
73
72
|
"./webhook": "./dist/wirings/webhook/index.js",
|
package/src/public-surface.json
CHANGED
|
@@ -84,12 +84,12 @@
|
|
|
84
84
|
"./scenario": [
|
|
85
85
|
"PikkuScenarioService",
|
|
86
86
|
"SCENARIO_SURFACES",
|
|
87
|
-
"ScenarioActorRequired",
|
|
88
87
|
"ScenarioBrowserUnavailable",
|
|
89
88
|
"ScenarioHookError",
|
|
90
89
|
"ScenarioNoSurfaceBinding",
|
|
91
90
|
"ScenarioNoWitness",
|
|
92
91
|
"ScenarioUnwitnessedAssertion",
|
|
92
|
+
"ScenarioVirtualUserRequired",
|
|
93
93
|
"ScenarioWitnessDisagreement",
|
|
94
94
|
"addFeature",
|
|
95
95
|
"composeStepProse",
|
|
@@ -111,44 +111,64 @@
|
|
|
111
111
|
"reconstructStateAt"
|
|
112
112
|
],
|
|
113
113
|
"./workflow/types": [],
|
|
114
|
-
"./
|
|
114
|
+
"./virtual-user/conversation": [
|
|
115
115
|
"runConversation"
|
|
116
116
|
],
|
|
117
117
|
"./virtual-user": [
|
|
118
|
+
"APP_SCOPE_ROOT",
|
|
118
119
|
"DEFAULT_MAX_INTERVAL_MS",
|
|
119
120
|
"DEFAULT_MIN_INTERVAL_MS",
|
|
120
121
|
"DISPOSITIONS",
|
|
122
|
+
"HttpVirtualUser",
|
|
121
123
|
"IntentStack",
|
|
122
124
|
"PRODUCTION_DISPOSITION",
|
|
123
125
|
"STALE_RUN_AFTER_MS",
|
|
124
126
|
"VIRTUAL_USER_VARIABLES",
|
|
127
|
+
"appScopeId",
|
|
128
|
+
"authMount",
|
|
129
|
+
"buildAppScopeDefinition",
|
|
125
130
|
"catalogueClassification",
|
|
126
131
|
"catalogueLookup",
|
|
132
|
+
"createCookieJar",
|
|
133
|
+
"createHttpVirtualUsers",
|
|
134
|
+
"defineVirtualUsers",
|
|
127
135
|
"deriveCatalogue",
|
|
128
136
|
"deriveIntents",
|
|
129
137
|
"dispositionProfile",
|
|
130
138
|
"executeVirtualUserRun",
|
|
131
|
-
"
|
|
139
|
+
"inProcessSignIn",
|
|
140
|
+
"intentsForVirtualUser",
|
|
132
141
|
"isDue",
|
|
133
142
|
"isReadOnly",
|
|
143
|
+
"isRunnableVirtualUser",
|
|
134
144
|
"logVirtualUserTick",
|
|
135
145
|
"nextRunAt",
|
|
136
|
-
"
|
|
137
|
-
"personaVirtualUserTarget",
|
|
146
|
+
"postScenarioJson",
|
|
138
147
|
"prepareVirtualUserRun",
|
|
139
148
|
"reachableCatalogue",
|
|
149
|
+
"readScenarioHttpResponse",
|
|
140
150
|
"requireVirtualUserRunStore",
|
|
141
151
|
"requireVirtualUserScheduleStore",
|
|
152
|
+
"roleMismatchMessage",
|
|
142
153
|
"runVirtualUser",
|
|
143
|
-
"
|
|
154
|
+
"runnableVirtualUser",
|
|
155
|
+
"runnableVirtualUsers",
|
|
144
156
|
"serializeVirtualUserRun",
|
|
145
157
|
"serializeVirtualUserSchedule",
|
|
146
158
|
"serializeVirtualUserSteps",
|
|
147
|
-
"signInPathFor",
|
|
148
159
|
"startVirtualUserRun",
|
|
160
|
+
"statementSignIn",
|
|
149
161
|
"tickVirtualUserSchedules",
|
|
150
162
|
"unreachableCatalogue",
|
|
163
|
+
"validateAndBuildVirtualUsersMeta",
|
|
164
|
+
"verifyVirtualUserRoles",
|
|
165
|
+
"virtualUserEmail",
|
|
166
|
+
"virtualUserEmails",
|
|
167
|
+
"virtualUserEnvironmentErrors",
|
|
168
|
+
"virtualUserEnvironmentRefusal",
|
|
151
169
|
"virtualUserScheduleRunInput",
|
|
170
|
+
"virtualUserScopes",
|
|
171
|
+
"virtualUserTarget",
|
|
152
172
|
"writeVirtualUserSchedule"
|
|
153
173
|
],
|
|
154
174
|
"./channel/local": [
|
|
@@ -352,36 +372,6 @@
|
|
|
352
372
|
"flattenSystemRoleDefinitions",
|
|
353
373
|
"validateAndBuildSystemRoleDefinitionsMeta"
|
|
354
374
|
],
|
|
355
|
-
"./persona": [
|
|
356
|
-
"ACTOR_ROOT_SECRET_MIN_LENGTH",
|
|
357
|
-
"ACTOR_SECRET_INFO",
|
|
358
|
-
"ACTOR_SECRET_NAME",
|
|
359
|
-
"APP_SCOPE_ROOT",
|
|
360
|
-
"ActorSignIn",
|
|
361
|
-
"HttpPersona",
|
|
362
|
-
"IMPERSONATE_USER_ID_HEADER",
|
|
363
|
-
"OperatorSignIn",
|
|
364
|
-
"actorSecretSubject",
|
|
365
|
-
"appScopeId",
|
|
366
|
-
"authMount",
|
|
367
|
-
"buildAppScopeDefinition",
|
|
368
|
-
"createHttpPersonas",
|
|
369
|
-
"definePersonas",
|
|
370
|
-
"deriveActorSecret",
|
|
371
|
-
"establishOperatorSession",
|
|
372
|
-
"isRunnablePersona",
|
|
373
|
-
"personaEmail",
|
|
374
|
-
"personaEmails",
|
|
375
|
-
"personaEnvironmentErrors",
|
|
376
|
-
"personaEnvironmentRefusal",
|
|
377
|
-
"postScenarioJson",
|
|
378
|
-
"readScenarioHttpResponse",
|
|
379
|
-
"roleMismatchMessage",
|
|
380
|
-
"runnablePersonas",
|
|
381
|
-
"validateAndBuildPersonasMeta",
|
|
382
|
-
"verifyActorSecret",
|
|
383
|
-
"verifyPersonaRoles"
|
|
384
|
-
],
|
|
385
375
|
"./secret": [
|
|
386
376
|
"defineSecret",
|
|
387
377
|
"deriveOAuth2AppSecrets",
|
|
@@ -447,9 +437,6 @@
|
|
|
447
437
|
"isExpectedError"
|
|
448
438
|
],
|
|
449
439
|
"./services": [
|
|
450
|
-
"ACTOR_ROOT_SECRET_MIN_LENGTH",
|
|
451
|
-
"ACTOR_SECRET_INFO",
|
|
452
|
-
"ACTOR_SECRET_NAME",
|
|
453
440
|
"ConsoleLogger",
|
|
454
441
|
"DEFAULT_WEBHOOK_RETRIES",
|
|
455
442
|
"InMemoryAgentRunStateService",
|
|
@@ -488,13 +475,18 @@
|
|
|
488
475
|
"TypedCredentialService",
|
|
489
476
|
"TypedSecretService",
|
|
490
477
|
"TypedVariablesService",
|
|
478
|
+
"VIRTUAL_USER_IN_PROCESS_ENDPOINT",
|
|
479
|
+
"VIRTUAL_USER_SIGNING_KEY_VARIABLE",
|
|
480
|
+
"VIRTUAL_USER_STATEMENT_ALGORITHM",
|
|
481
|
+
"VIRTUAL_USER_STATEMENT_MAX_TTL_SECONDS",
|
|
482
|
+
"VIRTUAL_USER_STATEMENT_TTL_SECONDS",
|
|
483
|
+
"VIRTUAL_USER_VERIFY_KEY_VARIABLE",
|
|
484
|
+
"VirtualUserStatementError",
|
|
491
485
|
"WebhookService",
|
|
492
|
-
"actorSecretSubject",
|
|
493
486
|
"assertSecretAllowedForHost",
|
|
494
487
|
"createInvocationAudit",
|
|
495
488
|
"createMiddlewareSessionWireProps",
|
|
496
489
|
"createStubProxy",
|
|
497
|
-
"deriveActorSecret",
|
|
498
490
|
"getStubTracker",
|
|
499
491
|
"holdLease",
|
|
500
492
|
"isTestRun",
|
|
@@ -504,7 +496,6 @@
|
|
|
504
496
|
"renderEmail",
|
|
505
497
|
"spy",
|
|
506
498
|
"stub",
|
|
507
|
-
"verifyActorSecret",
|
|
508
499
|
"withoutSecrets"
|
|
509
500
|
],
|
|
510
501
|
"./flag": [
|
|
@@ -545,15 +536,15 @@
|
|
|
545
536
|
"createLocalContentRequestHandler",
|
|
546
537
|
"verifySignedContentRequest"
|
|
547
538
|
],
|
|
539
|
+
"./services/temporary-file-service": [
|
|
540
|
+
"TemporaryFileInstance",
|
|
541
|
+
"TemporaryFileService"
|
|
542
|
+
],
|
|
548
543
|
"./services/file-scenario-run-store": [
|
|
549
544
|
"FileScenarioRunStore",
|
|
550
545
|
"scenarioArtifactContentType",
|
|
551
546
|
"scenarioRunSummary"
|
|
552
547
|
],
|
|
553
|
-
"./services/temporary-file-service": [
|
|
554
|
-
"TemporaryFileInstance",
|
|
555
|
-
"TemporaryFileService"
|
|
556
|
-
],
|
|
557
548
|
"./crypto-utils": [
|
|
558
549
|
"MIN_KEY_MATERIAL_LENGTH",
|
|
559
550
|
"REMOTE_SESSION_INFO",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/** The name the root secret is held under, used only in error messages. */
|
|
2
|
-
export declare const ACTOR_SECRET_NAME = "SCENARIO_ACTOR_SECRET";
|
|
3
|
-
/**
|
|
4
|
-
* Namespaces the derivation so the same root secret used for anything else
|
|
5
|
-
* produces different values. See knowledge/crypto.md.
|
|
6
|
-
*/
|
|
7
|
-
export declare const ACTOR_SECRET_INFO = "pikku:actor-sign-in";
|
|
8
|
-
/** The root must be strong: every persona's credential is derived from it. */
|
|
9
|
-
export declare const ACTOR_ROOT_SECRET_MIN_LENGTH = 32;
|
|
10
|
-
/**
|
|
11
|
-
* What the derivation is bound to. Lowercased because the sign-in endpoint
|
|
12
|
-
* looks the user up by lowercased address, and a credential that verified
|
|
13
|
-
* against a different string than the row it opens is a credential for nothing.
|
|
14
|
-
*/
|
|
15
|
-
export declare const actorSecretSubject: (email: string) => string;
|
|
16
|
-
/**
|
|
17
|
-
* One persona's actor credential: `HMAC-SHA256(root, email)`, base64url.
|
|
18
|
-
*
|
|
19
|
-
* The root secret is not itself a valid credential and never travels: what a
|
|
20
|
-
* scenario run, a CI job or a virtual user is handed is the derived value for
|
|
21
|
-
* the one address it is entitled to. Presenting it for any other address fails,
|
|
22
|
-
* so a leaked credential is worth exactly one synthetic account rather than the
|
|
23
|
-
* whole actor population.
|
|
24
|
-
*
|
|
25
|
-
* Deterministic, so nothing is stored and nothing is provisioned — the target
|
|
26
|
-
* re-derives the expected value from the address being signed in as. Rotating
|
|
27
|
-
* the root invalidates every derived credential at once, which is the property
|
|
28
|
-
* a per-persona secret table would have to implement by hand.
|
|
29
|
-
*/
|
|
30
|
-
export declare const deriveActorSecret: (rootSecret: string, email: string) => Promise<string>;
|
|
31
|
-
/**
|
|
32
|
-
* Whether `presented` is the credential for `email` under `rootSecret`.
|
|
33
|
-
*
|
|
34
|
-
* False — never throws — for a malformed, truncated or mismatched value, and
|
|
35
|
-
* the comparison is WebCrypto's own HMAC verify, so it does not exit early on
|
|
36
|
-
* the first differing byte.
|
|
37
|
-
*/
|
|
38
|
-
export declare const verifyActorSecret: (rootSecret: string, email: string, presented: string) => Promise<boolean>;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { MIN_KEY_MATERIAL_LENGTH, signWithKeyMaterial, verifyWithKeyMaterial, } from '../crypto-utils.js';
|
|
2
|
-
/** The name the root secret is held under, used only in error messages. */
|
|
3
|
-
export const ACTOR_SECRET_NAME = 'SCENARIO_ACTOR_SECRET';
|
|
4
|
-
/**
|
|
5
|
-
* Namespaces the derivation so the same root secret used for anything else
|
|
6
|
-
* produces different values. See knowledge/crypto.md.
|
|
7
|
-
*/
|
|
8
|
-
export const ACTOR_SECRET_INFO = 'pikku:actor-sign-in';
|
|
9
|
-
/** The root must be strong: every persona's credential is derived from it. */
|
|
10
|
-
export const ACTOR_ROOT_SECRET_MIN_LENGTH = MIN_KEY_MATERIAL_LENGTH;
|
|
11
|
-
/**
|
|
12
|
-
* What the derivation is bound to. Lowercased because the sign-in endpoint
|
|
13
|
-
* looks the user up by lowercased address, and a credential that verified
|
|
14
|
-
* against a different string than the row it opens is a credential for nothing.
|
|
15
|
-
*/
|
|
16
|
-
export const actorSecretSubject = (email) => email.trim().toLowerCase();
|
|
17
|
-
/**
|
|
18
|
-
* One persona's actor credential: `HMAC-SHA256(root, email)`, base64url.
|
|
19
|
-
*
|
|
20
|
-
* The root secret is not itself a valid credential and never travels: what a
|
|
21
|
-
* scenario run, a CI job or a virtual user is handed is the derived value for
|
|
22
|
-
* the one address it is entitled to. Presenting it for any other address fails,
|
|
23
|
-
* so a leaked credential is worth exactly one synthetic account rather than the
|
|
24
|
-
* whole actor population.
|
|
25
|
-
*
|
|
26
|
-
* Deterministic, so nothing is stored and nothing is provisioned — the target
|
|
27
|
-
* re-derives the expected value from the address being signed in as. Rotating
|
|
28
|
-
* the root invalidates every derived credential at once, which is the property
|
|
29
|
-
* a per-persona secret table would have to implement by hand.
|
|
30
|
-
*/
|
|
31
|
-
export const deriveActorSecret = async (rootSecret, email) => signWithKeyMaterial(ACTOR_SECRET_NAME, rootSecret, ACTOR_SECRET_INFO, actorSecretSubject(email));
|
|
32
|
-
/**
|
|
33
|
-
* Whether `presented` is the credential for `email` under `rootSecret`.
|
|
34
|
-
*
|
|
35
|
-
* False — never throws — for a malformed, truncated or mismatched value, and
|
|
36
|
-
* the comparison is WebCrypto's own HMAC verify, so it does not exit early on
|
|
37
|
-
* the first differing byte.
|
|
38
|
-
*/
|
|
39
|
-
export const verifyActorSecret = async (rootSecret, email, presented) => verifyWithKeyMaterial(ACTOR_SECRET_NAME, rootSecret, ACTOR_SECRET_INFO, actorSecretSubject(email), presented);
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import type { ResolvedPersona } from './personas-service.js';
|
|
2
|
-
import type { ScenarioCookieJar } from '../wirings/workflow/scenario-cookie-jar.js';
|
|
3
|
-
/**
|
|
4
|
-
* The header `resolveImpersonatedSession` reads the target user id from.
|
|
5
|
-
*
|
|
6
|
-
* A wire value rather than a shared import: the reader lives in
|
|
7
|
-
* `@pikku/services-better-auth`, which depends on core, so core cannot import
|
|
8
|
-
* it back. The two agree by protocol, the way an HTTP header always does.
|
|
9
|
-
*/
|
|
10
|
-
export declare const IMPERSONATE_USER_ID_HEADER = "x-pikku-impersonate-user-id";
|
|
11
|
-
/**
|
|
12
|
-
* How a persona obtains a session on the target, and what every later request
|
|
13
|
-
* needs to carry to keep acting as them.
|
|
14
|
-
*
|
|
15
|
-
* Two answers exist because the two environments have opposite trust models,
|
|
16
|
-
* not because one is a fallback for the other. See {@link ActorSignIn} and
|
|
17
|
-
* {@link OperatorSignIn}.
|
|
18
|
-
*/
|
|
19
|
-
export interface PersonaSignIn {
|
|
20
|
-
/**
|
|
21
|
-
* Establish a session in `jar`. Throws on failure with a message naming the
|
|
22
|
-
* persona, since a run that continues unauthenticated fails later and
|
|
23
|
-
* somewhere less informative.
|
|
24
|
-
*/
|
|
25
|
-
login(jar: ScenarioCookieJar, persona: ResolvedPersona): Promise<void>;
|
|
26
|
-
/** Headers every request after `login` must carry. */
|
|
27
|
-
headers(): Record<string, string>;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Yields the credential for one persona, for a caller that holds that persona's
|
|
31
|
-
* derived secret and not the root it came from.
|
|
32
|
-
*/
|
|
33
|
-
export type ActorSecretResolver = (persona: ResolvedPersona) => string | Promise<string>;
|
|
34
|
-
/**
|
|
35
|
-
* Sign a persona in through the Better Auth actor plugin — the local-development
|
|
36
|
-
* path.
|
|
37
|
-
*
|
|
38
|
-
* `POST /auth/sign-in/actor` upserts an `actor: true` row and mints a session
|
|
39
|
-
* for it. Passwordless by design and refused for any row not carrying that flag,
|
|
40
|
-
* so the secret can never reach a real user's account; the plugin still declines
|
|
41
|
-
* to serve the endpoint at all outside `pikku dev`.
|
|
42
|
-
*
|
|
43
|
-
* What is presented is the persona's own credential, derived from the root and
|
|
44
|
-
* bound to their address. A run driving many personas holds the root and
|
|
45
|
-
* derives as it goes; a run entitled to one persona is handed that one value
|
|
46
|
-
* through a resolver and can sign in as nobody else.
|
|
47
|
-
*/
|
|
48
|
-
export declare class ActorSignIn implements PersonaSignIn {
|
|
49
|
-
private readonly apiUrl;
|
|
50
|
-
private readonly secret;
|
|
51
|
-
private readonly signInPath;
|
|
52
|
-
constructor(apiUrl: string, secret: string | ActorSecretResolver, signInPath: string);
|
|
53
|
-
login(jar: ScenarioCookieJar, persona: ResolvedPersona): Promise<void>;
|
|
54
|
-
headers(): Record<string, string>;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* The auth mount a configured sign-in path sits under, or `undefined` when it
|
|
58
|
-
* names nothing recognisable.
|
|
59
|
-
*
|
|
60
|
-
* better-auth serves sign-in, operator sign-in and `get-session` from one
|
|
61
|
-
* prefix, so an app that mounts it at `/api/auth` moves all three together and
|
|
62
|
-
* says so once through `signInPath`. Reading the other two from a hardcoded
|
|
63
|
-
* `/auth` on such an app 404s — and for `get-session` a 404 reads as "this
|
|
64
|
-
* stage does not report roles", which silently turns off the check that tells a
|
|
65
|
-
* permissions finding from seed drift.
|
|
66
|
-
*/
|
|
67
|
-
export declare const authMount: (signInPath?: string) => string | undefined;
|
|
68
|
-
export interface OperatorSignInOptions {
|
|
69
|
-
/**
|
|
70
|
-
* The short-lived RS256 operator token, or a function that mints one. Prefer
|
|
71
|
-
* the function: tokens expire, and a long run re-logs-in after a 401.
|
|
72
|
-
*/
|
|
73
|
-
token: string | (() => string | Promise<string>);
|
|
74
|
-
/** Fabric operator sign-in path under apiUrl. Default `/auth/sign-in/fabric`. */
|
|
75
|
-
signInPath?: string;
|
|
76
|
-
}
|
|
77
|
-
/** What an operator handshake yields: the session, and who to act as. */
|
|
78
|
-
export interface OperatorSessionResult {
|
|
79
|
-
/** `Set-Cookie` values the operator sign-in returned. */
|
|
80
|
-
setCookies: string[];
|
|
81
|
-
/** The target's own id for the persona, for the impersonation header. */
|
|
82
|
-
userId: string;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Establish a Fabric operator session against `apiUrl` and resolve the target's
|
|
86
|
-
* own id for `persona`, which is what the impersonation header names.
|
|
87
|
-
*
|
|
88
|
-
* Takes the fetch to use rather than making one, because the two callers need
|
|
89
|
-
* the cookies to land in different places: an HTTP persona keeps them in its
|
|
90
|
-
* jar, a browser run plants them on a Playwright context. Both need the same
|
|
91
|
-
* handshake, and it is the kind of sequence that quietly diverges once it is
|
|
92
|
-
* written twice.
|
|
93
|
-
*/
|
|
94
|
-
export declare const establishOperatorSession: (fetchImpl: typeof fetch, apiUrl: string, persona: ResolvedPersona, options: OperatorSignInOptions, extraHeaders?: Record<string, string>) => Promise<OperatorSessionResult>;
|
|
95
|
-
/**
|
|
96
|
-
* Sign a persona in on a DEPLOYED stage, by having a Fabric operator act as
|
|
97
|
-
* them — the path that needs no test credential to exist anywhere.
|
|
98
|
-
*
|
|
99
|
-
* `POST /auth/sign-in/fabric` verifies an RS256 token against the stage's
|
|
100
|
-
* `FABRIC_AUTH_PUBLIC_KEY` and mints a session for a synthetic operator row
|
|
101
|
-
* granted the umbrella `admin` scope. Impersonation is then a header on each
|
|
102
|
-
* request rather than a second session, and its gate is that scope — not
|
|
103
|
-
* `user.role`, which is why this works without touching the app's roles.
|
|
104
|
-
*
|
|
105
|
-
* Asymmetric throughout: the stage can verify an operator token and never mint
|
|
106
|
-
* one, so nothing in a deployed environment is worth stealing. That is the
|
|
107
|
-
* property the actor secret cannot have, and the reason these are two classes
|
|
108
|
-
* instead of one with a flag.
|
|
109
|
-
*/
|
|
110
|
-
export declare class OperatorSignIn implements PersonaSignIn {
|
|
111
|
-
private readonly apiUrl;
|
|
112
|
-
private readonly options;
|
|
113
|
-
private userId;
|
|
114
|
-
constructor(apiUrl: string, options: OperatorSignInOptions);
|
|
115
|
-
login(jar: ScenarioCookieJar, persona: ResolvedPersona): Promise<void>;
|
|
116
|
-
headers(): Record<string, string>;
|
|
117
|
-
}
|