@pikku/core 0.12.72 → 0.12.74
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 +793 -0
- package/dist/crypto-utils.d.ts +30 -5
- package/dist/crypto-utils.js +146 -41
- package/dist/dev/hot-reload.js +11 -30
- package/dist/dev/module-runner.d.ts +3 -7
- package/dist/dev/module-runner.js +4 -10
- package/dist/dev/reload-meta.d.ts +8 -20
- package/dist/dev/reload-meta.js +9 -29
- package/dist/errors/error-handler.d.ts +5 -30
- package/dist/errors/error-handler.js +16 -32
- package/dist/errors/errors.d.ts +32 -151
- package/dist/errors/errors.js +55 -157
- package/dist/function/abort-scope.d.ts +47 -0
- package/dist/function/abort-scope.js +63 -0
- package/dist/function/function-runner.js +37 -32
- package/dist/function/functions.types.d.ts +44 -136
- package/dist/function/functions.types.js +0 -58
- package/dist/function/list.types.d.ts +12 -62
- package/dist/function/list.types.js +4 -25
- package/dist/handle-error.d.ts +0 -13
- package/dist/handle-error.js +0 -18
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/middleware/auth-apikey.d.ts +3 -18
- package/dist/middleware/auth-apikey.js +0 -17
- package/dist/middleware/auth-bearer.d.ts +6 -41
- package/dist/middleware/auth-bearer.js +3 -40
- package/dist/middleware/auth-cookie.d.ts +5 -27
- package/dist/middleware/auth-cookie.js +2 -26
- package/dist/middleware/cors.d.ts +7 -34
- package/dist/middleware/cors.js +7 -34
- package/dist/middleware/remote-auth.d.ts +3 -1
- package/dist/middleware/remote-auth.js +3 -2
- package/dist/middleware/telemetry.d.ts +8 -33
- package/dist/middleware/telemetry.js +2 -31
- package/dist/middleware-runner.d.ts +4 -55
- package/dist/middleware-runner.js +5 -74
- package/dist/permissions.d.ts +3 -44
- package/dist/permissions.js +19 -71
- package/dist/pikku-request.d.ts +0 -6
- package/dist/pikku-request.js +0 -6
- package/dist/pikku-state.d.ts +0 -26
- package/dist/pikku-state.js +2 -30
- package/dist/remote.d.ts +3 -5
- package/dist/remote.js +8 -7
- package/dist/schema.d.ts +5 -39
- package/dist/schema.js +5 -39
- package/dist/scopes.d.ts +4 -23
- package/dist/scopes.js +7 -48
- package/dist/services/ai-agent-runner-service.d.ts +20 -0
- package/dist/services/ai-embedding-service.d.ts +2 -25
- package/dist/services/audit-service.js +1 -2
- package/dist/services/content-service.d.ts +1 -46
- package/dist/services/credential-service.d.ts +3 -40
- package/dist/services/deployment-service.d.ts +3 -9
- package/dist/services/gateway-service.d.ts +0 -15
- package/dist/services/http-personas.d.ts +80 -0
- package/dist/services/http-personas.js +233 -0
- package/dist/services/in-memory-queue-service.d.ts +0 -14
- package/dist/services/in-memory-queue-service.js +1 -15
- package/dist/services/in-memory-trigger-service.d.ts +0 -18
- package/dist/services/in-memory-trigger-service.js +1 -18
- package/dist/services/in-memory-workflow-service.d.ts +0 -16
- package/dist/services/in-memory-workflow-service.js +4 -33
- package/dist/services/index.d.ts +5 -6
- package/dist/services/index.js +2 -5
- package/dist/services/istanbul-coverage-service.d.ts +1 -5
- package/dist/services/istanbul-coverage-service.js +2 -8
- package/dist/services/jwt-service.d.ts +1 -16
- package/dist/services/local-content.d.ts +13 -2
- package/dist/services/local-content.js +40 -13
- package/dist/services/local-gateway-service.d.ts +0 -16
- package/dist/services/local-gateway-service.js +2 -17
- package/dist/services/local-secrets.d.ts +0 -4
- package/dist/services/local-secrets.js +0 -4
- package/dist/services/logger-console.d.ts +3 -7
- package/dist/services/logger-console.js +3 -7
- package/dist/services/logger.d.ts +2 -37
- package/dist/services/meta-service.d.ts +23 -26
- package/dist/services/meta-service.js +22 -36
- package/dist/services/personas-service.d.ts +134 -0
- package/dist/services/personas-service.js +40 -0
- package/dist/services/pikku-user-id.js +0 -4
- package/dist/services/queue-webhook-service.d.ts +2 -36
- package/dist/services/queue-webhook-service.js +9 -41
- package/dist/services/scheduler-service.d.ts +1 -50
- package/dist/services/scheduler-service.js +0 -10
- package/dist/services/schema-service.d.ts +1 -24
- package/dist/services/scope-service.d.ts +49 -34
- package/dist/services/scoped-secret-service.d.ts +0 -4
- package/dist/services/scoped-secret-service.js +0 -4
- package/dist/services/secret-host-binding.d.ts +8 -0
- package/dist/services/secret-host-binding.js +36 -0
- package/dist/services/secret-service.d.ts +5 -33
- package/dist/services/secretless.d.ts +6 -0
- package/dist/services/secretless.js +21 -0
- package/dist/services/stub-tracker.d.ts +7 -18
- package/dist/services/stub-tracker.js +8 -18
- package/dist/services/system-role-guard.d.ts +33 -0
- package/dist/services/system-role-guard.js +38 -0
- package/dist/services/trigger-service.d.ts +0 -12
- package/dist/services/typed-secret-service.d.ts +0 -7
- package/dist/services/typed-secret-service.js +1 -7
- package/dist/services/v8-coverage-service.d.ts +2 -3
- package/dist/services/v8-coverage-service.js +1 -2
- package/dist/services/variables-service.d.ts +1 -8
- package/dist/services/webhook-service.d.ts +19 -63
- package/dist/services/webhook-service.js +6 -20
- package/dist/services/workflow-service.d.ts +3 -15
- package/dist/testing/service-tests.js +0 -17
- package/dist/time-utils.d.ts +0 -16
- package/dist/time-utils.js +1 -19
- package/dist/types/core.types.d.ts +99 -219
- package/dist/types/core.types.js +0 -42
- package/dist/types/state.types.d.ts +4 -9
- package/dist/utils/hmac.d.ts +4 -10
- package/dist/utils/hmac.js +4 -10
- package/dist/utils/safe-fetch.d.ts +7 -35
- package/dist/utils/safe-fetch.js +13 -53
- package/dist/utils.d.ts +1 -6
- package/dist/utils.js +6 -15
- package/dist/wirings/actor-flow/actor-flow.types.d.ts +1 -34
- package/dist/wirings/actor-flow/index.d.ts +0 -9
- package/dist/wirings/actor-flow/run-conversation.d.ts +5 -5
- package/dist/wirings/actor-flow/run-conversation.js +14 -7
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +0 -5
- package/dist/wirings/ai-agent/ai-agent-agui.js +34 -12
- package/dist/wirings/ai-agent/ai-agent-helpers.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-helpers.js +7 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.d.ts +153 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +256 -0
- package/dist/wirings/ai-agent/ai-agent-memory.js +0 -2
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +0 -9
- package/dist/wirings/ai-agent/ai-agent-model-config.js +1 -9
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -99
- package/dist/wirings/ai-agent/ai-agent-prepare.js +58 -131
- package/dist/wirings/ai-agent/ai-agent-registry.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-registry.js +5 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +49 -20
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +25 -2
- package/dist/wirings/ai-agent/ai-agent-stream.js +153 -63
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +84 -4
- package/dist/wirings/ai-agent/index.d.ts +6 -4
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-agent/voice-input.d.ts +39 -1
- package/dist/wirings/ai-agent/voice-input.js +46 -3
- package/dist/wirings/ai-agent/voice-output.d.ts +54 -1
- package/dist/wirings/ai-agent/voice-output.js +153 -50
- package/dist/wirings/channel/channel-common.d.ts +7 -20
- package/dist/wirings/channel/channel-common.js +7 -21
- package/dist/wirings/channel/channel-handler.js +25 -6
- package/dist/wirings/channel/channel-host-rpc.d.ts +25 -0
- package/dist/wirings/channel/channel-host-rpc.js +38 -0
- package/dist/wirings/channel/channel-middleware-runner.d.ts +0 -12
- package/dist/wirings/channel/channel-middleware-runner.js +0 -12
- package/dist/wirings/channel/channel-rpc-registry.d.ts +31 -0
- package/dist/wirings/channel/channel-rpc-registry.js +89 -0
- package/dist/wirings/channel/channel-rpc-responder.d.ts +15 -0
- package/dist/wirings/channel/channel-rpc-responder.js +71 -0
- package/dist/wirings/channel/channel-rpc-service.d.ts +40 -0
- package/dist/wirings/channel/channel-rpc-service.js +106 -0
- package/dist/wirings/channel/channel-rpc-validators.d.ts +14 -0
- package/dist/wirings/channel/channel-rpc-validators.js +30 -0
- package/dist/wirings/channel/channel-rpc.d.ts +5 -0
- package/dist/wirings/channel/channel-rpc.js +5 -0
- package/dist/wirings/channel/channel-rpc.types.d.ts +90 -0
- package/dist/wirings/channel/channel-rpc.types.js +50 -0
- package/dist/wirings/channel/channel-runner.d.ts +0 -4
- package/dist/wirings/channel/channel-runner.js +0 -14
- package/dist/wirings/channel/channel-store.d.ts +0 -10
- package/dist/wirings/channel/channel.types.d.ts +12 -1
- package/dist/wirings/channel/define-channel-routes.d.ts +0 -20
- package/dist/wirings/channel/define-channel-routes.js +0 -20
- package/dist/wirings/channel/eventhub-service.d.ts +0 -18
- package/dist/wirings/channel/index.d.ts +4 -1
- package/dist/wirings/channel/index.js +2 -0
- package/dist/wirings/channel/local/local-channel-runner.js +3 -1
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +0 -33
- package/dist/wirings/channel/local/local-eventhub-service.js +2 -36
- package/dist/wirings/channel/log-channels.d.ts +0 -4
- package/dist/wirings/channel/log-channels.js +0 -4
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +6 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +2 -5
- package/dist/wirings/cli/channel/cli-approval.d.ts +41 -0
- package/dist/wirings/cli/channel/cli-approval.js +81 -0
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +0 -4
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -25
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +47 -9
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +24 -16
- package/dist/wirings/cli/channel/cli-raw-client-runner.d.ts +20 -0
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +121 -0
- package/dist/wirings/cli/channel/index.d.ts +4 -0
- package/dist/wirings/cli/channel/index.js +2 -0
- package/dist/wirings/cli/cli-runner.d.ts +20 -20
- package/dist/wirings/cli/cli-runner.js +28 -89
- package/dist/wirings/cli/cli.types.d.ts +14 -3
- package/dist/wirings/cli/command-parser.d.ts +1 -10
- package/dist/wirings/cli/command-parser.js +10 -87
- package/dist/wirings/cli/define-cli-commands.d.ts +1 -17
- package/dist/wirings/cli/define-cli-commands.js +1 -17
- package/dist/wirings/credential/credential.types.d.ts +0 -12
- package/dist/wirings/credential/define-credential.d.ts +48 -0
- package/dist/wirings/credential/define-credential.js +47 -0
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/credential/index.js +1 -1
- package/dist/wirings/credential/validate-credential-definitions.d.ts +2 -4
- package/dist/wirings/gateway/gateway-runner.d.ts +1 -20
- package/dist/wirings/gateway/gateway-runner.js +8 -105
- package/dist/wirings/gateway/gateway.types.d.ts +7 -80
- package/dist/wirings/http/http-routes.d.ts +0 -63
- package/dist/wirings/http/http-routes.js +0 -63
- package/dist/wirings/http/http-runner.d.ts +0 -99
- package/dist/wirings/http/http-runner.js +9 -165
- package/dist/wirings/http/http.types.d.ts +14 -55
- package/dist/wirings/http/log-http-routes.d.ts +0 -4
- package/dist/wirings/http/log-http-routes.js +0 -4
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +0 -40
- package/dist/wirings/http/pikku-fetch-http-request.js +0 -58
- package/dist/wirings/http/pikku-fetch-http-response.js +0 -3
- package/dist/wirings/http/routers/path-to-regex.js +2 -13
- package/dist/wirings/http/web-request.d.ts +0 -8
- package/dist/wirings/http/web-request.js +25 -17
- package/dist/wirings/mcp/mcp-runner.d.ts +1 -4
- package/dist/wirings/mcp/mcp-runner.js +1 -14
- package/dist/wirings/mcp/mcp.types.d.ts +2 -35
- package/dist/wirings/oauth2/oauth2.types.d.ts +0 -28
- package/dist/wirings/oauth2/oauth2.types.js +0 -3
- package/dist/wirings/persona/define-personas.d.ts +28 -0
- package/dist/wirings/persona/define-personas.js +27 -0
- package/dist/wirings/persona/index.d.ts +21 -0
- package/dist/wirings/persona/index.js +17 -0
- package/dist/wirings/persona/persona-email.d.ts +37 -0
- package/dist/wirings/persona/persona-email.js +69 -0
- package/dist/wirings/persona/persona-environments.d.ts +45 -0
- package/dist/wirings/persona/persona-environments.js +81 -0
- package/dist/wirings/persona/persona-mailbox.d.ts +101 -0
- package/dist/wirings/persona/persona-mailbox.js +53 -0
- package/dist/wirings/persona/persona.types.d.ts +125 -0
- package/dist/wirings/persona/persona.types.js +1 -0
- package/dist/wirings/persona/validate-personas.d.ts +53 -0
- package/dist/wirings/persona/validate-personas.js +94 -0
- package/dist/wirings/queue/index.d.ts +3 -0
- package/dist/wirings/queue/index.js +2 -3
- package/dist/wirings/queue/queue-identity.d.ts +28 -0
- package/dist/wirings/queue/queue-identity.js +102 -0
- package/dist/wirings/queue/queue-runner.d.ts +0 -19
- package/dist/wirings/queue/queue-runner.js +9 -30
- package/dist/wirings/queue/queue.types.d.ts +18 -89
- package/dist/wirings/queue/register-queue-helper.d.ts +0 -12
- package/dist/wirings/queue/register-queue-helper.js +0 -11
- package/dist/wirings/queue/signed-queue-service.d.ts +16 -0
- package/dist/wirings/queue/signed-queue-service.js +42 -0
- package/dist/wirings/queue/validate-worker-config.d.ts +2 -23
- package/dist/wirings/queue/validate-worker-config.js +0 -14
- package/dist/wirings/role/define-system-role.d.ts +32 -0
- package/dist/wirings/role/define-system-role.js +31 -0
- package/dist/wirings/role/index.d.ts +3 -0
- package/dist/wirings/role/index.js +2 -0
- package/dist/wirings/role/role.types.d.ts +43 -0
- package/dist/wirings/role/role.types.js +1 -0
- package/dist/wirings/role/validate-role-definitions.d.ts +21 -0
- package/dist/wirings/role/validate-role-definitions.js +71 -0
- package/dist/wirings/rpc/addon-runner.d.ts +0 -19
- package/dist/wirings/rpc/addon-runner.js +0 -51
- package/dist/wirings/rpc/remote-addon-auth.d.ts +1 -12
- package/dist/wirings/rpc/remote-addon-auth.js +1 -9
- package/dist/wirings/rpc/rpc-runner.d.ts +11 -18
- package/dist/wirings/rpc/rpc-runner.js +88 -105
- package/dist/wirings/rpc/rpc-types.d.ts +7 -6
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +62 -0
- package/dist/wirings/rpc/wire-remote-addon.d.ts +3 -28
- package/dist/wirings/rpc/wire-remote-addon.js +0 -8
- package/dist/wirings/scheduler/log-schedulers.d.ts +0 -4
- package/dist/wirings/scheduler/log-schedulers.js +0 -4
- package/dist/wirings/scheduler/scheduler-runner.d.ts +0 -1
- package/dist/wirings/scheduler/scheduler-runner.js +0 -1
- package/dist/wirings/scheduler/scheduler.types.d.ts +1 -14
- package/dist/wirings/scope/define-scope.d.ts +32 -0
- package/dist/wirings/scope/define-scope.js +31 -0
- package/dist/wirings/scope/index.d.ts +1 -1
- package/dist/wirings/scope/index.js +1 -1
- package/dist/wirings/scope/scope.types.d.ts +7 -9
- package/dist/wirings/scope/validate-scope-definitions.d.ts +2 -21
- package/dist/wirings/scope/validate-scope-definitions.js +3 -21
- package/dist/wirings/secret/index.d.ts +1 -1
- package/dist/wirings/secret/index.js +1 -1
- package/dist/wirings/secret/secret.types.d.ts +19 -15
- package/dist/wirings/secret/secret.types.js +1 -1
- package/dist/wirings/secret/validate-secret-definitions.d.ts +2 -4
- package/dist/wirings/trigger/trigger-runner.d.ts +0 -27
- package/dist/wirings/trigger/trigger-runner.js +1 -24
- package/dist/wirings/trigger/trigger.types.d.ts +1 -82
- package/dist/wirings/trigger/trigger.types.js +0 -34
- package/dist/wirings/variable/index.d.ts +1 -1
- package/dist/wirings/variable/index.js +1 -1
- package/dist/wirings/variable/validate-variable-definitions.d.ts +2 -4
- package/dist/wirings/variable/variable.types.d.ts +1 -13
- package/dist/wirings/variable/variable.types.js +1 -1
- package/dist/wirings/virtual-user/index.d.ts +27 -0
- package/dist/wirings/virtual-user/index.js +8 -0
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +92 -0
- package/dist/wirings/virtual-user/run-virtual-user.js +478 -0
- package/dist/wirings/virtual-user/virtual-user-agents.d.ts +38 -0
- package/dist/wirings/virtual-user/virtual-user-agents.js +24 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.d.ts +92 -0
- package/dist/wirings/virtual-user/virtual-user-catalogue.js +134 -0
- package/dist/wirings/virtual-user/virtual-user-derive.d.ts +26 -0
- package/dist/wirings/virtual-user/virtual-user-derive.js +137 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.d.ts +79 -0
- package/dist/wirings/virtual-user/virtual-user-dispositions.js +128 -0
- package/dist/wirings/virtual-user/virtual-user-intents.d.ts +78 -0
- package/dist/wirings/virtual-user/virtual-user-intents.js +142 -0
- package/dist/wirings/virtual-user/virtual-user-rng.d.ts +24 -0
- package/dist/wirings/virtual-user/virtual-user-rng.js +44 -0
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +21 -0
- package/dist/wirings/virtual-user/virtual-user-target.js +34 -0
- package/dist/wirings/virtual-user/virtual-user.types.d.ts +199 -0
- package/dist/wirings/virtual-user/virtual-user.types.js +8 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +5 -5
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +0 -4
- package/dist/wirings/workflow/dsl/workflow-runner.js +0 -4
- package/dist/wirings/workflow/feature.d.ts +0 -19
- package/dist/wirings/workflow/feature.js +0 -19
- package/dist/wirings/workflow/graph/graph-node.d.ts +0 -98
- package/dist/wirings/workflow/graph/graph-node.js +0 -34
- package/dist/wirings/workflow/graph/graph-runner.js +6 -41
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +0 -4
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +0 -58
- package/dist/wirings/workflow/graph/workflow-graph.types.js +0 -6
- package/dist/wirings/workflow/index.d.ts +5 -7
- package/dist/wirings/workflow/index.js +3 -17
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +87 -5
- package/dist/wirings/workflow/pikku-scenario-service.js +204 -42
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +7 -459
- package/dist/wirings/workflow/pikku-workflow-service.js +58 -551
- package/dist/wirings/workflow/run-timeline.d.ts +0 -47
- package/dist/wirings/workflow/run-timeline.js +0 -22
- package/dist/wirings/workflow/scenario-cookie-jar.d.ts +0 -23
- package/dist/wirings/workflow/scenario-cookie-jar.js +0 -16
- package/dist/wirings/workflow/scenario-poll.d.ts +0 -15
- package/dist/wirings/workflow/scenario-poll.js +0 -12
- package/dist/wirings/workflow/scenario-prose.d.ts +0 -28
- package/dist/wirings/workflow/scenario-prose.js +0 -18
- package/dist/wirings/workflow/scenario-step-guards.d.ts +0 -13
- package/dist/wirings/workflow/scenario-step-guards.js +1 -14
- package/dist/wirings/workflow/scenario-step.types.d.ts +72 -6
- package/dist/wirings/workflow/scenario-step.types.js +5 -1
- package/dist/wirings/workflow/scenario-surface.d.ts +16 -0
- package/dist/wirings/workflow/scenario-surface.js +56 -0
- package/dist/wirings/workflow/workflow-invocation-id.d.ts +0 -18
- package/dist/wirings/workflow/workflow-invocation-id.js +2 -22
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +0 -20
- package/dist/wirings/workflow/workflow-queue-workers.js +0 -19
- package/dist/wirings/workflow/workflow.types.d.ts +0 -197
- package/knowledge/decisions/index.md +19 -0
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +49 -0
- package/knowledge/decisions/internals/actor-flow-conversations-seed-a-hidden-kickoff-message.md +23 -0
- package/knowledge/decisions/internals/actor-flow-drives-the-target-through-a-transport-seam.md +24 -0
- package/knowledge/decisions/internals/actor-flow-verdicts-are-llm-self-evaluations.md +25 -0
- package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +26 -0
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +33 -0
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +28 -0
- package/knowledge/decisions/internals/ai-agent-agui-bridge-obeys-the-client-ordering-contract.md +29 -0
- package/knowledge/decisions/internals/ai-agent-audio-chunks-carry-the-format-the-provider-returned.md +20 -0
- package/knowledge/decisions/internals/ai-agent-credential-suspensions-hide-the-tool-result.md +26 -0
- package/knowledge/decisions/internals/ai-agent-delegate-and-supervise-hide-different-text.md +26 -0
- package/knowledge/decisions/internals/ai-agent-llm-tool-arguments-have-nulls-stripped.md +23 -0
- package/knowledge/decisions/internals/ai-agent-model-config-stays-a-single-resolution-seam.md +25 -0
- package/knowledge/decisions/internals/ai-agent-onerror-hooks-cannot-change-the-failure.md +22 -0
- package/knowledge/decisions/internals/ai-agent-runner-methods-must-keep-their-receiver.md +22 -0
- package/knowledge/decisions/internals/ai-agent-stream-persistence-is-best-effort.md +27 -0
- package/knowledge/decisions/internals/ai-agent-sub-agents-inherit-the-parent-context-block.md +26 -0
- package/knowledge/decisions/internals/ai-agent-tool-execute-failures-are-logged-unconditionally.md +25 -0
- package/knowledge/decisions/internals/ai-agent-voice-input-transcribes-audio-parts-in-place.md +22 -0
- package/knowledge/decisions/internals/ai-agent-working-memory-is-persisted-only-when-valid.md +25 -0
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +30 -0
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +31 -0
- package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +29 -0
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +28 -0
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +27 -0
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +28 -0
- package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +34 -0
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +29 -0
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +34 -0
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +44 -0
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +39 -0
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +42 -0
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +39 -0
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +43 -0
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +38 -0
- package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +44 -0
- package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +27 -0
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +32 -0
- package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +28 -0
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +31 -0
- package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +26 -0
- package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +32 -0
- package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +28 -0
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +30 -0
- package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +26 -0
- package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +28 -0
- package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +32 -0
- package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +26 -0
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +25 -0
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +26 -0
- package/knowledge/decisions/internals/index.md +113 -0
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +25 -0
- package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +26 -0
- package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +24 -0
- package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +28 -0
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +27 -0
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +31 -0
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +32 -0
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +27 -0
- package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +26 -0
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +27 -0
- package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +30 -0
- package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +28 -0
- package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +30 -0
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +29 -0
- package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +27 -0
- package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +40 -0
- package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +26 -0
- package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +33 -0
- package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +26 -0
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +26 -0
- package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +25 -0
- package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +58 -0
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +34 -0
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +42 -0
- package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +38 -0
- package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +29 -0
- package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +42 -0
- package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +25 -0
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +29 -0
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +43 -0
- package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +29 -0
- package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +42 -0
- package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +33 -0
- package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +32 -0
- package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +31 -0
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +39 -0
- package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +29 -0
- package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +33 -0
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +37 -0
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +50 -0
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +43 -0
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +32 -0
- package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +18 -0
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +34 -0
- package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +26 -0
- package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +33 -0
- package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +27 -0
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +34 -0
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +38 -0
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +30 -0
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +26 -0
- package/knowledge/decisions/security/actor-flow-missing-approval-decisions-default-to-denied.md +22 -0
- package/knowledge/decisions/security/actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +27 -0
- package/knowledge/decisions/security/actor-sign-in-only-works-for-actor-flagged-users.md +27 -0
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +43 -0
- package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +52 -0
- package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +46 -0
- package/knowledge/decisions/security/ai-agent-approval-forwarding-requires-a-symbol-brand.md +27 -0
- package/knowledge/decisions/security/ai-agent-credential-requests-are-symbol-branded.md +37 -0
- package/knowledge/decisions/security/ai-agent-gate-requires-a-session-only-when-auth-is-true.md +31 -0
- package/knowledge/decisions/security/ai-agent-ownership-failures-never-echo-the-resource.md +23 -0
- package/knowledge/decisions/security/ai-agent-resume-re-runs-the-authorization-gate.md +22 -0
- package/knowledge/decisions/security/ai-agent-sessionless-deployments-have-no-thread-ownership.md +39 -0
- package/knowledge/decisions/security/ai-agent-thread-ownership-composes-the-session-principal.md +30 -0
- package/knowledge/decisions/security/ai-agent-tool-filtering-reads-the-live-function-config.md +24 -0
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +30 -0
- package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +49 -0
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +76 -0
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +39 -0
- package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +37 -0
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +31 -0
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +30 -0
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +40 -0
- package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +33 -0
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +46 -0
- package/knowledge/decisions/security/index.md +55 -0
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +27 -0
- package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +40 -0
- package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +31 -0
- package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md +26 -0
- package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +69 -0
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +39 -0
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +34 -0
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +49 -0
- package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +30 -0
- package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +28 -0
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +34 -0
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +37 -0
- package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +25 -0
- package/knowledge/decisions/security/workflow-actor-steps-always-use-the-real-transport.md +34 -0
- package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +40 -0
- package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md +32 -0
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +31 -0
- package/knowledge/index.md +24 -0
- package/knowledge/questions/index.md +15 -0
- package/package.json +4 -1
- package/run-tests.sh +0 -0
- package/src/crypto-utils.test.ts +460 -19
- package/src/crypto-utils.ts +283 -55
- package/src/data-classification.ts +1 -7
- package/src/dev/hot-reload.test.ts +0 -4
- package/src/dev/hot-reload.ts +11 -30
- package/src/dev/module-runner.ts +7 -32
- package/src/dev/reload-meta.ts +9 -29
- package/src/errors/error-handler.ts +20 -35
- package/src/errors/error.test.ts +30 -1
- package/src/errors/errors.ts +73 -157
- package/src/function/abort-scope.test.ts +97 -0
- package/src/function/abort-scope.ts +80 -0
- package/src/function/function-runner.test.ts +0 -7
- package/src/function/function-runner.ts +62 -32
- package/src/function/functions.types.ts +56 -136
- package/src/function/list.types.test.ts +3 -25
- package/src/function/list.types.ts +12 -62
- package/src/handle-error.ts +0 -18
- package/src/index.ts +60 -1
- package/src/middleware/auth-apikey.test.ts +0 -1
- package/src/middleware/auth-apikey.ts +0 -17
- package/src/middleware/auth-bearer.test.ts +0 -3
- package/src/middleware/auth-bearer.ts +3 -40
- package/src/middleware/auth-cookie.test.ts +0 -6
- package/src/middleware/auth-cookie.ts +2 -26
- package/src/middleware/cors.test.ts +34 -0
- package/src/middleware/cors.ts +12 -33
- package/src/middleware/remote-auth.test.ts +24 -9
- package/src/middleware/remote-auth.ts +10 -2
- package/src/middleware/telemetry.ts +2 -31
- package/src/middleware-runner.test.ts +0 -2
- package/src/middleware-runner.ts +5 -74
- package/src/permissions.test.ts +30 -0
- package/src/permissions.ts +24 -74
- package/src/pikku-request.ts +0 -6
- package/src/pikku-state.ts +2 -30
- package/src/production-barrels-stay-lean.test.ts +110 -0
- package/src/remote.test.ts +172 -0
- package/src/remote.ts +17 -7
- package/src/schema.ts +5 -39
- package/src/scopes.ts +7 -48
- package/src/services/ai-agent-runner-service.ts +20 -0
- package/src/services/ai-embedding-service.ts +3 -25
- package/src/services/audit-service.ts +1 -2
- package/src/services/content-service.ts +1 -46
- package/src/services/credential-service.ts +3 -40
- package/src/services/credential-wire-service.test.ts +0 -2
- package/src/services/deployment-service.ts +3 -9
- package/src/services/gateway-service.ts +0 -15
- package/src/services/{http-scenario-actors-converse.test.ts → http-personas-converse.test.ts} +38 -9
- package/src/services/{http-scenario-actors.test.ts → http-personas.test.ts} +39 -22
- package/src/services/{http-scenario-actors.ts → http-personas.ts} +85 -45
- package/src/services/in-memory-queue-service.ts +1 -15
- package/src/services/in-memory-trigger-service.ts +1 -18
- package/src/services/in-memory-workflow-service.test.ts +0 -13
- package/src/services/in-memory-workflow-service.ts +4 -38
- package/src/services/index.ts +20 -15
- package/src/services/istanbul-coverage-service.ts +2 -8
- package/src/services/jwt-service.ts +1 -16
- package/src/services/local-content.test.ts +159 -27
- package/src/services/local-content.ts +55 -23
- package/src/services/local-gateway-service.ts +2 -17
- package/src/services/local-secrets.ts +0 -4
- package/src/services/logger-console.test.ts +0 -1
- package/src/services/logger-console.ts +3 -7
- package/src/services/logger.ts +2 -37
- package/src/services/meta-service.test.ts +1 -5
- package/src/services/meta-service.ts +41 -61
- package/src/services/{scenario-actors-service.ts → personas-service.ts} +48 -43
- package/src/services/pikku-user-id.ts +0 -4
- package/src/services/queue-webhook-service.ts +9 -41
- package/src/services/scheduler-service.ts +1 -50
- package/src/services/schema-service.ts +1 -24
- package/src/services/scope-service.ts +50 -34
- package/src/services/scoped-secret-service.ts +0 -4
- package/src/services/secret-host-binding.test.ts +138 -0
- package/src/services/secret-host-binding.ts +51 -0
- package/src/services/secret-service.ts +5 -33
- package/src/services/secretless.test.ts +54 -0
- package/src/services/secretless.ts +29 -0
- package/src/services/stub-tracker.ts +8 -18
- package/src/services/system-role-guard.test.ts +93 -0
- package/src/services/system-role-guard.ts +71 -0
- package/src/services/trigger-service.ts +0 -12
- package/src/services/typed-secret-service.ts +1 -7
- package/src/services/v8-coverage-service.ts +3 -6
- package/src/services/variables-service.ts +1 -8
- package/src/services/webhook-service.ts +19 -63
- package/src/services/workflow-service.ts +3 -20
- package/src/testing/service-tests.ts +0 -26
- package/src/time-utils.ts +1 -19
- package/src/types/core.types.ts +116 -229
- package/src/types/state.types.ts +7 -9
- package/src/utils/hmac.ts +4 -10
- package/src/utils/safe-fetch.ts +13 -54
- package/src/utils.test.ts +11 -2
- package/src/utils.ts +6 -15
- package/src/wirings/actor-flow/actor-flow.types.ts +1 -34
- package/src/wirings/actor-flow/index.ts +0 -9
- package/src/wirings/actor-flow/run-conversation.test.ts +11 -6
- package/src/wirings/actor-flow/run-conversation.ts +19 -12
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +75 -10
- package/src/wirings/ai-agent/ai-agent-agui.ts +36 -17
- package/src/wirings/ai-agent/ai-agent-helpers.ts +20 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.test.ts +842 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +399 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-model-config.ts +1 -9
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +202 -31
- package/src/wirings/ai-agent/ai-agent-prepare.ts +82 -138
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +191 -6
- package/src/wirings/ai-agent/ai-agent-registry.ts +18 -1
- package/src/wirings/ai-agent/ai-agent-resume-authorization.test.ts +0 -2
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +11 -9
- package/src/wirings/ai-agent/ai-agent-runner.ts +67 -33
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +205 -103
- package/src/wirings/ai-agent/ai-agent-stream.ts +192 -75
- package/src/wirings/ai-agent/ai-agent-thread-ownership.test.ts +301 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +85 -4
- package/src/wirings/ai-agent/index.ts +35 -3
- package/src/wirings/ai-agent/voice-input.test.ts +72 -7
- package/src/wirings/ai-agent/voice-input.ts +48 -3
- package/src/wirings/ai-agent/voice-output.test.ts +422 -0
- package/src/wirings/ai-agent/voice-output.ts +216 -56
- package/src/wirings/channel/channel-common.ts +15 -20
- package/src/wirings/channel/channel-handler.test.ts +50 -0
- package/src/wirings/channel/channel-handler.ts +32 -11
- package/src/wirings/channel/channel-host-rpc.test.ts +150 -0
- package/src/wirings/channel/channel-host-rpc.ts +69 -0
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -1
- package/src/wirings/channel/channel-middleware-runner.ts +0 -12
- package/src/wirings/channel/channel-rpc-registry.ts +116 -0
- package/src/wirings/channel/channel-rpc-responder.ts +117 -0
- package/src/wirings/channel/channel-rpc-service.ts +146 -0
- package/src/wirings/channel/channel-rpc-validators.ts +65 -0
- package/src/wirings/channel/channel-rpc.test.ts +820 -0
- package/src/wirings/channel/channel-rpc.ts +5 -0
- package/src/wirings/channel/channel-rpc.types.ts +150 -0
- package/src/wirings/channel/channel-runner.ts +0 -14
- package/src/wirings/channel/channel-store.ts +0 -10
- package/src/wirings/channel/channel.types.ts +19 -8
- package/src/wirings/channel/define-channel-routes.ts +0 -20
- package/src/wirings/channel/eventhub-service.ts +0 -18
- package/src/wirings/channel/index.ts +35 -0
- package/src/wirings/channel/local/local-channel-handler.ts +3 -1
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -10
- package/src/wirings/channel/local/local-channel-runner.ts +3 -1
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -13
- package/src/wirings/channel/local/local-eventhub-service.ts +2 -37
- package/src/wirings/channel/log-channels.ts +0 -4
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +83 -2
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +7 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +2 -5
- package/src/wirings/cli/channel/cli-approval.test.ts +177 -0
- package/src/wirings/cli/channel/cli-approval.ts +135 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -26
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +169 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +59 -16
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +480 -0
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +155 -0
- package/src/wirings/cli/channel/index.ts +9 -0
- package/src/wirings/cli/cli-runner.test.ts +0 -1
- package/src/wirings/cli/cli-runner.ts +46 -88
- package/src/wirings/cli/cli.types.ts +14 -3
- package/src/wirings/cli/command-parser.test.ts +0 -4
- package/src/wirings/cli/command-parser.ts +11 -91
- package/src/wirings/cli/define-cli-commands.ts +1 -17
- package/src/wirings/credential/credential.types.ts +0 -12
- package/src/wirings/credential/{wire-credential.ts → define-credential.ts} +7 -7
- package/src/wirings/credential/index.ts +1 -1
- package/src/wirings/credential/validate-credential-definitions.ts +2 -4
- package/src/wirings/gateway/gateway-runner.test.ts +1 -21
- package/src/wirings/gateway/gateway-runner.ts +8 -110
- package/src/wirings/gateway/gateway.types.ts +7 -80
- package/src/wirings/http/http-routes.test.ts +0 -3
- package/src/wirings/http/http-routes.ts +0 -86
- package/src/wirings/http/http-runner.test.ts +0 -1
- package/src/wirings/http/http-runner.ts +8 -167
- package/src/wirings/http/http.types.ts +15 -62
- package/src/wirings/http/log-http-routes.ts +0 -4
- package/src/wirings/http/pikku-fetch-http-request.test.ts +2 -10
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -58
- package/src/wirings/http/pikku-fetch-http-response.test.ts +1 -1
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -3
- package/src/wirings/http/routers/path-to-regex.test.ts +4 -17
- package/src/wirings/http/routers/path-to-regex.ts +2 -13
- package/src/wirings/http/web-request.test.ts +33 -2
- package/src/wirings/http/web-request.ts +30 -17
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -1
- package/src/wirings/mcp/mcp-runner.ts +2 -17
- package/src/wirings/mcp/mcp.types.ts +7 -42
- package/src/wirings/oauth2/oauth2.types.ts +0 -30
- package/src/wirings/persona/define-personas.ts +29 -0
- package/src/wirings/persona/index.ts +62 -0
- package/src/wirings/persona/persona-email.ts +87 -0
- package/src/wirings/persona/persona-environments.test.ts +183 -0
- package/src/wirings/persona/persona-environments.ts +138 -0
- package/src/wirings/persona/persona-mailbox.ts +156 -0
- package/src/wirings/persona/persona.test.ts +220 -0
- package/src/wirings/persona/persona.types.ts +131 -0
- package/src/wirings/persona/validate-personas.ts +133 -0
- package/src/wirings/queue/index.ts +13 -3
- package/src/wirings/queue/queue-identity.test.ts +453 -0
- package/src/wirings/queue/queue-identity.ts +173 -0
- package/src/wirings/queue/queue-runner.ts +12 -31
- package/src/wirings/queue/queue.types.ts +19 -89
- package/src/wirings/queue/register-queue-helper.ts +0 -14
- package/src/wirings/queue/signed-queue-service.ts +59 -0
- package/src/wirings/queue/validate-worker-config.ts +2 -28
- package/src/wirings/role/define-system-role.ts +33 -0
- package/src/wirings/role/index.ts +13 -0
- package/src/wirings/role/role.test.ts +104 -0
- package/src/wirings/role/role.types.ts +47 -0
- package/src/wirings/role/validate-role-definitions.ts +93 -0
- package/src/wirings/rpc/addon-auth-tags.test.ts +223 -0
- package/src/wirings/rpc/addon-runner.ts +0 -56
- package/src/wirings/rpc/addon-scopes.test.ts +225 -0
- package/src/wirings/rpc/remote-addon-auth.ts +1 -13
- package/src/wirings/rpc/rpc-runner.test.ts +186 -2
- package/src/wirings/rpc/rpc-runner.ts +145 -127
- package/src/wirings/rpc/rpc-types.ts +11 -6
- package/src/wirings/rpc/wire-addon.test.ts +43 -1
- package/src/wirings/rpc/wire-addon.ts +99 -0
- package/src/wirings/rpc/wire-remote-addon.ts +9 -29
- package/src/wirings/scheduler/log-schedulers.ts +0 -4
- package/src/wirings/scheduler/scheduler-runner.test.ts +1 -8
- package/src/wirings/scheduler/scheduler-runner.ts +0 -2
- package/src/wirings/scheduler/scheduler.types.ts +1 -14
- package/src/wirings/scope/{wire-scope.ts → define-scope.ts} +5 -6
- package/src/wirings/scope/index.ts +1 -1
- package/src/wirings/scope/scope.test.ts +1 -2
- package/src/wirings/scope/scope.types.ts +7 -9
- package/src/wirings/scope/validate-scope-definitions.ts +3 -21
- package/src/wirings/secret/index.ts +1 -1
- package/src/wirings/secret/secret.types.ts +19 -15
- package/src/wirings/secret/validate-secret-definitions.ts +2 -4
- package/src/wirings/trigger/trigger-runner.ts +1 -27
- package/src/wirings/trigger/trigger.types.ts +1 -82
- package/src/wirings/variable/index.ts +1 -1
- package/src/wirings/variable/validate-variable-definitions.ts +2 -4
- package/src/wirings/variable/variable.types.ts +1 -13
- package/src/wirings/virtual-user/index.ts +76 -0
- package/src/wirings/virtual-user/run-virtual-user.test.ts +765 -0
- package/src/wirings/virtual-user/run-virtual-user.ts +671 -0
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +65 -0
- package/src/wirings/virtual-user/virtual-user-agents.ts +57 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +215 -0
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +184 -0
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +398 -0
- package/src/wirings/virtual-user/virtual-user-derive.ts +173 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +63 -0
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +213 -0
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +208 -0
- package/src/wirings/virtual-user/virtual-user-intents.ts +185 -0
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +72 -0
- package/src/wirings/virtual-user/virtual-user-rng.ts +50 -0
- package/src/wirings/virtual-user/virtual-user-target.ts +47 -0
- package/src/wirings/virtual-user/virtual-user.types.ts +219 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +5 -4
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -4
- package/src/wirings/workflow/feature.ts +0 -19
- package/src/wirings/workflow/graph/graph-node.ts +0 -136
- package/src/wirings/workflow/graph/graph-runner.test.ts +20 -19
- package/src/wirings/workflow/graph/graph-runner.ts +6 -41
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -4
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -58
- package/src/wirings/workflow/index.ts +10 -44
- package/src/wirings/workflow/pikku-scenario-service.ts +235 -52
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -39
- package/src/wirings/workflow/pikku-workflow-service.ts +77 -674
- package/src/wirings/workflow/run-timeline.test.ts +7 -19
- package/src/wirings/workflow/run-timeline.ts +0 -56
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -1
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -25
- package/src/wirings/workflow/scenario-expectations.test.ts +2 -7
- package/src/wirings/workflow/scenario-hooks.test.ts +2 -7
- package/src/wirings/workflow/scenario-poll.test.ts +0 -2
- package/src/wirings/workflow/scenario-poll.ts +0 -15
- package/src/wirings/workflow/scenario-prose.ts +0 -28
- package/src/wirings/workflow/scenario-service.test.ts +2 -9
- package/src/wirings/workflow/scenario-step-guards.ts +1 -14
- package/src/wirings/workflow/scenario-step.test.ts +159 -14
- package/src/wirings/workflow/scenario-step.types.ts +82 -6
- package/src/wirings/workflow/scenario-surface.test.ts +145 -0
- package/src/wirings/workflow/scenario-surface.ts +71 -0
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +14 -15
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -4
- package/src/wirings/workflow/workflow-inline-authority.test.ts +169 -0
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -2
- package/src/wirings/workflow/workflow-invocation-id.ts +2 -22
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -7
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -9
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -21
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +8 -7
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -5
- package/src/wirings/workflow/workflow-run-context.test.ts +5 -10
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -5
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +19 -4
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -7
- package/src/wirings/workflow/workflow.types.ts +0 -203
- package/tsconfig.tsbuildinfo +1 -1
- package/src/middleware/timeout.ts +0 -22
- package/src/pikku-response.ts +0 -5
- package/src/wirings/mcp/mcp-endpoint-registry.test.d.ts +0 -1
- package/src/wirings/workflow/dsl/index.ts +0 -36
- package/src/wirings/workflow/graph/index.ts +0 -15
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: The console addon's privileged functions gate themselves
|
|
4
|
+
description: Thread listing is owner-scoped unless the caller holds admin, and addon installation requires an admin session, rather than trusting the host to register a global permission
|
|
5
|
+
tags: addon, ai-agent, rpc
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# The console addon's privileged functions gate themselves
|
|
9
|
+
|
|
10
|
+
`@pikku/addon-console` is wired into the consuming application by the generated
|
|
11
|
+
`console.gen.ts` that `scaffold.console` produces — `wireAddon({ name:
|
|
12
|
+
'console', package: '@pikku/addon-console' })`. That file is compiled into the
|
|
13
|
+
app and ships wherever the app ships, so every `expose: true` console function
|
|
14
|
+
is reachable over the app's own `POST /rpc/:rpcName` route in production, not
|
|
15
|
+
only from `pikku dev`. `pikku serve --console` binds `127.0.0.1` and adds a
|
|
16
|
+
static SPA mount; it wires no functions. The console UI being a development tool
|
|
17
|
+
is a convention about who opens the browser tab; it is not a deployment
|
|
18
|
+
boundary.
|
|
19
|
+
|
|
20
|
+
The transport in front of that route is configured separately from the console:
|
|
21
|
+
`scaffold.rpc` defaults to `"no-auth"` in `templates/functions/pikku.config.json`
|
|
22
|
+
and in `e2e/pikku.config.json`, which generates `wireHTTP({ route:
|
|
23
|
+
'/rpc/:rpcName', auth: false })`. `scaffold.console: "auth"` does not raise it,
|
|
24
|
+
and the generated `wireAddon` carries no `auth` key, so `addonConfig.auth` is
|
|
25
|
+
undefined at the addon boundary too. The remaining session check is the function
|
|
26
|
+
runner's, which for a `pikkuSessionlessFunc` fires only when the function itself
|
|
27
|
+
sets `auth: true`.
|
|
28
|
+
|
|
29
|
+
The addon's original authorization story was a single package-scoped
|
|
30
|
+
`addGlobalPermission([isAdmin], '@pikku/addon-console')` that the *host*
|
|
31
|
+
registered, which `runPermissions` resolves in the callee's package namespace and
|
|
32
|
+
applies to every function at once. Nothing generated that call — the scaffold
|
|
33
|
+
emitted only a comment recommending it, and `resolveGlobalPermissions` returns an
|
|
34
|
+
empty list when the host registered none, which `runPermissions` treats as
|
|
35
|
+
allow. An app that followed the scaffold and stopped there had a fully open
|
|
36
|
+
console surface. The scaffold now emits `wireAddon({ …, scopes: ['admin'] })`
|
|
37
|
+
instead, which the function runner enforces on every function in the package.
|
|
38
|
+
|
|
39
|
+
The two functions whose failure mode is worst still do not depend on any
|
|
40
|
+
package-wide gate:
|
|
41
|
+
|
|
42
|
+
`getAgentThreads` derives `owners` from the session with core's
|
|
43
|
+
`threadOwnerConstraint` and passes it on every call, omitting it only when the
|
|
44
|
+
session holds the `admin` scope root — listing every thread is the legitimate
|
|
45
|
+
purpose of an admin console, and `hasScopes` accepts the umbrella grant and its
|
|
46
|
+
wildcards. The `owners` value is never read from input, so a caller cannot name
|
|
47
|
+
someone else's principal. A session with no principal yields `[]`, which every
|
|
48
|
+
storage backend already treats as no rows.
|
|
49
|
+
|
|
50
|
+
`installAddon` and `installOpenapiAddon` run a package manager against an
|
|
51
|
+
attacker-chosen package name and write a wiring file into the project. They were
|
|
52
|
+
`pikkuSessionlessFunc` with `auth: false`, which — behind a `no-auth` RPC route
|
|
53
|
+
and with no host global permission — meant an unauthenticated POST reached
|
|
54
|
+
`execFileSync`. They now declare `auth: true` and `scopes: ['admin']`, enforced
|
|
55
|
+
by `verifyScopes` in the function runner before the body runs. Both gates are
|
|
56
|
+
checked whether or not the host registered a global permission, and whether or
|
|
57
|
+
not the addon itself was wired with scopes — an app that hand-wires
|
|
58
|
+
`wireAddon({ name: 'console', package: '@pikku/addon-console' })` without them
|
|
59
|
+
still cannot reach `execFileSync` unauthenticated.
|
|
60
|
+
|
|
61
|
+
`admin` is spelled as a literal here because it is a scope id in the addon's own
|
|
62
|
+
`wireScope` tree, the same tree the `pikku:scopes:*` gates on the scope-admin
|
|
63
|
+
functions come from.
|
|
64
|
+
|
|
65
|
+
**What this rules out:** treating the host's `addGlobalPermission` as the only
|
|
66
|
+
gate in front of an operation that installs code or reads another principal's
|
|
67
|
+
data; deriving `owners` from request input; returning `owners: undefined` for a
|
|
68
|
+
caller who simply has no principal; and gating these functions on a
|
|
69
|
+
`metaService.basePath` check — that is a filesystem-availability test which the
|
|
70
|
+
generated `PikkuMetaService` satisfies in any unbundled Node deployment, not an
|
|
71
|
+
is-this-localhost test.
|
|
72
|
+
|
|
73
|
+
See [[addon-scopes-are-resolved-where-the-function-runs]],
|
|
74
|
+
[[an-empty-owners-constraint-matches-nothing]],
|
|
75
|
+
[[ai-agent-sessionless-deployments-have-no-thread-ownership]] and
|
|
76
|
+
[[global-permissions-and-function-permissions-are-independent-gates]].
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Core's SSRF guard matches host literals because edge runtimes have no DNS
|
|
4
|
+
description: safeFetch rejects internal address literals and re-validates every redirect hop; it cannot stop DNS rebinding
|
|
5
|
+
tags: core
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Core's SSRF guard matches host literals because edge runtimes have no DNS
|
|
9
|
+
|
|
10
|
+
`packages/core/src/utils/safe-fetch.ts` guards every outbound fetch that core
|
|
11
|
+
makes on a caller-supplied URL. `@pikku/core` runs in edge runtimes (Cloudflare
|
|
12
|
+
Workers) that have no Node `dns` module, so the guard cannot resolve a hostname
|
|
13
|
+
and inspect the resulting address. It instead rejects address *literals* that are
|
|
14
|
+
obviously internal — loopback, `10/8`, `172.16/12`, `192.168/16`, `0.0.0.0/8`,
|
|
15
|
+
link-local `169.254/16` (the cloud metadata endpoint), IPv6 `::`, `::1`,
|
|
16
|
+
`fe80::/10` and `fc00::/7` — plus the alias forms that reach the same targets:
|
|
17
|
+
trailing-dot FQDNs, the reserved `*.localhost` names, IPv4-mapped IPv6, and the
|
|
18
|
+
`inet_aton`-style octal/hex/32-bit-integer encodings that `fetch` and `undici`
|
|
19
|
+
accept (`0177.0.0.1`, `0x7f000001`, `2130706433`).
|
|
20
|
+
|
|
21
|
+
Literal matching alone is not enough, because the classic bypass is a public URL
|
|
22
|
+
that 302s to `169.254.169.254`. So `safeFetch` sets `redirect: 'manual'` and
|
|
23
|
+
re-validates every hop through `assertFetchableUrl` before following it, follows
|
|
24
|
+
only the statuses in `REDIRECT_STATUSES` (301/302/303/307/308 — `300`, `304`,
|
|
25
|
+
`305` and `306` are returned to the caller as-is), applies the WHATWG
|
|
26
|
+
method/body transform per hop, cancels each intermediate response body, and
|
|
27
|
+
strips `Authorization` and `Cookie` whenever a hop crosses origin. What it
|
|
28
|
+
deliberately does **not** cover is DNS rebinding: a public hostname that itself
|
|
29
|
+
resolves to a private IP passes, because catching that needs resolution the
|
|
30
|
+
runtime does not offer.
|
|
31
|
+
|
|
32
|
+
**What this rules out:** "simplifying" `isPrivateHost` down to a `localhost` /
|
|
33
|
+
`127.0.0.1` string check — the octal, hex, integer and IPv4-mapped-IPv6 branches
|
|
34
|
+
are each a live bypass, not defensive noise. It equally rules out replacing the
|
|
35
|
+
manual redirect loop with `redirect: 'follow'` to shorten the function: the
|
|
36
|
+
platform would then follow a `Location` into the internal network with no
|
|
37
|
+
validation and no credential stripping, which is the entire attack this file
|
|
38
|
+
exists to block. Do not add DNS-based checks either without gating them —
|
|
39
|
+
importing `node:dns` breaks the Workers build.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Core secrets are encrypted with a per-secret DEK wrapped by a KEK
|
|
4
|
+
description: Envelope encryption keeps ciphertext untouched during key rotation, at the cost of storing two blobs per secret
|
|
5
|
+
tags: core
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Core secrets are encrypted with a per-secret DEK wrapped by a KEK
|
|
9
|
+
|
|
10
|
+
`packages/core/src/crypto-utils.ts` exposes two layers. `encryptJSON` /
|
|
11
|
+
`decryptJSON` are the direct layer: a passphrase is hashed to an AES-GCM key and
|
|
12
|
+
the output is `base64url(iv || ciphertext)` with a 12-byte IV. On top of that sit
|
|
13
|
+
`envelopeEncrypt`, `envelopeDecrypt` and `envelopeRewrap`, which give every stored
|
|
14
|
+
secret its own random 32-byte DEK (data encryption key). The secret is encrypted
|
|
15
|
+
with the DEK; the DEK is then encrypted with a KEK (key encryption key) — in
|
|
16
|
+
practice an environment variable or platform secret — and the pair
|
|
17
|
+
`{ ciphertext, wrappedDEK }` is what gets persisted.
|
|
18
|
+
|
|
19
|
+
The split exists so that rotating the KEK is cheap and non-destructive.
|
|
20
|
+
`envelopeRewrap` unwraps the DEK under the old KEK and re-wraps it under the new
|
|
21
|
+
one; the ciphertext of the secret itself is never read, never re-encrypted, and
|
|
22
|
+
never re-written. A store holding a million secrets rotates by touching a million
|
|
23
|
+
short blobs rather than re-encrypting every payload, and a rotation that fails
|
|
24
|
+
part-way leaves both halves independently decryptable by whichever KEK still
|
|
25
|
+
wraps them. The `keyVersion` / `previousKey` / `rotateKEK` shape that
|
|
26
|
+
`ServiceTestConfig` requires of every `SecretService` and `CredentialService`
|
|
27
|
+
implementation (`packages/core/src/testing/service-tests.ts`) is the storage-side
|
|
28
|
+
contract that falls out of this.
|
|
29
|
+
|
|
30
|
+
**What this rules out:** collapsing the envelope back to a single
|
|
31
|
+
`encryptJSON(kek, secret)` call, or deriving the DEK from the KEK rather than
|
|
32
|
+
generating it randomly. Either change makes the ciphertext depend on the KEK, so
|
|
33
|
+
rotation becomes a full re-encrypt of every stored secret and `envelopeRewrap`
|
|
34
|
+
stops being correct. It also rules out changing the `iv || ciphertext` layout or
|
|
35
|
+
the 12-byte IV length in either layer — the length is hard-coded on the decrypt
|
|
36
|
+
side (`data.slice(0, 12)`, minimum length 13), so any change silently fails to
|
|
37
|
+
read every previously stored value.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Gateway handlers run through the function runner gate
|
|
4
|
+
description: A gateway's handler is registered as a real pikku function and invoked via runPikkuFunc, because calling it directly skips auth, scopes and permissions
|
|
5
|
+
tags: gateway
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Gateway handlers run through the function runner gate
|
|
9
|
+
|
|
10
|
+
`registerGatewayHandler` in
|
|
11
|
+
`packages/core/src/wirings/gateway/gateway-runner.ts` registers `config.func`
|
|
12
|
+
under a synthetic function id (`gateway__<name>__handler`) and every transport —
|
|
13
|
+
webhook POST, websocket message, listener callback — invokes it via
|
|
14
|
+
`runPikkuFunc`. This looks like an unnecessary indirection: the handler is right
|
|
15
|
+
there in the config and could be awaited directly. It cannot. The function
|
|
16
|
+
runner's gate is the only thing that evaluates a function's declared `auth`,
|
|
17
|
+
`scopes` and `permissions`; a direct call runs the handler with none of them
|
|
18
|
+
checked.
|
|
19
|
+
|
|
20
|
+
The handler is registered with `sessionless: true`. Gateway inbound traffic is
|
|
21
|
+
authenticated by the platform adapter (webhook signature verification, platform
|
|
22
|
+
tokens), not by a user session, so defaulting to session-required would reject
|
|
23
|
+
every legitimate webhook. `CoreGateway.auth` and the handler's own `auth: true`
|
|
24
|
+
opt back into requiring a session. `scopes` and `permissions` are enforced
|
|
25
|
+
whenever declared, session or not.
|
|
26
|
+
|
|
27
|
+
**What this rules out:** invoking `config.func` directly from any gateway
|
|
28
|
+
transport as an optimisation, and "simplifying" the synthetic function
|
|
29
|
+
registration away. It also rules out flipping the sessionless default to
|
|
30
|
+
session-required as a hardening measure — that breaks every webhook rather than
|
|
31
|
+
securing it; require auth per gateway via `CoreGateway.auth` instead.
|
package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Gateway middleware sessions must be bridged onto the wire
|
|
4
|
+
description: Gateway middleware calling wire.setSession writes to a session service the handler's invocation never reads, so the session is copied onto wire.session before the gate runs
|
|
5
|
+
tags: gateway
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Gateway middleware sessions must be bridged onto the wire
|
|
9
|
+
|
|
10
|
+
`bridgeMiddlewareSession` in
|
|
11
|
+
`packages/core/src/wirings/gateway/gateway-runner.ts` runs immediately before
|
|
12
|
+
every `runPikkuFunc` call in the gateway runner, copying `await wire.getSession()`
|
|
13
|
+
onto `wire.session` when the latter is unset. Gateway middleware is the only
|
|
14
|
+
place a webhook can acquire a session at all — typically by mapping a verified
|
|
15
|
+
platform sender id to a user. Middleware that assigns `wire.session` directly
|
|
16
|
+
needs no help, but the idiomatic `wire.setSession()` writes into the *enclosing
|
|
17
|
+
wiring's* session service, and the handler's own invocation does not read that
|
|
18
|
+
service.
|
|
19
|
+
|
|
20
|
+
Without the bridge, the failure mode is silent and it fails open in the way that
|
|
21
|
+
matters: `auth` and `scopes` on the handler see no session, so a gateway whose
|
|
22
|
+
middleware successfully authenticated the sender still evaluates its gate as
|
|
23
|
+
unauthenticated. Nothing throws; the session is simply invisible.
|
|
24
|
+
|
|
25
|
+
**What this rules out:** removing the `bridgeMiddlewareSession` call from any of
|
|
26
|
+
the three transports (webhook POST, websocket message, listener) on the grounds
|
|
27
|
+
that middleware "already set the session", and adding a fourth gateway transport
|
|
28
|
+
that calls `runPikkuFunc` without bridging first. It also rules out relying on
|
|
29
|
+
`wire.setSession()` alone anywhere the callee is a separately-registered pikku
|
|
30
|
+
function.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Global permissions and function permissions are independent gates
|
|
4
|
+
description: Globals AND together and can only narrow access; a function's own group ORs internally and is never satisfied by a global
|
|
5
|
+
tags: permissions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Global permissions and function permissions are independent gates
|
|
9
|
+
|
|
10
|
+
`runPermissions` in `packages/core/src/permissions.ts` runs two gates and both
|
|
11
|
+
must pass. Every requirement registered through `addGlobalPermission` is checked
|
|
12
|
+
in turn and any failure throws `ForbiddenError` — globals AND, so a global can
|
|
13
|
+
only ever narrow access, never grant it. The function's own `permissions` group
|
|
14
|
+
is then evaluated by `verifyPermissions`, which ORs the group's branches and ANDs
|
|
15
|
+
the entries within a branch: the group passes if at least one branch is
|
|
16
|
+
satisfied.
|
|
17
|
+
|
|
18
|
+
That OR inside `verifyPermissions` is the only OR in authorization. Everything
|
|
19
|
+
cross-cutting — globals, scopes, auth — ANDs. A function's own group is the sole
|
|
20
|
+
place where "owner OR admin"-style alternatives belong. Crucially, a global that
|
|
21
|
+
passed contributes nothing to the function gate: a broad `signedIn` global cannot
|
|
22
|
+
satisfy an admin-only function's requirements, because the two gates are
|
|
23
|
+
evaluated independently against separate inputs. An empty or absent group passes,
|
|
24
|
+
so declaring no permissions means "globals only", not "deny".
|
|
25
|
+
|
|
26
|
+
Globals are bucketed per package, and a function reads *both* the root bucket and
|
|
27
|
+
its own package's — root first. The generated `addGlobalPermission` wrapper takes
|
|
28
|
+
no package argument, so an application's rules only ever land in the root bucket;
|
|
29
|
+
resolving the package bucket alone meant a host rule like "every request needs a
|
|
30
|
+
signed-in user" silently stopped at the addon boundary, while the bucket an
|
|
31
|
+
addon's functions actually read was one no host could write to. Unioning is safe
|
|
32
|
+
in a way that nothing else here would be: globals AND, so adding the root ones
|
|
33
|
+
can only tighten. Package buckets stay one-way — a package's globals never apply
|
|
34
|
+
to root functions, or an installed addon could gate the whole application.
|
|
35
|
+
|
|
36
|
+
**What this rules out:** short-circuiting the function gate when the globals
|
|
37
|
+
passed, merging the global requirements into the function group (which would turn
|
|
38
|
+
AND into OR and let any global satisfy any function), and giving
|
|
39
|
+
`verifyPermissions` AND-across-branches semantics to "make it consistent" with
|
|
40
|
+
the global gate.
|
package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: HTTP error detail is withheld from clients in production
|
|
4
|
+
description: 5xx bodies carry only a trace id in production; exposeErrors can widen that in development but never in production
|
|
5
|
+
tags: http
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# HTTP error detail is withheld from clients in production
|
|
9
|
+
|
|
10
|
+
`fetchData` in `packages/core/src/wirings/http/http-runner.ts` defaults
|
|
11
|
+
`exposeErrors` to `!isProduction()`, and `handleHTTPError` in
|
|
12
|
+
`packages/core/src/handle-error.ts` computes `clientFacing` as
|
|
13
|
+
`errorResponse.status < 500 || (exposeErrors && !isProduction())`. Registered
|
|
14
|
+
errors below 500 always return their own message and `payload`, because they
|
|
15
|
+
describe something the caller did. Anything 500 and above returns only the
|
|
16
|
+
registered generic message and the `errorId` trace id; the original `e.message`,
|
|
17
|
+
`e.stack` and `payload` are dropped. The unregistered-error path is stricter
|
|
18
|
+
still — it emits `{ errorId }` alone and attaches `message`/`stack` only when
|
|
19
|
+
`exposeErrors && !isProduction()`.
|
|
20
|
+
|
|
21
|
+
Unexpected 5xx messages are the ones that leak: driver errors carrying
|
|
22
|
+
connection strings, ORM errors quoting rows, assertion text naming internal
|
|
23
|
+
services. The trace id is the deliberate substitute — the operator correlates it
|
|
24
|
+
with the logged error, which does carry the full message.
|
|
25
|
+
|
|
26
|
+
Note the double gate. `exposeErrors` is re-checked against `isProduction()`
|
|
27
|
+
inside `handleHTTPError` even though its default already accounts for it, so a
|
|
28
|
+
runtime that passes `exposeErrors: true` explicitly still cannot open up a
|
|
29
|
+
production deployment.
|
|
30
|
+
|
|
31
|
+
**What this rules out:** collapsing the two `isProduction()` checks into the
|
|
32
|
+
default value, letting a caller-supplied `exposeErrors` win in production, and
|
|
33
|
+
"improving" the 5xx body by echoing `e.message` unconditionally.
|
package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: HTTP request bodies are bounded before they are buffered
|
|
4
|
+
description: Content-Length is rejected up front and the stream is measured as it arrives, because that header is optional and attacker-controlled
|
|
5
|
+
tags: http
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# HTTP request bodies are bounded before they are buffered
|
|
9
|
+
|
|
10
|
+
`PikkuFetchHTTPRequest.#readBoundedBuffer` in
|
|
11
|
+
`packages/core/src/wirings/http/pikku-fetch-http-request.ts` enforces
|
|
12
|
+
`maxBodySize` twice. A declared `content-length` above the limit is rejected
|
|
13
|
+
before a single byte is transferred, which is the cheap path. The body stream is
|
|
14
|
+
then measured chunk by chunk anyway, cancelling the reader and throwing
|
|
15
|
+
`PayloadTooLargeError` the moment the running total crosses the limit — because
|
|
16
|
+
`content-length` is optional (chunked encoding omits it) and, when present, is
|
|
17
|
+
whatever the client chose to write. Trusting it alone would let a request declare
|
|
18
|
+
1 KB and stream a gigabyte.
|
|
19
|
+
|
|
20
|
+
`DEFAULT_MAX_BODY_SIZE` is 10 MB: ample for JSON APIs and ordinary uploads while
|
|
21
|
+
keeping one request's memory footprint bounded, since the whole body is
|
|
22
|
+
materialised in memory.
|
|
23
|
+
|
|
24
|
+
Every adapter enforces the same limit and the same `PayloadTooLargeError`, but
|
|
25
|
+
they divide into two kinds, and the difference is what can actually be
|
|
26
|
+
prevented rather than merely reported:
|
|
27
|
+
|
|
28
|
+
- **Prevention.** uWebSockets drives `res.onData` itself, so it counts bytes and
|
|
29
|
+
*drops* chunks past the limit rather than concatenating, replying 413 before
|
|
30
|
+
routing — the only point at which the buffering can be stopped, which is why
|
|
31
|
+
it deviates from the fetch adapter's 404-an-unmatched-route order. Fastify
|
|
32
|
+
delegates to native `bodyLimit`, set only when `maxBodySize` is configured so
|
|
33
|
+
the default never *loosens* fastify's stricter 1 MB. `PikkuExpressServer`
|
|
34
|
+
feeds the limit into `express.json`/`text`/`urlencoded`.
|
|
35
|
+
- **Rejection only.** `express-middleware` is handed an already-parsed body, so
|
|
36
|
+
its guard makes the rejection uniform but cannot reclaim the memory; a
|
|
37
|
+
deployment mounting it on its own app must bound its own parser. Next server
|
|
38
|
+
actions receive a decoded JS value with no wire size left to measure — the
|
|
39
|
+
limit there is `experimental.serverActions.bodySizeLimit`.
|
|
40
|
+
|
|
41
|
+
**What this rules out:** dropping the streaming measurement once the
|
|
42
|
+
`content-length` check exists; raising the default because "10 MB is small" —
|
|
43
|
+
raise it per-route via `maxBodySize` instead; reading the body with a plain
|
|
44
|
+
`request.arrayBuffer()` anywhere that bypasses this method; and adding an
|
|
45
|
+
adapter that buffers a body without a bound, on the assumption that some layer
|
|
46
|
+
downstream will catch it.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: overview
|
|
3
|
+
title: Security decisions
|
|
4
|
+
description: Who may do what in core — gates, defaults, and the failures they are chosen to cause
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Security decisions
|
|
8
|
+
|
|
9
|
+
A rule about who may do what, and which way it fails when it is unsure.
|
|
10
|
+
|
|
11
|
+
<!-- pikku:knowledge-index -->
|
|
12
|
+
- [A dropped audit write is always logged](a-dropped-audit-write-is-always-logged.md) — The no-op audit service falls back to the singleton logger when the wire carries none, so an unconfigured audit call is never silent
|
|
13
|
+
- [An actor's missing approval decision defaults to denied](actor-flow-missing-approval-decisions-default-to-denied.md) — Every pending tool call gets an explicit decision; an id the persona LLM omitted is denied, so a dropped field can never read as consent
|
|
14
|
+
- [Actor sign-in is proven by Set-Cookie, not a non-empty jar](actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md) — HttpScenarioActor tracks its own signedIn flag and requires the sign-in response itself to set a cookie, because a populated jar proves nothing
|
|
15
|
+
- [Actor sign-in only works for actor-flagged users](actor-sign-in-only-works-for-actor-flagged-users.md) — The scenario actor secret mints sessions for user rows flagged actor and nothing else, so holding it never impersonates a real user
|
|
16
|
+
- [Addon auth and tags only tighten, and resolve where the function runs](addon-auth-and-tags-only-tighten.md) — wireAddon auth and tags are applied in runPikkuFunc like scopes, but auth:false is ignored and tags resolve against the consuming app's tag groups rather than the addon package's
|
|
17
|
+
- [Addon auth and tag gates apply wherever the function runs, including inside the addon](addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md) — wireAddon's auth and tags moved from the namespaced RPC boundary into runPikkuFunc, so they also apply to direct wirings and to bare intra-addon calls
|
|
18
|
+
- [Addon scopes are resolved where the function runs](addon-scopes-are-resolved-where-the-function-runs.md) — wireAddon scopes are merged inside runPikkuFunc rather than at namespace resolution, because most wirings reach an addon function without ever resolving a namespace
|
|
19
|
+
- [Only a Symbol-branded framework result can request tool approval](ai-agent-approval-forwarding-requires-a-symbol-brand.md) — Approval markers are trusted from the APPROVAL_REQUIRED Symbol on a forwardsApproval tool, never from a JSON key an LLM could emit
|
|
20
|
+
- [Credential requests are trusted only when Symbol-branded](ai-agent-credential-requests-are-symbol-branded.md) — The string key is a wire field; the Symbol is the capability, and only core can mint it
|
|
21
|
+
- [An agent requires a session only when auth is true, but always enforces scopes and permissions](ai-agent-gate-requires-a-session-only-when-auth-is-true.md) — Agents follow pikkuSessionlessFunc semantics so crons and queue workers can run them; scopes are an AND gate checked before any permission I/O
|
|
22
|
+
- [An agent ownership failure never echoes the resource it refused](ai-agent-ownership-failures-never-echo-the-resource.md) — assertResourceOwner throws a bare ForbiddenError so the error cannot be used as an existence oracle, at the cost of thinner debugging output
|
|
23
|
+
- [Resuming a suspended agent run re-runs the agent's authorization gate](ai-agent-resume-re-runs-the-authorization-gate.md) — Ownership of the run is not enough — a grant revoked while the run was suspended must block the approval
|
|
24
|
+
- [Agent thread ownership fails closed when there is no principal](ai-agent-sessionless-deployments-have-no-thread-ownership.md) — A sessionless caller gets an ephemeral owner and reaches no stored thread, rather than reaching all of them
|
|
25
|
+
- [An agent thread key is always prefixed with the trusted principal](ai-agent-thread-ownership-composes-the-session-principal.md) — Ownership keys are composed as principal:resourceId, so a client id can sub-divide its own boundary but never widen it
|
|
26
|
+
- [Agent tool permission filtering reads the live function config, not the metadata](ai-agent-tool-filtering-reads-the-live-function-config.md) — The pikkuAuth brand survives only on live permission objects, so a metadata-driven check would silently admit every gated tool
|
|
27
|
+
- [An empty owners constraint matches nothing](an-empty-owners-constraint-matches-nothing.md) — owners is an authorization boundary, so every storage backend must treat [] as no rows rather than no filter
|
|
28
|
+
- [An exposed function with no gate is reported at codegen, not at boot](an-exposed-ungated-function-is-a-codegen-warning.md) — The check runs in the inspector where function meta and every wireAddon declaration are both in hand, because neither source alone can tell a gated function from an ungated one
|
|
29
|
+
- [The console addon's privileged functions gate themselves](console-addon-privileged-functions-gate-themselves.md) — Thread listing is owner-scoped unless the caller holds admin, and addon installation requires an admin session, rather than trusting the host to register a global permission
|
|
30
|
+
- [Core's SSRF guard matches host literals because edge runtimes have no DNS](core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md) — safeFetch rejects internal address literals and re-validates every redirect hop; it cannot stop DNS rebinding
|
|
31
|
+
- [Core secrets are encrypted with a per-secret DEK wrapped by a KEK](core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md) — Envelope encryption keeps ciphertext untouched during key rotation, at the cost of storing two blobs per secret
|
|
32
|
+
- [Gateway handlers run through the function runner gate](gateway-handlers-run-through-the-function-runner-gate.md) — A gateway's handler is registered as a real pikku function and invoked via runPikkuFunc, because calling it directly skips auth, scopes and permissions
|
|
33
|
+
- [Gateway middleware sessions must be bridged onto the wire](gateway-middleware-sessions-must-be-bridged-onto-the-wire.md) — Gateway middleware calling wire.setSession writes to a session service the handler's invocation never reads, so the session is copied onto wire.session before the gate runs
|
|
34
|
+
- [Global permissions and function permissions are independent gates](global-permissions-and-function-permissions-are-independent-gates.md) — Globals AND together and can only narrow access; a function's own group ORs internally and is never satisfied by a global
|
|
35
|
+
- [HTTP error detail is withheld from clients in production](http-error-detail-is-withheld-from-clients-in-production.md) — 5xx bodies carry only a trace id in production; exposeErrors can widen that in development but never in production
|
|
36
|
+
- [HTTP request bodies are bounded before they are buffered](http-request-bodies-are-bounded-before-they-are-buffered.md) — Content-Length is rejected up front and the stream is measured as it arrives, because that header is optional and attacker-controlled
|
|
37
|
+
- [MCP internal error details are double-gated on production](mcp-internal-error-details-are-double-gated-on-production.md) — exposeErrors is checked again against isProduction() at throw time so an explicit true cannot leak stack traces from a production build
|
|
38
|
+
- [Passphrases are stretched, key material is expanded](passphrases-are-stretched-key-material-is-expanded.md) — PBKDF2 guards low-entropy human input; HKDF derives from high-entropy secrets, and the two are never swapped
|
|
39
|
+
- [Auth filtering requires live permission functions, never their metadata](permission-auth-filtering-requires-live-permission-functions.md) — checkAuthPermissions collects pikkuAuth-branded predicates off the real config; passing metadata would let every gated tool through
|
|
40
|
+
- [Pikku carries actor scopes as data and the app grants them](pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md) — scopes and roles on a ScenarioActorConfig are transported, never applied — the app's own seed reads them back and performs the grant
|
|
41
|
+
- [Queue job identities are signed at enqueue](queue-job-identities-are-signed-at-enqueue.md) — A job's pikkuUserId is an HMAC claim bound to the queue and payload; an unverifiable claim is dropped, never trusted
|
|
42
|
+
- [Queue jobs carry the producer's pikku user id](queue-jobs-carry-the-producers-pikku-user-id.md) — A job's pikkuUserId is trusted as identity by the worker, so enqueue rights are effectively act-as-user rights
|
|
43
|
+
- [Remote addon tokens are client credentials, not mesh trust](remote-addon-tokens-are-client-credentials-not-mesh-trust.md) — wireRemoteAddon authenticates as a client to a hosted library and fails closed on an empty token; it never uses PIKKU_REMOTE_SECRET
|
|
44
|
+
- [A scaffolded surface is authenticated unless the config opts out in writing](scaffold-features-are-authenticated-unless-opted-out.md) — scaffold.<feature> became boolean | { auth, path }, where true means authenticated — going public requires typing { auth: false }, and the legacy 'auth' | 'no-auth' strings are refused rather than coerced
|
|
45
|
+
- [Scenario-step functions are never externally invocable over RPC](scenario-step-functions-are-never-externally-invocable.md) — rpcExposed requires expose and rejects scenarioStep, so test steps stay reachable only from inside a scenario run
|
|
46
|
+
- [Scope resolution happens at the session boundary and scope sync never deletes](scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md) — ScopeService is called when a session is built, never by the function runner, and syncScopes only ever adds — revoking is an explicit operation
|
|
47
|
+
- [Self-authentication is declared, not detected](self-authentication-is-declared-not-detected.md) — A function that authorizes callers in its own body says so with selfAuthenticated; codegen never tries to infer it
|
|
48
|
+
- [Signed content URLs bind the request path and verify fail-closed](signed-content-urls-bind-the-request-path.md) — A signature that only carries timestamps authorizes every asset, and a verifier with no key must refuse rather than allow
|
|
49
|
+
- [Webhook bodies are signed before they are enqueued](webhook-bodies-are-signed-before-they-are-enqueued.md) — QueueWebhookService computes the HMAC at enqueue time so the signing key never travels in the queue payload
|
|
50
|
+
- [A scenario actor step always goes over the real transport and never through internal dispatch](workflow-actor-steps-always-use-the-real-transport.md) — Internal dispatch would bypass auth middleware and permissions, turning a scenario into a green health check that proves nothing
|
|
51
|
+
- [An approval decision is stored raw and validated on replay, and an invalid one closes the gate rather than failing the run](workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md) — The schema only exists inside the workflow body, and letting an external payload fail the run would let any caller kill a workflow
|
|
52
|
+
- [A queued workflow step rehydrates its session from the persisted run wire](workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md) — The queue job payload is just `{ runId }`, so without threading `pikkuUserId` an authed step sees no session and throws
|
|
53
|
+
- [Scenario sessions are isolated per actor and reset between scenarios](workflow-scenario-sessions-are-isolated-per-actor-and-per-scenario.md) — One jar per actor keeps two personas from sharing a session; a browser reset keeps one scenario from leaving the next signed in as somebody else
|
|
54
|
+
- [A scenario step is never registered as a callable RPC and never dispatched on the queue](workflow-scenario-steps-are-never-network-invocable.md) — A step drives a browser and holds an actor's session, so exposing it as an RPC would put that reach on the network
|
|
55
|
+
<!-- /pikku:knowledge-index -->
|
package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: MCP internal error details are double-gated on production
|
|
4
|
+
description: exposeErrors is checked again against isProduction() at throw time so an explicit true cannot leak stack traces from a production build
|
|
5
|
+
tags: mcp
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MCP internal error details are double-gated on production
|
|
9
|
+
|
|
10
|
+
`RunMCPEndpointParams.exposeErrors` in
|
|
11
|
+
`packages/core/src/wirings/mcp/mcp-runner.ts` defaults to `!isProduction()`, and
|
|
12
|
+
`runMCPPikkuFunc` checks `exposeErrors && !isProduction()` *again* when building
|
|
13
|
+
the `-32603` internal-error response that carries `{ message, stack }`. The
|
|
14
|
+
second check looks redundant against the default — it is not. The default only
|
|
15
|
+
applies when the caller omits the option; a caller that passes
|
|
16
|
+
`exposeErrors: true` explicitly would otherwise put the raw exception message and
|
|
17
|
+
stack trace into a JSON-RPC response served from production.
|
|
18
|
+
|
|
19
|
+
Internal errors reaching that branch are by definition unmapped — no `mcpCode` —
|
|
20
|
+
so their message is whatever the underlying failure produced: a database error, a
|
|
21
|
+
file path, a credential in a connection string. MCP responses go to a model
|
|
22
|
+
client, so anything in `data` is exfiltrated into a transcript.
|
|
23
|
+
|
|
24
|
+
**What this rules out:** collapsing `exposeErrors && !isProduction()` to
|
|
25
|
+
`exposeErrors` on the grounds that the default already handles production, and
|
|
26
|
+
adding any other MCP error path that serializes `e.message` or `e.stack` without
|
|
27
|
+
its own `isProduction()` guard.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Passphrases are stretched, key material is expanded
|
|
4
|
+
description: PBKDF2 guards low-entropy human input; HKDF derives from high-entropy secrets, and the two are never swapped
|
|
5
|
+
tags: crypto
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Passphrases are stretched, key material is expanded
|
|
9
|
+
|
|
10
|
+
`packages/core/src/crypto-utils.ts` carries two derivation paths that look
|
|
11
|
+
alike and are not interchangeable.
|
|
12
|
+
|
|
13
|
+
`encryptJSON` / `decryptJSON` take a **passphrase** and run PBKDF2-HMAC-SHA256
|
|
14
|
+
at 600,000 iterations over a per-message random salt. They protect data at rest
|
|
15
|
+
— the secret and credential services — where the KEK may be something a human
|
|
16
|
+
chose, so the work factor is the only thing standing between a leaked ciphertext
|
|
17
|
+
and a dictionary.
|
|
18
|
+
|
|
19
|
+
`encryptWithKeyMaterial` / `decryptWithKeyMaterial` take **high-entropy key
|
|
20
|
+
material** and use HKDF-SHA256, which is effectively free. They protect the
|
|
21
|
+
remote-RPC session envelope, whose secret is a generated deployment value. HKDF
|
|
22
|
+
offers no brute-force resistance at all, which is why the minimum length is
|
|
23
|
+
enforced fail-closed at both ends: the entropy has to live in the secret,
|
|
24
|
+
because nothing else supplies it.
|
|
25
|
+
|
|
26
|
+
The split exists because the cost profiles are opposites. Stretching on a
|
|
27
|
+
per-request path cost ~269ms per remote hop and would have consumed most of a
|
|
28
|
+
Cloudflare Workers CPU budget; expanding a human passphrase would leave it
|
|
29
|
+
one cheap hash from being guessed. Both blobs use the same
|
|
30
|
+
`[salt:16][iv:12][ct+tag]` layout, so they are indistinguishable on the wire but
|
|
31
|
+
cryptographically disjoint — feeding one to the other's verifier rejects.
|
|
32
|
+
|
|
33
|
+
The `info` parameter namespaces each use of the same key material, so a key
|
|
34
|
+
derived for one purpose cannot decrypt another's payload.
|
|
35
|
+
|
|
36
|
+
**What this rules out:** using `encryptJSON` on any per-request path; using the
|
|
37
|
+
key-material functions for anything a human types; sharing one derived key
|
|
38
|
+
across two purposes by passing the same `info`; and lowering the minimum length
|
|
39
|
+
to accommodate an existing short secret. See
|
|
40
|
+
[[the-kek-salt-is-scoped-to-the-key-version]].
|
package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Auth filtering requires live permission functions, never their metadata
|
|
4
|
+
description: checkAuthPermissions collects pikkuAuth-branded predicates off the real config; passing metadata would let every gated tool through
|
|
5
|
+
tags: permissions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Auth filtering requires live permission functions, never their metadata
|
|
9
|
+
|
|
10
|
+
`checkAuthPermissions` in `packages/core/src/permissions.ts` answers "may this
|
|
11
|
+
session see this function/agent at all", for listing and tool-filtering. It
|
|
12
|
+
collects only predicates branded `__pikkuAuth` — from the globals and from the
|
|
13
|
+
supplied group — and ignores data-dependent permissions, which cannot be
|
|
14
|
+
evaluated without request data at filter time. If no auth predicates exist at
|
|
15
|
+
all, the answer is `true`: nothing is gating visibility.
|
|
16
|
+
|
|
17
|
+
`funcPermissions` must therefore be the **live** `CorePermissionGroup` taken from
|
|
18
|
+
the function or agent config, not the metadata form. The `__pikkuAuth` brand only
|
|
19
|
+
survives on the actual predicate objects, and the by-name registry that metadata
|
|
20
|
+
entries would resolve against is never populated. Passing metadata compiles
|
|
21
|
+
cleanly, collects nothing, hits the `authPerms.length === 0` branch and returns
|
|
22
|
+
`true` — every gated tool becomes visible to every session, silently and with no
|
|
23
|
+
error anywhere.
|
|
24
|
+
|
|
25
|
+
Note also that the collected predicates are ORed: any one passing auth predicate
|
|
26
|
+
grants visibility. This is a visibility filter only; `runPermissions` still runs
|
|
27
|
+
the full gate on invocation.
|
|
28
|
+
|
|
29
|
+
**What this rules out:** feeding this function anything derived from function
|
|
30
|
+
metadata or a serialized permission description, and treating a `true` result as
|
|
31
|
+
authorization to invoke rather than permission to list.
|
package/knowledge/decisions/security/pikku-carries-actor-scopes-as-data-and-the-app-grants-them.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Pikku carries actor scopes as data and the app grants them
|
|
4
|
+
description: scopes and roles on a ScenarioActorConfig are transported, never applied — the app's own seed reads them back and performs the grant
|
|
5
|
+
tags: services
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Pikku carries actor scopes as data and the app grants them
|
|
9
|
+
|
|
10
|
+
`ScenarioActorConfig.scopes` and `.roles`
|
|
11
|
+
(`packages/core/src/services/scenario-actors-service.ts`) come from
|
|
12
|
+
`pikku.config.json` and are carried through to `scenarioActorConfigs`. Nothing in
|
|
13
|
+
core reads them to grant anything. The app's seed reads them back off that config
|
|
14
|
+
and performs the grants itself.
|
|
15
|
+
|
|
16
|
+
Core cannot do the granting: which scope store exists, whether roles have been
|
|
17
|
+
created, and what a role means are all the application's own. Worse, a framework
|
|
18
|
+
that granted scopes would be a framework that can escalate an actor's privileges
|
|
19
|
+
from a config file — the one place where a scenario author would least expect a
|
|
20
|
+
security decision to take effect. Keeping the fields inert makes the grant an act
|
|
21
|
+
the app performs deliberately, in code it owns, against a store it wired.
|
|
22
|
+
|
|
23
|
+
**What this rules out:** teaching `createHttpScenarioActors` (or any core service)
|
|
24
|
+
to call `ScopeService.addScopeToUser` / `addUserToRole` from these fields, and
|
|
25
|
+
treating a scope listed on an actor config as evidence that the actor holds it.
|
|
26
|
+
If a scenario fails on a missing scope, the fix is in the app's seed, not here.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: Queue job identities are signed at enqueue
|
|
4
|
+
description: A job's pikkuUserId is an HMAC claim bound to the queue and payload; an unverifiable claim is dropped, never trusted
|
|
5
|
+
tags: queue, crypto, security
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Queue job identities are signed at enqueue
|
|
9
|
+
|
|
10
|
+
`QueueJob.pikkuUserId` used to be a bare string that `runQueueJob`
|
|
11
|
+
(`packages/core/src/wirings/queue/queue-runner.ts`) copied onto the wire, where
|
|
12
|
+
`defaultPikkuUserIdResolver` resolved it and `resolveSession` loaded that user's
|
|
13
|
+
session out of the `sessionStore`. Anyone able to write to the queue backend —
|
|
14
|
+
a `LPUSH` against Redis, a `SendMessage` against SQS — could name any user and
|
|
15
|
+
the worker would run as them.
|
|
16
|
+
|
|
17
|
+
The field now carries a signed claim instead:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
pq1.<base64url({"u":<pikkuUserId>,"q":<queueName>,"j":<jobId>?})>.<base64url(HMAC-SHA256)>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The HMAC covers `pq1.<encoded claim>.<canonical JSON of the job payload>`, so a
|
|
24
|
+
signature cannot be lifted onto a different queue, a different payload, or —
|
|
25
|
+
when the producer knew the job id — a different job. `jobId` is optional
|
|
26
|
+
because most adapters (SQS, Cloudflare Queues, Azure Storage Queues) mint the id
|
|
27
|
+
after `add` returns and never see the producer's; binding the payload is what
|
|
28
|
+
holds in every adapter. Canonicalization sorts keys after a JSON round trip, so
|
|
29
|
+
transport reserialization cannot change the digest.
|
|
30
|
+
|
|
31
|
+
The key is expanded with HKDF from `PIKKU_QUEUE_IDENTITY_SECRET`, read through
|
|
32
|
+
the `SecretService`, under the `pikku:queue-identity` info namespace — the same
|
|
33
|
+
deployment secret therefore cannot produce a key that opens the remote-RPC
|
|
34
|
+
envelope. `signWithKeyMaterial` / `verifyWithKeyMaterial` in
|
|
35
|
+
`packages/core/src/crypto-utils.ts` are WebCrypto-only because that file is in
|
|
36
|
+
the Cloudflare Workers build; the `node:crypto` helpers behind
|
|
37
|
+
`WebhookService.sign` are not reachable from it.
|
|
38
|
+
|
|
39
|
+
Verification lives in `runQueueJob`, the single funnel every adapter calls.
|
|
40
|
+
`SignedQueueService` wraps any `QueueService` to sign on the way out.
|
|
41
|
+
|
|
42
|
+
Failure directions are deliberately asymmetric:
|
|
43
|
+
|
|
44
|
+
- **No secret configured**: the claim is dropped, the job still runs with no
|
|
45
|
+
`pikkuUserId`, and one warning is logged per process. Hard-failing would break
|
|
46
|
+
every deployment on upgrade, and per-job logging is an attacker-triggerable
|
|
47
|
+
flood.
|
|
48
|
+
- **Secret configured, claim does not verify**: the identity is dropped and the
|
|
49
|
+
rejection is logged per job — it sits alongside the per-job logs `runQueueJob`
|
|
50
|
+
already writes, so it adds no flood an attacker did not already have.
|
|
51
|
+
- **Secret too short**: signing throws `WeakKeyMaterialError` at enqueue, so the
|
|
52
|
+
misconfiguration surfaces on the producer, while the worker drops the identity
|
|
53
|
+
rather than failing the job.
|
|
54
|
+
|
|
55
|
+
The threat model is a **compromised or shared queue backend**, nothing more. The
|
|
56
|
+
application process must hold the signing secret in order to sign, so an
|
|
57
|
+
attacker who has code execution inside the app can mint any claim it likes. This
|
|
58
|
+
defends the queue as a transport, not the app as a host. It also does not stop
|
|
59
|
+
replay of a byte-identical job, which is indistinguishable from the redelivery
|
|
60
|
+
every queue already performs; revocation is by rotating the secret.
|
|
61
|
+
|
|
62
|
+
**What this rules out:** treating `JobOptions.pikkuUserId` as trusted anywhere
|
|
63
|
+
downstream of the queue; letting a client-supplied value flow into it without an
|
|
64
|
+
authorization check first (signing proves the producer held the secret, not that
|
|
65
|
+
the producer was entitled to name that user); adding a `node:crypto` import to
|
|
66
|
+
`crypto-utils.ts`; reusing `pikku:queue-identity` as the info string for any
|
|
67
|
+
other purpose; and claiming this mitigates application compromise. See
|
|
68
|
+
[[queue-jobs-carry-the-producers-pikku-user-id]] and
|
|
69
|
+
[[passphrases-are-stretched-key-material-is-expanded]].
|