@pikku/core 0.12.143 → 0.12.144
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/dist/analytics/analytics.d.ts +14 -0
- package/dist/analytics/analytics.js +14 -0
- package/dist/analytics/analytics.types.d.ts +1 -0
- package/dist/analytics/anonymous-analytics-identity.d.ts +1 -0
- package/dist/analytics/cookie-analytics-identity.d.ts +1 -0
- package/dist/analytics/mint-cookie.d.ts +1 -0
- package/dist/{wirings/channel/channel-middleware-runner.d.ts → channel-middleware-runner.d.ts} +17 -3
- package/dist/{wirings/channel/channel-middleware-runner.js → channel-middleware-runner.js} +24 -8
- package/dist/classification/data-classification.d.ts +13 -0
- package/dist/classification/secret-value.d.ts +7 -0
- package/dist/classification/secret-value.js +7 -0
- package/dist/crypto-utils.d.ts +93 -0
- package/dist/crypto-utils.js +92 -0
- package/dist/dev/hot-reload.d.ts +16 -0
- package/dist/dev/hot-reload.js +16 -1
- package/dist/dev/module-runner.js +7 -3
- package/dist/dev/reload-meta.js +1 -1
- package/dist/errors/error-handler.d.ts +9 -0
- package/dist/errors/error-handler.js +8 -0
- package/dist/errors/errors.d.ts +219 -0
- package/dist/errors/errors.js +227 -0
- package/dist/errors/serialized-error.d.ts +4 -0
- package/dist/function/abort-scope.d.ts +5 -0
- package/dist/function/function-meta.types.d.ts +20 -0
- package/dist/function/function-runner.d.ts +41 -0
- package/dist/function/function-runner.js +57 -11
- package/dist/function/functions.types.d.ts +59 -0
- package/dist/function/functions.types.js +23 -0
- package/dist/function/list.types.d.ts +4 -0
- package/dist/lifecycle.d.ts +12 -1
- package/dist/lifecycle.js +12 -1
- package/dist/middleware/addon-scope-cap.d.ts +4 -0
- package/dist/middleware/addon-scope-cap.js +7 -3
- package/dist/middleware/auth-apikey.js +3 -3
- package/dist/middleware/auth-bearer.js +5 -13
- package/dist/middleware/auth-cookie.js +3 -3
- package/dist/middleware/middleware-factories.d.ts +17 -0
- package/dist/middleware/middleware-factories.js +17 -0
- package/dist/middleware/middleware.types.d.ts +23 -0
- package/dist/middleware/remote-auth.d.ts +15 -0
- package/dist/middleware/remote-auth.js +18 -3
- package/dist/middleware/remote-jobs-secret.js +2 -10
- package/dist/middleware-runner.d.ts +7 -0
- package/dist/middleware-runner.js +23 -7
- package/dist/permissions.d.ts +13 -0
- package/dist/permissions.js +13 -0
- package/dist/pikku-state.d.ts +49 -0
- package/dist/pikku-state.js +49 -0
- package/dist/remote.js +20 -5
- package/dist/schema.d.ts +36 -0
- package/dist/schema.js +36 -0
- package/dist/services/agent-run-state-service.d.ts +8 -0
- package/dist/services/agent-runner-service.d.ts +24 -0
- package/dist/services/agent-storage-service.d.ts +8 -0
- package/dist/services/ai-embedding-service.d.ts +4 -0
- package/dist/services/audit-service.d.ts +48 -0
- package/dist/services/audit-service.js +22 -0
- package/dist/services/content-service.d.ts +34 -0
- package/dist/services/credential-wire-service.d.ts +9 -0
- package/dist/services/credential-wire-service.js +9 -0
- package/dist/services/deployment-service.d.ts +14 -0
- package/dist/services/email-service.d.ts +20 -0
- package/dist/services/email-template.d.ts +24 -0
- package/dist/services/email-template.js +12 -0
- package/dist/services/file-scenario-run-store.d.ts +5 -0
- package/dist/services/file-scenario-run-store.js +4 -0
- package/dist/services/gateway-service.d.ts +5 -0
- package/dist/services/in-memory-agent-run-state-service.d.ts +5 -0
- package/dist/services/in-memory-agent-run-state-service.js +5 -0
- package/dist/services/in-memory-lease-service.d.ts +5 -0
- package/dist/services/in-memory-lease-service.js +5 -0
- package/dist/services/in-memory-session-store.d.ts +4 -0
- package/dist/services/in-memory-session-store.js +4 -0
- package/dist/services/incoming-webhook-queue.d.ts +2 -0
- package/dist/services/incoming-webhook-queue.js +2 -0
- package/dist/services/incoming-webhook-service.d.ts +9 -1
- package/dist/services/incoming-webhook-service.js +1 -1
- package/dist/services/index.d.ts +1 -5
- package/dist/services/index.js +1 -5
- package/dist/services/jwt-service.d.ts +8 -0
- package/dist/services/lease-service.d.ts +5 -0
- package/dist/services/lease-service.js +1 -0
- package/dist/services/local-content-request-handler.d.ts +20 -0
- package/dist/services/local-content-request-handler.js +11 -0
- package/dist/services/local-content.d.ts +45 -0
- package/dist/services/local-content.js +35 -0
- package/dist/services/local-credential-service.d.ts +5 -0
- package/dist/services/local-credential-service.js +5 -0
- package/dist/services/local-email-service.d.ts +7 -0
- package/dist/services/local-email-service.js +7 -0
- package/dist/services/local-secrets.d.ts +8 -0
- package/dist/services/local-secrets.js +8 -0
- package/dist/services/local-variables.d.ts +6 -0
- package/dist/services/local-variables.js +6 -0
- package/dist/services/logger.d.ts +4 -0
- package/dist/services/logger.js +4 -0
- package/dist/services/meta-service.d.ts +38 -0
- package/dist/services/meta-service.js +6 -0
- package/dist/services/queue-webhook-service.d.ts +19 -0
- package/dist/services/queue-webhook-service.js +19 -0
- package/dist/services/remote-jobs.d.ts +8 -13
- package/dist/services/remote-jobs.js +3 -54
- package/dist/services/scenario-cookie-jar.d.ts +4 -0
- package/dist/services/scheduler-service.d.ts +13 -0
- package/dist/services/scheduler-service.js +8 -0
- package/dist/services/schema-service.d.ts +9 -0
- package/dist/services/scope-service.d.ts +10 -0
- package/dist/services/scoped-secret-service.d.ts +5 -0
- package/dist/services/scoped-secret-service.js +5 -0
- package/dist/services/secret-host-binding.d.ts +5 -0
- package/dist/services/secret-host-binding.js +5 -0
- package/dist/services/secret-service.d.ts +19 -1
- package/dist/services/secretless.d.ts +6 -0
- package/dist/services/secretless.js +6 -0
- package/dist/services/session-store.d.ts +5 -0
- package/dist/services/stub-tracker.d.ts +1 -0
- package/dist/services/temporary-file-service.d.ts +9 -0
- package/dist/services/temporary-file-service.js +9 -0
- package/dist/services/trigger-service.d.ts +4 -0
- package/dist/services/trigger-source-store.d.ts +13 -0
- package/dist/services/trigger-source-store.js +7 -0
- package/dist/services/typed-credential-service.d.ts +6 -0
- package/dist/services/typed-credential-service.js +5 -0
- package/dist/services/typed-secret-service.d.ts +12 -0
- package/dist/services/typed-secret-service.js +11 -0
- package/dist/services/typed-variables-service.d.ts +1 -0
- package/dist/services/user-session-service.d.ts +31 -1
- package/dist/services/user-session-service.js +29 -3
- package/dist/services/v8-coverage-service.d.ts +27 -0
- package/dist/services/v8-coverage-service.js +5 -0
- package/dist/services/variables-service.d.ts +8 -0
- package/dist/services/virtual-user-sign-in.d.ts +1 -0
- package/dist/services/virtual-user-statement.d.ts +8 -0
- package/dist/services/virtual-user-statement.js +4 -0
- package/dist/services/webhook-service.d.ts +25 -0
- package/dist/services/webhook-service.js +15 -0
- package/dist/services/workflow-service.d.ts +6 -0
- package/dist/test-utils.js +1 -1
- package/dist/testing/service-tests.d.ts +4 -0
- package/dist/time-utils.d.ts +15 -0
- package/dist/time-utils.js +14 -0
- package/dist/types/core.types.d.ts +80 -0
- package/dist/types/state.types.d.ts +4 -0
- package/dist/utils/hmac.d.ts +3 -0
- package/dist/utils/safe-fetch.d.ts +14 -0
- package/dist/utils/safe-fetch.js +13 -0
- package/dist/utils/stable-stringify.d.ts +11 -0
- package/dist/utils/stable-stringify.js +33 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.js +22 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.js +7 -0
- package/dist/wirings/addon/define-screens.d.ts +2 -0
- package/dist/wirings/addon/remote-addon-auth.d.ts +9 -0
- package/dist/wirings/addon/remote-addon-auth.js +5 -0
- package/dist/wirings/addon/wire-addon.d.ts +4 -0
- package/dist/wirings/addon/wire-remote-addon.d.ts +1 -0
- package/dist/wirings/agent/agent-agui.d.ts +14 -5
- package/dist/wirings/agent/agent-agui.js +17 -6
- package/dist/wirings/agent/agent-helpers.d.ts +61 -1
- package/dist/wirings/agent/agent-helpers.js +62 -2
- package/dist/wirings/agent/agent-interrupt.d.ts +5 -0
- package/dist/wirings/agent/agent-output.d.ts +20 -0
- package/dist/wirings/agent/agent-output.js +69 -0
- package/dist/wirings/agent/agent-prepare.d.ts +41 -0
- package/dist/wirings/agent/agent-prepare.js +38 -2
- package/dist/wirings/agent/agent-registry.d.ts +8 -0
- package/dist/wirings/agent/agent-registry.js +10 -4
- package/dist/wirings/agent/agent-runner.d.ts +33 -0
- package/dist/wirings/agent/agent-runner.js +40 -7
- package/dist/wirings/agent/agent-stream.d.ts +30 -0
- package/dist/wirings/agent/agent-stream.js +33 -11
- package/dist/wirings/agent/agent.types.d.ts +43 -0
- package/dist/wirings/agent/voice-input.d.ts +12 -0
- package/dist/wirings/agent/voice-input.js +12 -0
- package/dist/wirings/agent/voice-output.d.ts +14 -0
- package/dist/wirings/agent/voice-output.js +10 -0
- package/dist/wirings/agent-scorer/agent-scorer-judge.js +1 -2
- package/dist/wirings/agent-scorer/agent-scorer-registry.d.ts +10 -0
- package/dist/wirings/agent-scorer/agent-scorer-registry.js +10 -0
- package/dist/wirings/agent-scorer/agent-scorer-snapshots.d.ts +1 -0
- package/dist/wirings/agent-scorer/agent-scorer-snapshots.js +1 -0
- package/dist/wirings/agent-scorer/agent-scorer.types.d.ts +7 -0
- package/dist/wirings/channel/channel-common.js +1 -1
- package/dist/wirings/channel/channel-rpc.types.d.ts +5 -0
- package/dist/wirings/channel/channel-runner.d.ts +22 -0
- package/dist/wirings/channel/channel-runner.js +22 -0
- package/dist/wirings/channel/channel-store.d.ts +8 -0
- package/dist/wirings/channel/channel-store.js +7 -0
- package/dist/wirings/channel/channel.types.d.ts +18 -0
- package/dist/wirings/channel/define-channel-routes.d.ts +5 -0
- package/dist/wirings/channel/define-channel-routes.js +5 -0
- package/dist/wirings/channel/eventhub-service.d.ts +9 -0
- package/dist/wirings/channel/eventhub-store.d.ts +1 -0
- package/dist/wirings/channel/eventhub-store.js +1 -0
- package/dist/wirings/channel/index.d.ts +1 -1
- package/dist/wirings/channel/index.js +1 -1
- package/dist/wirings/channel/local/local-channel-handler.d.ts +1 -0
- package/dist/wirings/channel/local/local-channel-handler.js +1 -0
- package/dist/wirings/channel/local/local-channel-runner.d.ts +13 -0
- package/dist/wirings/channel/local/local-channel-runner.js +13 -0
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +1 -0
- package/dist/wirings/channel/local/local-eventhub-service.js +1 -0
- package/dist/wirings/channel/log-channels.d.ts +1 -0
- package/dist/wirings/channel/log-channels.js +1 -0
- package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +5 -0
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +5 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +32 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +31 -0
- package/dist/wirings/cli/channel/cli-channel-runner.d.ts +8 -0
- package/dist/wirings/cli/channel/cli-channel-runner.js +8 -0
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +1 -0
- package/dist/wirings/cli/cli-runner.d.ts +43 -0
- package/dist/wirings/cli/cli-runner.js +43 -0
- package/dist/wirings/cli/command-parser.d.ts +13 -0
- package/dist/wirings/cli/command-parser.js +12 -0
- package/dist/wirings/credential/credential-overrides.d.ts +1 -0
- package/dist/wirings/credential/credential.types.d.ts +7 -0
- package/dist/wirings/credential/validate-credential-definitions.d.ts +7 -0
- package/dist/wirings/credential/validate-credential-definitions.js +7 -0
- package/dist/wirings/flag/cached-flag-source.d.ts +1 -0
- package/dist/wirings/flag/flag.types.d.ts +3 -0
- package/dist/wirings/gateway/gateway-runner.d.ts +23 -0
- package/dist/wirings/gateway/gateway-runner.js +23 -0
- package/dist/wirings/gateway/gateway.types.d.ts +3 -0
- package/dist/wirings/gateway/local-gateway-service.d.ts +10 -0
- package/dist/{services → wirings/gateway}/local-gateway-service.js +6 -2
- package/dist/wirings/http/http-routes.d.ts +21 -0
- package/dist/wirings/http/http-routes.js +11 -0
- package/dist/wirings/http/http-runner.d.ts +71 -0
- package/dist/wirings/http/http-runner.js +73 -5
- package/dist/wirings/http/http.types.d.ts +27 -0
- package/dist/wirings/http/index.d.ts +1 -0
- package/dist/wirings/http/index.js +1 -0
- package/dist/wirings/http/log-http-routes.d.ts +1 -0
- package/dist/wirings/http/log-http-routes.js +1 -0
- package/dist/wirings/http/merge-request-data.d.ts +12 -0
- package/dist/wirings/http/merge-request-data.js +42 -0
- package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -32
- package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-response.js +1 -0
- package/dist/wirings/http/routers/http-router.d.ts +6 -0
- package/dist/wirings/http/routers/http-router.js +6 -0
- package/dist/wirings/http/web-request.d.ts +17 -0
- package/dist/wirings/http/web-request.js +17 -0
- package/dist/wirings/mcp/mcp-endpoint-registry.d.ts +7 -0
- package/dist/wirings/mcp/mcp-endpoint-registry.js +7 -0
- package/dist/wirings/mcp/mcp-runner.d.ts +63 -0
- package/dist/wirings/mcp/mcp-runner.js +62 -0
- package/dist/wirings/mcp/mcp.types.d.ts +12 -0
- package/dist/wirings/node/node.types.d.ts +4 -0
- package/dist/wirings/oauth2/oauth2.types.d.ts +2 -0
- package/dist/{services → wirings/queue}/in-memory-queue-service.d.ts +9 -1
- package/dist/{services → wirings/queue}/in-memory-queue-service.js +9 -1
- package/dist/wirings/queue/index.d.ts +1 -0
- package/dist/wirings/queue/index.js +1 -0
- package/dist/wirings/queue/queue-identity.js +2 -14
- package/dist/wirings/queue/queue-runner.d.ts +37 -0
- package/dist/wirings/queue/queue-runner.js +37 -0
- package/dist/wirings/queue/queue.types.d.ts +35 -0
- package/dist/wirings/queue/register-queue-helper.d.ts +12 -0
- package/dist/wirings/queue/register-queue-helper.js +11 -0
- package/dist/wirings/queue/remote-queue-job.d.ts +11 -0
- package/dist/wirings/queue/remote-queue-job.js +41 -0
- package/dist/wirings/queue/validate-worker-config.d.ts +1 -0
- package/dist/wirings/role/role.types.d.ts +3 -0
- package/dist/wirings/rpc/rpc-runner.d.ts +24 -0
- package/dist/wirings/rpc/rpc-runner.js +37 -2
- package/dist/wirings/rpc/rpc-types.d.ts +2 -0
- package/dist/wirings/scheduler/index.d.ts +1 -0
- package/dist/wirings/scheduler/index.js +1 -0
- package/dist/wirings/scheduler/log-schedulers.d.ts +1 -0
- package/dist/wirings/scheduler/log-schedulers.js +1 -0
- package/dist/wirings/scheduler/remote-scheduled-job.d.ts +11 -0
- package/dist/wirings/scheduler/remote-scheduled-job.js +20 -0
- package/dist/wirings/scheduler/scheduler-runner.d.ts +29 -0
- package/dist/wirings/scheduler/scheduler-runner.js +28 -0
- package/dist/wirings/scheduler/scheduler.types.d.ts +3 -0
- package/dist/wirings/scope/scope.types.d.ts +5 -0
- package/dist/wirings/secret/index.d.ts +2 -2
- package/dist/wirings/secret/index.js +1 -1
- package/dist/wirings/secret/secret.types.d.ts +26 -1
- package/dist/wirings/secret/secret.types.js +1 -0
- package/dist/wirings/secret/validate-secret-definitions.d.ts +8 -0
- package/dist/wirings/secret/validate-secret-definitions.js +15 -0
- package/dist/wirings/trigger/in-memory-trigger-service.d.ts +11 -0
- package/dist/{services → wirings/trigger}/in-memory-trigger-service.js +10 -3
- package/dist/wirings/trigger/index.d.ts +1 -1
- package/dist/wirings/trigger/pikku-trigger-service.d.ts +8 -3
- package/dist/wirings/trigger/pikku-trigger-service.js +14 -3
- package/dist/wirings/trigger/trigger-runner.d.ts +27 -2
- package/dist/wirings/trigger/trigger-runner.js +35 -2
- package/dist/wirings/trigger/trigger.types.d.ts +26 -4
- package/dist/wirings/trigger/webhook-source-runner.d.ts +24 -0
- package/dist/wirings/trigger/webhook-source-runner.js +19 -0
- package/dist/wirings/trigger/webhook-source.types.d.ts +12 -2
- package/dist/wirings/trigger/webhook-source.types.js +1 -1
- package/dist/wirings/variable/validate-variable-definitions.d.ts +7 -0
- package/dist/wirings/variable/validate-variable-definitions.js +7 -0
- package/dist/wirings/variable/variable.types.d.ts +7 -0
- package/dist/wirings/virtual-user/conversation/conversation.types.d.ts +5 -0
- package/dist/wirings/virtual-user/conversation/run-conversation.d.ts +6 -0
- package/dist/wirings/virtual-user/conversation/run-conversation.js +1 -1
- package/dist/{services → wirings/virtual-user}/http-virtual-users.d.ts +4 -3
- package/dist/{services → wirings/virtual-user}/http-virtual-users.js +6 -6
- package/dist/wirings/virtual-user/index.d.ts +1 -1
- package/dist/wirings/virtual-user/index.js +1 -1
- package/dist/wirings/virtual-user/run-virtual-user.d.ts +2 -1
- package/dist/wirings/virtual-user/run-virtual-user.js +1 -14
- package/dist/wirings/virtual-user/virtual-user-declaration.types.d.ts +4 -0
- package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +10 -1
- package/dist/wirings/virtual-user/virtual-user-scaffold.js +5 -0
- package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +5 -0
- package/dist/wirings/virtual-user/virtual-user-schedule.js +1 -0
- package/dist/wirings/virtual-user/virtual-user-target.d.ts +1 -0
- package/dist/wirings/webhook/define-outgoing-webhook.d.ts +20 -0
- package/dist/wirings/webhook/define-outgoing-webhook.js +9 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +8 -4
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +7 -0
- package/dist/wirings/workflow/dsl/workflow-runner.js +7 -0
- package/dist/wirings/workflow/feature.d.ts +12 -0
- package/dist/wirings/workflow/feature.js +12 -0
- package/dist/wirings/workflow/graph/graph-node.d.ts +5 -0
- package/dist/wirings/workflow/graph/graph-node.js +5 -0
- package/dist/wirings/workflow/graph/template.d.ts +6 -0
- package/dist/wirings/workflow/graph/template.js +5 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +12 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.js +10 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +11 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.js +1 -0
- package/dist/{services → wirings/workflow}/in-memory-workflow-service.d.ts +8 -3
- package/dist/{services → wirings/workflow}/in-memory-workflow-service.js +9 -4
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +1 -1
- package/dist/wirings/workflow/pikku-scenario-service.js +2 -2
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +1 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +1 -0
- package/dist/wirings/workflow/run-timeline.d.ts +21 -0
- package/dist/wirings/workflow/run-timeline.js +15 -7
- package/dist/wirings/workflow/scenario-coverage.d.ts +1 -0
- package/dist/wirings/workflow/scenario-poll.d.ts +1 -0
- package/dist/wirings/workflow/scenario-prose.d.ts +6 -0
- package/dist/wirings/workflow/scenario-prose.js +6 -0
- package/dist/wirings/workflow/scenario-run.types.d.ts +3 -0
- package/dist/wirings/workflow/scenario-step.types.d.ts +1 -0
- package/dist/wirings/workflow/scenario-step.types.js +1 -0
- package/dist/wirings/workflow/scenario-surface.js +1 -13
- package/dist/wirings/workflow/scenario.types.d.ts +6 -0
- package/dist/wirings/workflow/workflow-approval-policy.d.ts +6 -0
- package/dist/wirings/workflow/workflow-approval-policy.js +6 -0
- package/dist/wirings/workflow/workflow-constants.d.ts +7 -1
- package/dist/wirings/workflow/workflow-constants.js +10 -1
- package/dist/wirings/workflow/workflow-errors.d.ts +13 -0
- package/dist/wirings/workflow/workflow-errors.js +13 -0
- package/dist/wirings/workflow/workflow-invocation-id.d.ts +4 -0
- package/dist/wirings/workflow/workflow-invocation-id.js +4 -0
- package/dist/wirings/workflow/workflow-queue-workers.d.ts +3 -0
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +1 -0
- package/dist/wirings/workflow/workflow-run-ownership.js +1 -0
- package/dist/wirings/workflow/workflow-status-stream.d.ts +1 -0
- package/dist/wirings/workflow/workflow-unwind-plan.js +2 -7
- package/dist/wirings/workflow/workflow.types.d.ts +40 -0
- package/package.json +26 -9
- package/CHANGELOG.md +0 -6978
- package/dist/services/in-memory-trigger-service.d.ts +0 -4
- package/dist/services/local-gateway-service.d.ts +0 -6
- package/dist/utils/hash.d.ts +0 -2
- package/dist/utils/hash.js +0 -23
- package/knowledge/decisions/index.md +0 -19
- package/knowledge/decisions/internals/a-credential-name-declared-twice-must-agree-on-schema-and-type.md +0 -29
- package/knowledge/decisions/internals/a-grant-can-name-a-scope-declared-without-its-addon.md +0 -29
- package/knowledge/decisions/internals/a-held-run-is-woken-later-not-retried.md +0 -28
- package/knowledge/decisions/internals/a-latency-budget-is-asserted-by-its-cause.md +0 -32
- package/knowledge/decisions/internals/a-non-streaming-agent-run-registers-with-agentrunstate-too.md +0 -22
- package/knowledge/decisions/internals/a-reset-suite-makes-the-dev-seed-the-fixture.md +0 -50
- package/knowledge/decisions/internals/a-resumed-agent-turn-is-as-interruptible-as-the-first.md +0 -20
- package/knowledge/decisions/internals/a-scenario-step-template-is-offered-unfilled.md +0 -21
- package/knowledge/decisions/internals/a-scheduled-task-writes-its-caller-session-before-it-runs.md +0 -22
- package/knowledge/decisions/internals/a-secret-set-in-memory-shadows-the-variable-of-the-same-name.md +0 -28
- package/knowledge/decisions/internals/a-secret-that-fails-to-decrypt-fails-the-whole-read.md +0 -49
- package/knowledge/decisions/internals/a-virtual-user-acts-through-invokeraw-so-a-refusal-is-data.md +0 -32
- package/knowledge/decisions/internals/a-virtual-user-cadence-is-a-row-not-a-timer.md +0 -66
- package/knowledge/decisions/internals/a-virtual-user-converse-signs-in-before-its-first-turn.md +0 -24
- package/knowledge/decisions/internals/a-virtual-user-decides-whether-to-trust-memory-once-per-turn.md +0 -21
- package/knowledge/decisions/internals/a-virtual-user-email-is-computed-with-the-run-id-as-a-sub-address.md +0 -34
- package/knowledge/decisions/internals/a-virtual-user-run-is-not-a-workflow-but-it-needs-a-trigger.md +0 -65
- package/knowledge/decisions/internals/a-wall-clock-threshold-is-a-load-test-in-disguise.md +0 -49
- package/knowledge/decisions/internals/a-workflow-wire-is-built-from-the-run-not-from-the-rpc-service.md +0 -45
- package/knowledge/decisions/internals/addon-package-roots-resolve-by-walking-node-module-search-paths.md +0 -26
- package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +0 -32
- package/knowledge/decisions/internals/addon-singleton-services-are-cached-per-namespace-not-per-package.md +0 -33
- package/knowledge/decisions/internals/addon-workflow-names-are-prefixed-with-the-consumer-namespace.md +0 -28
- package/knowledge/decisions/internals/agent-agui-bridge-obeys-the-client-ordering-contract.md +0 -29
- package/knowledge/decisions/internals/agent-audio-chunks-carry-the-format-the-provider-returned.md +0 -20
- package/knowledge/decisions/internals/agent-context-waits-for-a-tool-result-still-being-written.md +0 -21
- package/knowledge/decisions/internals/agent-credential-suspensions-hide-the-tool-result.md +0 -26
- package/knowledge/decisions/internals/agent-delegate-and-supervise-hide-different-text.md +0 -26
- package/knowledge/decisions/internals/agent-llm-tool-arguments-have-nulls-stripped.md +0 -23
- package/knowledge/decisions/internals/agent-model-config-stays-a-single-resolution-seam.md +0 -25
- package/knowledge/decisions/internals/agent-onerror-hooks-cannot-change-the-failure.md +0 -22
- package/knowledge/decisions/internals/agent-runner-methods-must-keep-their-receiver.md +0 -22
- package/knowledge/decisions/internals/agent-speech-travels-as-a-custom-agui-event.md +0 -22
- package/knowledge/decisions/internals/agent-stream-persistence-is-best-effort.md +0 -27
- package/knowledge/decisions/internals/agent-sub-agents-inherit-the-parent-context-block.md +0 -26
- package/knowledge/decisions/internals/agent-tool-execute-failures-are-logged-unconditionally.md +0 -25
- package/knowledge/decisions/internals/agent-voice-input-transcribes-audio-parts-in-place.md +0 -22
- package/knowledge/decisions/internals/agent-working-memory-is-persisted-only-when-valid.md +0 -25
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +0 -39
- package/knowledge/decisions/internals/an-agent-interrupt-is-not-a-failure.md +0 -23
- package/knowledge/decisions/internals/an-agent-is-offered-under-its-registry-key-and-only-within-the-virtual-users-scopes.md +0 -27
- package/knowledge/decisions/internals/an-agent-run-owned-by-another-instance-says-so.md +0 -25
- package/knowledge/decisions/internals/an-agent-stream-send-must-return-the-inner-sends-promise.md +0 -22
- package/knowledge/decisions/internals/an-empty-text-part-is-omitted-from-an-agent-message.md +0 -20
- package/knowledge/decisions/internals/an-empty-transcript-is-not-recorded.md +0 -22
- package/knowledge/decisions/internals/an-oauth2-credential-implies-an-optional-app-secret.md +0 -26
- package/knowledge/decisions/internals/an-unref-d-timer-cannot-be-awaited-under-node-test.md +0 -66
- package/knowledge/decisions/internals/analytics-identity-resolvers-run-in-declared-order.md +0 -22
- package/knowledge/decisions/internals/channel-message-handlers-accept-three-config-shapes.md +0 -30
- package/knowledge/decisions/internals/channel-middleware-caches-only-statically-resolved-middleware.md +0 -31
- package/knowledge/decisions/internals/channel-state-accessors-are-unsound-generics-that-every-implementation-asserts.md +0 -33
- package/knowledge/decisions/internals/channel-state-is-per-socket-session-state-is-per-user.md +0 -29
- package/knowledge/decisions/internals/channel-user-id-is-persisted-after-onconnect-middleware-runs.md +0 -28
- package/knowledge/decisions/internals/cli-option-names-are-camelcase-in-state-and-kebab-on-the-command-line.md +0 -27
- package/knowledge/decisions/internals/cli-parse-errors-are-routed-by-message-prefix.md +0 -28
- package/knowledge/decisions/internals/cli-stdout-is-reserved-for-machine-readable-output.md +0 -34
- package/knowledge/decisions/internals/cli-unknown-long-options-warn-instead-of-failing.md +0 -29
- package/knowledge/decisions/internals/core-column-form-is-an-axis-of-its-own.md +0 -84
- package/knowledge/decisions/internals/core-data-classification-brand-is-an-optional-property.md +0 -41
- package/knowledge/decisions/internals/core-function-runner-restores-the-wire-fields-it-overwrites.md +0 -44
- package/knowledge/decisions/internals/core-hot-reload-merges-generated-meta-never-replaces-it.md +0 -39
- package/knowledge/decisions/internals/core-hot-reload-owns-its-module-registry.md +0 -42
- package/knowledge/decisions/internals/core-middleware-order-is-scope-then-priority.md +0 -39
- package/knowledge/decisions/internals/core-schema-defaults-apply-on-every-transport.md +0 -43
- package/knowledge/decisions/internals/core-scopes-are-an-and-gate-separate-from-permissions.md +0 -38
- package/knowledge/decisions/internals/core-state-is-a-global-map-written-only-at-registration-time.md +0 -44
- package/knowledge/decisions/internals/email-meta-is-read-uncached-because-codegen-rewrites-it-mid-session.md +0 -27
- package/knowledge/decisions/internals/feature-flag-declarations-refuse-an-empty-anyof-and-conflicting-scopes.md +0 -22
- package/knowledge/decisions/internals/feature-flags-fail-open-and-never-enforce-access.md +0 -28
- package/knowledge/decisions/internals/flag-sync-rewrites-only-the-declared-text.md +0 -24
- package/knowledge/decisions/internals/gateway-adapters-resolve-lazily-and-are-promise-cached.md +0 -32
- package/knowledge/decisions/internals/gateway-listener-middleware-runs-without-an-rpc-on-the-wire.md +0 -43
- package/knowledge/decisions/internals/gateway-webhook-challenges-echo-bytes-not-json.md +0 -28
- package/knowledge/decisions/internals/gateway-wiring-is-a-meta-wiring-over-http-and-channels.md +0 -31
- package/knowledge/decisions/internals/generated-src-paths-in-pikku-meta-are-absolute.md +0 -26
- package/knowledge/decisions/internals/global-middleware-resolves-from-the-root-namespace-too.md +0 -32
- package/knowledge/decisions/internals/hot-reload-reads-the-changed-source-never-a-compiled-copy.md +0 -38
- package/knowledge/decisions/internals/hot-reload-writes-into-the-function-map-captured-at-startup.md +0 -26
- package/knowledge/decisions/internals/http-request-bodies-are-read-once-and-shared.md +0 -32
- package/knowledge/decisions/internals/http-route-groups-cascade-config-in-a-fixed-order.md +0 -28
- package/knowledge/decisions/internals/http-router-matches-normalized-paths-but-returns-registered-ones.md +0 -30
- package/knowledge/decisions/internals/http-runner-logs-through-a-trace-scoped-logger-functions-do-not.md +0 -26
- package/knowledge/decisions/internals/http-set-cookie-headers-are-appended-never-joined.md +0 -28
- package/knowledge/decisions/internals/http-sse-streams-flush-headers-only-after-middleware.md +0 -32
- package/knowledge/decisions/internals/http-wiring-without-metadata-is-skipped-not-fatal.md +0 -26
- package/knowledge/decisions/internals/in-a-scenario-a-4xx-is-data-not-an-exception.md +0 -25
- package/knowledge/decisions/internals/in-memory-workflow-history-aliases-the-live-step-object.md +0 -26
- package/knowledge/decisions/internals/index.md +0 -182
- package/knowledge/decisions/internals/istanbul-statement-counts-attach-to-the-start-line-only.md +0 -25
- package/knowledge/decisions/internals/local-trigger-and-gateway-services-assume-a-single-process.md +0 -26
- package/knowledge/decisions/internals/mcp-wire-names-are-assigned-over-the-whole-registry.md +0 -39
- package/knowledge/decisions/internals/node-only-builtins-are-imported-dynamically.md +0 -24
- package/knowledge/decisions/internals/one-project-shape-check-two-validators.md +0 -53
- package/knowledge/decisions/internals/only-exposed-functions-enter-a-virtual-user-catalogue.md +0 -21
- package/knowledge/decisions/internals/queue-group-concurrency-keeps-one-shared-queue-fair.md +0 -28
- package/knowledge/decisions/internals/queue-jobs-always-carry-an-explicit-attempts-count.md +0 -27
- package/knowledge/decisions/internals/remote-addons-dispatch-over-http-instead-of-local-meta.md +0 -31
- package/knowledge/decisions/internals/role-names-cannot-contain-the-scope-separator.md +0 -30
- package/knowledge/decisions/internals/rpc-names-resolve-through-package-scope-before-root.md +0 -32
- package/knowledge/decisions/internals/scenario-agent-calls-sign-in-on-401-only.md +0 -31
- package/knowledge/decisions/internals/scenario-given-and-when-are-sugar-but-then-is-not.md +0 -24
- package/knowledge/decisions/internals/scenario-meta-lives-apart-from-app-meta-but-merges-when-read-off-disk.md +0 -26
- package/knowledge/decisions/internals/scenario-steps-return-drained-response-records.md +0 -27
- package/knowledge/decisions/internals/scenarios-live-in-files-named-for-them.md +0 -48
- package/knowledge/decisions/internals/scope-roots-may-be-co-declared-by-an-addon-and-its-host-app.md +0 -30
- package/knowledge/decisions/internals/serverless-channel-disconnect-must-tolerate-a-missing-channel.md +0 -28
- package/knowledge/decisions/internals/side-effects-are-an-allowlist-not-a-boolean.md +0 -34
- package/knowledge/decisions/internals/speech-synthesis-picks-a-voice-per-sentence-and-warns-once.md +0 -24
- package/knowledge/decisions/internals/the-agent-done-event-goes-through-the-middleware-and-is-awaited.md +0 -26
- package/knowledge/decisions/internals/the-api-report-pins-members-not-just-names.md +0 -43
- package/knowledge/decisions/internals/the-core-scope-validator-refuses-separator-wildcard-and-empty-segments.md +0 -22
- package/knowledge/decisions/internals/the-dev-queue-copies-prod-timing-and-serialization-semantics.md +0 -30
- package/knowledge/decisions/internals/the-ecosystem-entry-point-carries-the-adapter-surface.md +0 -83
- package/knowledge/decisions/internals/the-embedding-model-is-pinned-per-service-and-doc-query-embedding-is-split.md +0 -29
- package/knowledge/decisions/internals/the-in-memory-workflow-service-is-inline-only-and-single-process.md +0 -27
- package/knowledge/decisions/internals/the-kek-salt-is-scoped-to-the-key-version.md +0 -40
- package/knowledge/decisions/internals/the-mcp-handshake-is-challenged-only-when-every-target-is-gated.md +0 -29
- package/knowledge/decisions/internals/the-middleware-resolution-cache-is-deliberately-unbounded.md +0 -40
- package/knowledge/decisions/internals/the-per-invocation-rpc-view-is-a-class.md +0 -40
- package/knowledge/decisions/internals/the-pikku-user-id-resolves-in-a-fixed-order.md +0 -24
- package/knowledge/decisions/internals/the-runner-resolves-wire-services-itself.md +0 -31
- package/knowledge/decisions/internals/the-schema-service-is-never-stubbed.md +0 -26
- package/knowledge/decisions/internals/the-transcript-event-is-sent-ahead-of-the-run.md +0 -25
- package/knowledge/decisions/internals/the-virtual-user-catalogue-is-the-only-gate-on-what-may-be-called.md +0 -21
- package/knowledge/decisions/internals/the-virtual-user-prompt-says-json-because-of-json-object-mode.md +0 -22
- package/knowledge/decisions/internals/the-virtual-user-runtime-is-exported-from-the-virtual-user-entry-point.md +0 -28
- package/knowledge/decisions/internals/the-worker-disposition-is-the-one-that-is-not-testing.md +0 -22
- package/knowledge/decisions/internals/thread-history-records-the-transcript-not-the-audio.md +0 -25
- package/knowledge/decisions/internals/trigger-declaration-is-split-from-trigger-source.md +0 -33
- package/knowledge/decisions/internals/typed-secret-service-caches-for-the-process-lifetime.md +0 -26
- package/knowledge/decisions/internals/validate-checks-virtual-users-through-a-shared-module.md +0 -43
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +0 -115
- package/knowledge/decisions/internals/virtual-user-conversation-drives-the-target-through-a-transport-seam.md +0 -24
- package/knowledge/decisions/internals/virtual-user-conversation-verdicts-are-llm-self-evaluations.md +0 -25
- package/knowledge/decisions/internals/virtual-user-conversations-seed-a-hidden-kickoff-message.md +0 -23
- package/knowledge/decisions/internals/virtual-user-step-order-comes-from-insertion-order.md +0 -21
- package/knowledge/decisions/internals/virtual-users-are-declared-in-one-definevirtualusers-call-per-codebase.md +0 -30
- package/knowledge/decisions/internals/voice-output-speaks-unless-voice-input-explicitly-says-otherwise.md +0 -24
- package/knowledge/decisions/internals/webhook-delivery-history-records-every-attempt-best-effort.md +0 -26
- package/knowledge/decisions/internals/webhook-service-collaborators-are-constructor-args-not-locator-lookups.md +0 -25
- package/knowledge/decisions/internals/whether-a-run-is-inline-is-read-from-the-run-record.md +0 -58
- package/knowledge/decisions/internals/wiring-registries-erase-the-generics-their-wire-functions-capture.md +0 -35
- package/knowledge/decisions/internals/workflow-approval-expiry-is-decided-from-a-recorded-deadline.md +0 -34
- package/knowledge/decisions/internals/workflow-core-never-imports-a-browser-driver.md +0 -42
- package/knowledge/decisions/internals/workflow-dsl-meta-separates-runtime-expressions-from-literals.md +0 -38
- package/knowledge/decisions/internals/workflow-features-resolve-scenarios-by-object-identity.md +0 -29
- package/knowledge/decisions/internals/workflow-graph-inline-and-queued-runs-share-one-planner.md +0 -42
- package/knowledge/decisions/internals/workflow-graph-node-notes-are-excluded-from-the-graph-hash.md +0 -25
- package/knowledge/decisions/internals/workflow-inline-runs-report-their-run-id-before-they-can-fail.md +0 -29
- package/knowledge/decisions/internals/workflow-invocation-id-is-the-dedupe-key-not-step-id.md +0 -43
- package/knowledge/decisions/internals/workflow-queued-step-dispatch-requires-an-explicit-opt-in.md +0 -29
- package/knowledge/decisions/internals/workflow-queues-are-per-workflow-by-default.md +0 -42
- package/knowledge/decisions/internals/workflow-repeated-step-names-get-an-ordinal-suffix.md +0 -33
- package/knowledge/decisions/internals/workflow-replay-reads-its-steps-once-and-caches-only-the-immutable-half.md +0 -32
- package/knowledge/decisions/internals/workflow-retries-are-owned-by-the-workflow-not-the-queue.md +0 -31
- package/knowledge/decisions/internals/workflow-run-capabilities-are-extensions-not-subclasses.md +0 -39
- package/knowledge/decisions/internals/workflow-run-mirror-is-never-a-source-of-truth.md +0 -29
- package/knowledge/decisions/internals/workflow-run-polling-backs-off-to-the-callers-ceiling.md +0 -33
- package/knowledge/decisions/internals/workflow-run-timeline-is-a-pure-fold-over-durable-history.md +0 -37
- package/knowledge/decisions/internals/workflow-scenario-assertions-never-retry-and-record-one-step.md +0 -54
- package/knowledge/decisions/internals/workflow-scenario-hooks-are-a-scenario-only-affordance.md +0 -43
- package/knowledge/decisions/internals/workflow-scenario-prose-is-rendered-from-typed-calls-not-parsed-from-english.md +0 -32
- package/knowledge/decisions/internals/workflow-scenario-quarantine-reason-lives-in-code.md +0 -18
- package/knowledge/decisions/internals/workflow-scenario-step-targets-are-string-literals-for-the-inspector.md +0 -34
- package/knowledge/decisions/internals/workflow-step-compensation-runs-as-its-own-durable-step.md +0 -29
- package/knowledge/decisions/internals/workflow-step-dispatch-failure-is-transient-not-a-run-failure.md +0 -33
- package/knowledge/decisions/internals/workflow-step-lock-is-held-only-to-claim-the-step.md +0 -34
- package/knowledge/decisions/internals/workflow-step-rpc-name-is-provenance-only.md +0 -34
- package/knowledge/decisions/internals/workflow-suspend-and-approval-reasons-are-durable-step-identities.md +0 -38
- package/knowledge/decisions/internals/workflow-suspended-runs-keep-their-in-process-context.md +0 -30
- package/knowledge/decisions/security/a-bearer-token-checked-against-a-secret-reads-it-per-request.md +0 -26
- package/knowledge/decisions/security/a-call-without-a-session-raises-missing-session-not-forbidden.md +0 -20
- package/knowledge/decisions/security/a-caller-cookie-wins-over-the-jar-for-the-same-name.md +0 -23
- package/knowledge/decisions/security/a-channel-lifecycle-acts-as-the-socket-upgrade-session.md +0 -16
- package/knowledge/decisions/security/a-channel-requires-a-session-unless-it-says-auth-false.md +0 -26
- package/knowledge/decisions/security/a-credential-read-path-follows-its-declared-type-not-absence.md +0 -32
- package/knowledge/decisions/security/a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md +0 -27
- package/knowledge/decisions/security/a-dropped-audit-write-is-always-logged.md +0 -28
- package/knowledge/decisions/security/a-first-party-cookie-is-written-only-after-its-purposes-consent.md +0 -33
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +0 -37
- package/knowledge/decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md +0 -33
- package/knowledge/decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md +0 -33
- package/knowledge/decisions/security/a-public-function-says-so-with-auth-false.md +0 -34
- package/knowledge/decisions/security/a-readonly-session-reaches-only-readonly-functions.md +0 -25
- package/knowledge/decisions/security/a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md +0 -52
- package/knowledge/decisions/security/a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md +0 -27
- package/knowledge/decisions/security/a-scoped-credential-view-is-limited-to-declared-names.md +0 -26
- package/knowledge/decisions/security/a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md +0 -25
- package/knowledge/decisions/security/a-secret-is-sent-only-to-the-hosts-it-declares.md +0 -29
- package/knowledge/decisions/security/a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md +0 -25
- package/knowledge/decisions/security/a-step-runs-the-function-the-workflow-dispatched-it-with.md +0 -37
- package/knowledge/decisions/security/a-stopped-run-by-id-skips-the-ownership-check.md +0 -24
- package/knowledge/decisions/security/a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md +0 -29
- package/knowledge/decisions/security/a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md +0 -30
- package/knowledge/decisions/security/a-virtual-user-environment-fails-closed.md +0 -38
- package/knowledge/decisions/security/a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md +0 -27
- package/knowledge/decisions/security/a-virtual-user-never-runs-against-production.md +0 -23
- package/knowledge/decisions/security/a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md +0 -27
- package/knowledge/decisions/security/a-virtual-user-run-treats-an-unresolved-environment-as-production.md +0 -41
- package/knowledge/decisions/security/a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md +0 -25
- package/knowledge/decisions/security/a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md +0 -32
- package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +0 -50
- package/knowledge/decisions/security/addon-auth-and-tags-only-tighten.md +0 -43
- package/knowledge/decisions/security/addon-config-gates-apply-only-at-the-namespaced-rpc-boundary.md +0 -52
- package/knowledge/decisions/security/addon-scopes-are-resolved-where-the-function-runs.md +0 -46
- package/knowledge/decisions/security/addon-screen-scopes-hide-and-do-not-enforce.md +0 -20
- package/knowledge/decisions/security/agent-approval-forwarding-requires-a-symbol-brand.md +0 -27
- package/knowledge/decisions/security/agent-credential-requests-are-symbol-branded.md +0 -37
- package/knowledge/decisions/security/agent-gate-requires-a-session-only-when-auth-is-true.md +0 -31
- package/knowledge/decisions/security/agent-ownership-failures-never-echo-the-resource.md +0 -23
- package/knowledge/decisions/security/agent-resume-re-runs-the-authorization-gate.md +0 -22
- package/knowledge/decisions/security/agent-sessionless-deployments-have-no-thread-ownership.md +0 -39
- package/knowledge/decisions/security/agent-thread-ownership-composes-the-session-principal.md +0 -30
- package/knowledge/decisions/security/agent-tool-filtering-reads-the-live-function-config.md +0 -24
- package/knowledge/decisions/security/an-addon-header-can-only-narrow-the-session.md +0 -26
- package/knowledge/decisions/security/an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md +0 -33
- package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md +0 -16
- package/knowledge/decisions/security/an-agent-approval-is-claimed-before-the-tool-runs.md +0 -33
- package/knowledge/decisions/security/an-api-key-is-jwt-decoded-into-the-session.md +0 -26
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +0 -59
- package/knowledge/decisions/security/an-empty-owners-constraint-matches-nothing.md +0 -30
- package/knowledge/decisions/security/an-exposed-ungated-function-is-a-codegen-warning.md +0 -52
- package/knowledge/decisions/security/an-external-virtual-user-caller-presents-one-signed-statement.md +0 -38
- package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md +0 -31
- package/knowledge/decisions/security/an-mcp-call-with-no-http-request-stays-anonymous.md +0 -18
- package/knowledge/decisions/security/an-mcp-refusal-is-decided-before-dispatch.md +0 -48
- package/knowledge/decisions/security/an-oauth2-credential-is-read-by-its-account-row.md +0 -40
- package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md +0 -32
- package/knowledge/decisions/security/an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md +0 -24
- package/knowledge/decisions/security/analytics-identity-is-stamped-from-the-session-not-the-event.md +0 -24
- package/knowledge/decisions/security/app-access-is-a-scope-tree-synthesised-from-virtual-users.md +0 -26
- package/knowledge/decisions/security/approval-policy-decides-who-answers-a-tool-call.md +0 -27
- package/knowledge/decisions/security/auth-false-on-a-session-function-warns-and-still-requires-one.md +0 -25
- package/knowledge/decisions/security/bearer-token-hashes-are-unpeppered-sha256.md +0 -18
- package/knowledge/decisions/security/consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md +0 -29
- package/knowledge/decisions/security/console-addon-privileged-functions-gate-themselves.md +0 -84
- package/knowledge/decisions/security/core-safe-fetch-blocks-ssrf-by-host-literal-not-dns.md +0 -39
- package/knowledge/decisions/security/core-secrets-use-a-per-secret-dek-wrapped-by-a-kek.md +0 -37
- package/knowledge/decisions/security/cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md +0 -22
- package/knowledge/decisions/security/credential-backends-share-one-conformance-suite-for-per-user-isolation.md +0 -29
- package/knowledge/decisions/security/each-stage-has-one-virtual-user-key-pair.md +0 -27
- package/knowledge/decisions/security/gateway-handlers-run-through-the-function-runner-gate.md +0 -41
- package/knowledge/decisions/security/gateway-middleware-sessions-must-be-bridged-onto-the-wire.md +0 -30
- package/knowledge/decisions/security/global-permissions-and-function-permissions-are-independent-gates.md +0 -40
- package/knowledge/decisions/security/hmac-verification-fails-closed-on-empty-inputs.md +0 -25
- package/knowledge/decisions/security/http-error-detail-is-withheld-from-clients-in-production.md +0 -33
- package/knowledge/decisions/security/http-request-bodies-are-bounded-before-they-are-buffered.md +0 -46
- package/knowledge/decisions/security/index.md +0 -135
- package/knowledge/decisions/security/local-content-refuses-private-buckets-under-the-public-segment.md +0 -21
- package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md +0 -38
- package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md +0 -27
- package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md +0 -18
- package/knowledge/decisions/security/origin-locked-routes-match-the-origin-exactly.md +0 -22
- package/knowledge/decisions/security/oss-auth-works-without-pikku-studio-cloud.md +0 -26
- package/knowledge/decisions/security/outbound-webhooks-use-an-allowlist-when-configured.md +0 -24
- package/knowledge/decisions/security/passphrases-are-stretched-key-material-is-expanded.md +0 -40
- package/knowledge/decisions/security/permission-auth-filtering-requires-live-permission-functions.md +0 -31
- package/knowledge/decisions/security/pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md +0 -35
- package/knowledge/decisions/security/queue-job-identities-are-signed-at-enqueue.md +0 -69
- package/knowledge/decisions/security/queue-jobs-carry-the-producers-pikku-user-id.md +0 -39
- package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md +0 -34
- package/knowledge/decisions/security/scaffold-features-are-authenticated-unless-opted-out.md +0 -55
- package/knowledge/decisions/security/scenario-step-functions-are-never-externally-invocable.md +0 -30
- package/knowledge/decisions/security/scope-resolution-happens-at-the-session-boundary-and-sync-never-deletes.md +0 -28
- package/knowledge/decisions/security/scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md +0 -31
- package/knowledge/decisions/security/self-authentication-is-declared-not-detected.md +0 -34
- package/knowledge/decisions/security/services-without-secrets-get-a-throwing-secrets-accessor.md +0 -22
- package/knowledge/decisions/security/shared-guards-refuse-to-mutate-or-shadow-a-system-role.md +0 -34
- package/knowledge/decisions/security/signed-content-urls-bind-the-request-path.md +0 -37
- package/knowledge/decisions/security/signed-uploads-are-verified-by-the-same-check-as-signed-reads.md +0 -26
- package/knowledge/decisions/security/system-role-scopes-must-be-declared-with-define-scope.md +0 -24
- package/knowledge/decisions/security/system-roles-are-additive-and-immutable.md +0 -16
- package/knowledge/decisions/security/the-channel-rpc-does-not-send-the-callers-session.md +0 -21
- package/knowledge/decisions/security/the-remote-jobs-inbox-fails-closed-without-a-secret.md +0 -24
- package/knowledge/decisions/security/the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md +0 -34
- package/knowledge/decisions/security/the-workflow-status-stream-rechecks-ownership-on-every-poll.md +0 -16
- package/knowledge/decisions/security/two-credentials-on-one-key-must-share-a-read-mode.md +0 -29
- package/knowledge/decisions/security/verified-bearer-claims-are-pass-through-data-that-core-never-fills.md +0 -23
- package/knowledge/decisions/security/virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md +0 -25
- package/knowledge/decisions/security/virtual-user-conversation-missing-approval-decisions-default-to-denied.md +0 -22
- package/knowledge/decisions/security/virtual-user-run-records-are-gated-behind-a-read-scope.md +0 -18
- package/knowledge/decisions/security/virtual-user-sign-in-does-not-exist-in-production.md +0 -25
- package/knowledge/decisions/security/virtual-user-sign-in-has-one-door.md +0 -42
- package/knowledge/decisions/security/virtual-user-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md +0 -29
- package/knowledge/decisions/security/virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md +0 -29
- package/knowledge/decisions/security/webhook-bodies-are-signed-before-they-are-enqueued.md +0 -25
- package/knowledge/decisions/security/wire-addon-expose-selects-the-rpc-surface.md +0 -51
- package/knowledge/decisions/security/workflow-approval-payloads-are-validated-on-replay-inside-the-workflow.md +0 -40
- package/knowledge/decisions/security/workflow-helper-functions-are-sessionless-by-declaration.md +0 -18
- package/knowledge/decisions/security/workflow-queued-steps-rehydrate-their-session-from-the-run-wire.md +0 -32
- package/knowledge/decisions/security/workflow-scenario-sessions-are-isolated-per-virtual-user-and-per-scenario.md +0 -32
- package/knowledge/decisions/security/workflow-scenario-steps-are-never-network-invocable.md +0 -31
- package/knowledge/decisions/security/workflow-virtual-user-steps-always-use-the-real-transport.md +0 -34
- package/knowledge/decisions/security/writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md +0 -18
- package/knowledge/index.md +0 -24
- package/knowledge/questions/a-bearer-jwt-with-no-identity-becomes-a-session.md +0 -24
- package/knowledge/questions/channel-middleware-accepts-bare-factories-that-nothing-resolves.md +0 -35
- package/knowledge/questions/dev-quick-login-as-impersonation.md +0 -25
- package/knowledge/questions/does-a-delayed-rpc-run-as-the-session-it-was-scheduled-with.md +0 -26
- package/knowledge/questions/does-a-user-id-with-a-colon-read-another-users-credentials.md +0 -27
- package/knowledge/questions/does-a-wirecli-command-need-a-session-by-default.md +0 -26
- package/knowledge/questions/does-an-adversarial-in-scope-success-count-as-a-finding.md +0 -26
- package/knowledge/questions/does-approve-step-need-the-run-owners-session-or-the-gates-approvers.md +0 -34
- package/knowledge/questions/does-cors-origin-true-reflect-any-origin-with-credentials.md +0 -20
- package/knowledge/questions/does-skip-user-session-do-anything-on-a-channel.md +0 -24
- package/knowledge/questions/does-the-store-session-match-the-other-two.md +0 -19
- package/knowledge/questions/index.md +0 -41
- package/knowledge/questions/is-the-agent-score-worker-reachable-without-a-session.md +0 -21
- package/knowledge/questions/live-scheduled-tests-may-write.md +0 -19
- package/knowledge/questions/provision-virtual-users-has-no-caller-in-this-repo.md +0 -23
- package/knowledge/questions/remote-secret-refuses-requests-without-a-remote-token-on-every-path.md +0 -32
- package/knowledge/questions/should-a-middleware-set-session-persist-to-the-store.md +0 -25
- package/knowledge/questions/should-a-scoped-secret-view-throw-from-has-secret-for-a-disallowed-key.md +0 -23
- package/knowledge/questions/should-a-wrong-bearer-token-be-anonymous-or-refused.md +0 -35
- package/knowledge/questions/should-an-empty-webhook-signing-secret-send-unsigned.md +0 -23
- package/knowledge/questions/should-http-virtual-user-sessions-reset-between-scenarios-in-one-run.md +0 -27
- package/knowledge/questions/should-the-cookie-jar-scope-cookies-to-the-api-origin.md +0 -26
- package/knowledge/questions/should-voice-input-fetch-audio-urls-without-a-host-allowlist.md +0 -20
- package/knowledge/questions/staging-impersonation-rules.md +0 -27
- package/knowledge/questions/unauthorized-channel-replies-escape-the-declared-out-type.md +0 -44
- package/knowledge/questions/virtual-user-keys-on-cloud-stages-are-pikku-studios-to-build.md +0 -33
- package/knowledge/questions/virtual-user-statement-replay-is-per-process.md +0 -20
- package/knowledge/questions/what-an-impersonated-session-carries.md +0 -22
- package/run-tests.sh +0 -68
- package/scripts/generate-api-report.d.mts +0 -1
- package/scripts/generate-api-report.mjs +0 -96
- package/scripts/generate-api-report.mts +0 -333
- package/src/public-surface.json +0 -637
- package/src/public-surface.json.README +0 -43
- /package/dist/{function/compensation-name.d.ts → compensation-name.d.ts} +0 -0
- /package/dist/{function/compensation-name.js → compensation-name.js} +0 -0
|
@@ -1,135 +0,0 @@
|
|
|
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 bearer token checked against a secret is read per request, and an unreadable secret means no session](a-bearer-token-checked-against-a-secret-reads-it-per-request.md) — The secretId form of authBearer reads the secret on every request; a missing or failing read leaves the request without a session rather than raising an error
|
|
13
|
-
- [A call without a session raises MissingSessionError, and a missing scope raises MissingScopeError](a-call-without-a-session-raises-missing-session-not-forbidden.md) — The function runner separates the no-session refusal from the scope and permission refusals, so a caller can tell a missing login from a denied grant
|
|
14
|
-
- [A cookie the caller sets wins over the jar's copy of the same name](a-caller-cookie-wins-over-the-jar-for-the-same-name.md) — createCookieJar sends the caller's cookie for a name and drops the jar's copy of that name, so one request never carries the same cookie twice
|
|
15
|
-
- [A channel lifecycle acts as the session the socket upgrade established](a-channel-lifecycle-acts-as-the-socket-upgrade-session.md) — onConnect and onDisconnect run with the userSession captured during the socket upgrade, both in the wire they receive and as the session service passed to runPikkuFunc
|
|
16
|
-
- [A channel requires a session unless it says auth: false](a-channel-requires-a-session-unless-it-says-auth-false.md) — The channel-level requirement is auth !== false, and a message handler's own auth, when it is set, decides for that message in either direction
|
|
17
|
-
- [A credential's read path follows its declared type, never a missing value](a-credential-read-path-follows-its-declared-type-not-absence.md) — The credential wire service reads a wire credential only from the user's store and a singleton only from the deployment, so an absent user value never falls back to the deployment's token
|
|
18
|
-
- [A credential's wire or singleton type is a literal the inspector checks at declaration](a-credential-type-is-a-literal-the-inspector-checks-at-declaration.md) — defineCredential is a no-op read by AST, so its type must be a literal 'wire' or 'singleton' and the inspector refuses any other value
|
|
19
|
-
- [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
|
|
20
|
-
- [A first-party tracking cookie is written only after its purposes are consented](a-first-party-cookie-is-written-only-after-its-purposes-consent.md) — mintCookie writes a new cookie only when every purpose it requires is true in the consent record, and writes nothing on a wire with no HTTP response
|
|
21
|
-
- [A function never receives the secret service](a-function-never-receives-the-secret-service.md) — Every function-, permission- and auth-facing services type is bounded by SecretlessServices, so reaching for `secrets` in a function body is a type error rather than a lint
|
|
22
|
-
- [A graph run starts at an entry node the graph declared](a-graph-run-starts-at-an-entry-node-the-graph-declared.md) — startNode may only name a node in meta.entryNodeIds, and no generated route offers it, because otherwise a caller picks which half of the graph to skip
|
|
23
|
-
- [A permission gets a wire it cannot reply on](a-permission-gets-a-wire-it-cannot-reply-on.md) — The permission wire is typed with Out = never so a permission cannot send on the channel; that narrowing is not a subtype, so the call site asserts
|
|
24
|
-
- [A public function says so with auth: false](a-public-function-says-so-with-auth-false.md) — An exposed sessionless function that writes auth: false is public on purpose and PKU574 stays quiet; one that leaves auth out still warns
|
|
25
|
-
- [A readonly session reaches only functions declared readonly](a-readonly-session-reaches-only-readonly-functions.md) — A session flagged readonly is refused by every function not declared readonly, checked straight after the session is required and before scopes
|
|
26
|
-
- [A scaffold flag says a surface exists, not who may call it](a-scaffold-flag-says-a-surface-exists-not-who-may-call-it.md) — scaffold.<feature> is boolean | { path } — the auth field is gone, because the function runner already enforces each generated function's own auth, wiring, scopes and addon
|
|
27
|
-
- [A scheduled virtual-user run needs no admin at run time](a-scheduled-virtual-user-run-needs-no-admin-at-run-time.md) — An admin may start a virtual-user run, but the run itself never depends on an admin being signed in or on an admin's credential
|
|
28
|
-
- [A scoped credential service is limited to the names its package declared](a-scoped-credential-view-is-limited-to-declared-names.md) — ScopedCredentialService refuses any credential name outside the package's declared set, filters getAll to that set, and refuses to enumerate users
|
|
29
|
-
- [A scoped secret view reads only its declared keys and never writes](a-scoped-secret-view-reads-only-declared-keys-and-never-writes.md) — ScopedSecretService checks every read against an allowed-key set and refuses all writes, unlike the credential service that stays writable
|
|
30
|
-
- [A secret may only be sent to the hosts its declaration names](a-secret-is-sent-only-to-the-hosts-it-declares.md) — assertSecretAllowedForHost refuses a host outside a secret's allowedHosts, and refuses any host at all for a secret with no allowedHosts when the project requires them
|
|
31
|
-
- [A scenario step can name an identity no virtual user holds, through per-call headers](a-step-can-name-an-identity-no-virtual-user-holds-through-per-call-headers.md) — ScenarioInvokeOptions.headers lets a step send an impersonation or shim header on one call, and a virtual-user run never gets that option
|
|
32
|
-
- [A step runs the function the workflow dispatched it with](a-step-runs-the-function-the-workflow-dispatched-it-with.md) — StepState records the step's function name so the worker can reject a queue message naming a different one, because the step executes under the run owner's identity
|
|
33
|
-
- [Stopping an agent run by its id skips the ownership check; interruptAgent is the checked path](a-stopped-run-by-id-skips-the-ownership-check.md) — signalRunInterrupt is exported and performs no authorisation, while interruptAgent asserts the run's resource owner before it signals
|
|
34
|
-
- [A vault secret redacts when serialized and throws when coerced to a string](a-vault-secret-redacts-on-serialization-and-throws-on-coercion.md) — SecretValue serializes to a redacted marker so logs stay honest without crashing, but string coercion throws, and reveal() is the only way to read the value
|
|
35
|
-
- [Virtual users are offered calls by declared scope, and approval-gated calls are denied by default](a-virtual-user-catalogue-entry-is-filtered-by-scope-and-approval-is-denied-by-default.md) — The catalogue type filters by the declared scopes of each RPC rather than by permission functions, and the catalogue filter drops approval-required entries unless the caller opts in
|
|
36
|
-
- [A virtual user's sign-in is refused unless the environment is known and allowed](a-virtual-user-environment-fails-closed.md) — Virtual user provisioning, sync and sign-in refuse when PIKKU_ENV is unresolved, unknown, production without the accountable disposition, or outside the virtual user's environments, and production environments are omitted by default
|
|
37
|
-
- [A virtual user is never offered a scenario, platform or addon step](a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md) — Being able to invoke "the webhook arrives" lets the user manufacture the outcome it exists to discover, which invalidates every finding downstream
|
|
38
|
-
- [A virtual user never runs against production, whatever its disposition](a-virtual-user-never-runs-against-production.md) — startVirtualUserRun and an enabling writeVirtualUserSchedule both refuse with a 403 in production, including for the accountable disposition; an unresolved environment counts as production
|
|
39
|
-
- [A virtual user reads its roles from the app's own RPC before better-auth's user.role](a-virtual-user-reads-its-roles-from-the-apps-own-rpc-before-better-auth.md) — sessionRoles asks getMyScopes first, then get-session, and returns null rather than an empty list when the stage reports no roles
|
|
40
|
-
- [A virtual-user run treats an unresolved PIKKU_ENV as production](a-virtual-user-run-treats-an-unresolved-environment-as-production.md) — The virtual-user scaffold decides "production" from the configured PIKKU_ENV environment, counts an unresolved one as production, and falls back to NODE_ENV only when no environments are configured
|
|
41
|
-
- [A virtual user's scopes are expanded from its roles once, at run preparation](a-virtual-user-scopes-are-expanded-from-its-roles-once-at-run-preparation.md) — A virtual user's scope set is computed from its declared roles against the defineSystemRole definitions when the run is prepared, and that set is the oracle for scope drift
|
|
42
|
-
- [A virtual user whose granted roles drift from its declaration refuses to run](a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md) — verifyVirtualUserRoles compares declared and granted roles in both directions before a run, and roleMismatchMessage produces the refusal text; a provider login is never runnable
|
|
43
|
-
- [A workflow run is read by its owner, and answered by whoever its gate declares](a-workflow-run-is-read-and-approved-by-its-owner.md) — A run started through a session records that user and only that user may read it; who may answer an approval gate is the gate's own declaration, and a run with no recorded owner has no ownership to enforce
|
|
44
|
-
- [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
|
|
45
|
-
- [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
|
|
46
|
-
- [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
|
|
47
|
-
- [Addon screen scopes hide a screen and do not enforce access](addon-screen-scopes-hide-and-do-not-enforce.md) — The scopes on a defineScreens entry state what a viewer must hold to see the screen; the addon's own functions are the access check
|
|
48
|
-
- [Only a Symbol-branded framework result can request tool approval](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
|
|
49
|
-
- [Credential requests are trusted only when Symbol-branded](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
|
|
50
|
-
- [An agent requires a session only when auth is true, but always enforces scopes and permissions](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
|
|
51
|
-
- [An agent ownership failure never echoes the resource it refused](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
|
|
52
|
-
- [Resuming a suspended agent run re-runs the agent's authorization gate](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
|
|
53
|
-
- [Agent thread ownership fails closed when there is no principal](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
|
|
54
|
-
- [An agent thread key is always prefixed with the trusted principal](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
|
|
55
|
-
- [Agent tool permission filtering reads the live function config, not the metadata](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
|
|
56
|
-
- [An addon header can only narrow the session, and an unknown addon is refused](an-addon-header-can-only-narrow-the-session.md) — addon-scope-cap intersects the session's scopes with the named addon's role, so naming an addon never widens access, and a name the host does not know is a 403
|
|
57
|
-
- [An addon instance reads only the secrets and credentials it is named](an-addon-instance-reads-only-the-secrets-and-credentials-it-is-named.md) — An addon's secret and credential views are its declared names plus the consuming app's grants and override keys; the global flags opt out of scoping
|
|
58
|
-
- [An addon may reach only itself and the addons it declares in uses](an-addon-may-reach-only-itself-and-the-addons-it-uses.md) — An RPC call that arrives tagged with an addon header is refused unless the target is the caller or one of the caller's declared uses, and an unknown addon name is refused outright
|
|
59
|
-
- [An agent approval is claimed before the tool runs](an-agent-approval-is-claimed-before-the-tool-runs.md) — resolveApproval is a compare-and-swap returning whether this caller won, because the read that precedes it is not a claim and ten concurrent approvals would otherwise mean ten refunds
|
|
60
|
-
- [An API key is JWT-decoded into the session, from a header or a query parameter](an-api-key-is-jwt-decoded-into-the-session.md) — authAPIKey reads x-api-key or the apiKey query parameter, decodes it as a JWT, and sets the session only when no session exists yet
|
|
61
|
-
- [An approval answer outlives the run it answered](an-approval-answer-outlives-the-run-it-answered.md) — Run state holds a decision only while the gate is open, so the settled answer carries decidedBy/decidedAt into the step result and every attempt is written to the audit sink, which has no foreign key to the run
|
|
62
|
-
- [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
|
|
63
|
-
- [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
|
|
64
|
-
- [An external virtual user caller presents one signed statement](an-external-virtual-user-caller-presents-one-signed-statement.md) — Callers outside the app sign an EdDSA statement naming the virtual user; the app verifies it against a public key and accepts each statement once
|
|
65
|
-
- [An inbound webhook source dispatches only requests verified against its credential](an-inbound-webhook-source-dispatches-only-verified-requests.md) — A webhook source with verify refuses a body-carrying request that fails its check, fails closed on an empty secret, and does not dispatch events from a body-less request
|
|
66
|
-
- [An MCP call with no HTTP request stays anonymous by transport design](an-mcp-call-with-no-http-request-stays-anonymous.md) — MCP calls that arrive without an HTTP request, such as stdio, reach the function with no session because auth middleware reads the session only from the request
|
|
67
|
-
- [An MCP refusal is decided before dispatch, not raised from the tool](an-mcp-refusal-is-decided-before-dispatch.md) — mcpTargetRequiresSession lets the transport answer 401 with an OAuth challenge, because the MCP response streams and the status is gone by the time the runner refuses
|
|
68
|
-
- [An OAuth2 credential is read by its account row, not by its provider name](an-oauth2-credential-is-read-by-its-account-row.md) — better-auth 1.7.5 selects an account by row id under a strict body schema, so BetterAuthCredentialService resolves the row through the internal adapter before asking for a token
|
|
69
|
-
- [An omitted userId on CredentialService addresses the platform credential](an-omitted-user-id-addresses-the-platform-credential.md) — The CredentialService contract reads and writes the platform-level credential whenever a per-credential call omits userId, and the better-auth implementation defaults its platform owner to pikku-platform
|
|
70
|
-
- [An upload is counted as it arrives, not buffered and then measured](an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md) — Reading the whole body before checking its size hands an unauthenticated caller a way to spend the server's memory
|
|
71
|
-
- [Analytics identity is stamped from the session and resolvers, never from the event body](analytics-identity-is-stamped-from-the-session-not-the-event.md) — Each analytics record takes userId and orgId from the wire session, pikkuUserId from the wire, and the rest from the identity resolvers, not from the event payload
|
|
72
|
-
- [App access is a scope tree synthesised from the virtual users that name an app](app-access-is-a-scope-tree-synthesised-from-virtual-users.md) — buildAppScopeDefinition creates one app:<name> scope per app any virtual user names, so which frontend a person may sign into is a runtime-revocable grant rather than a column per app
|
|
73
|
-
- [The approval policy decides who answers a tool call](approval-policy-decides-who-answers-a-tool-call.md) — The conversation approvals option picks either a fixed answer or the virtual user's model for each pending tool call, and a call with no answer is denied
|
|
74
|
-
- [auth false on a session-required function warns and still requires a session](auth-false-on-a-session-function-warns-and-still-requires-one.md) — Writing auth: false on a function that is not sessionless does not open it; the runner logs a warning and still throws MissingSessionError without a session
|
|
75
|
-
- [Bearer token hashes are unpeppered SHA-256](bearer-token-hashes-are-unpeppered-sha256.md) — hashToken stores bearer tokens as plain SHA-256 hex because they are high-entropy random values; a low-entropy secret is not a hashed column
|
|
76
|
-
- [Consent is read from a cookie, and a recognised denial value counts as a refusal](consent-is-read-from-a-cookie-and-a-recognised-denial-counts-as-refusal.md) — cookieAnalyticsIdentity grants a consent purpose only when its cookie is present and not one of a fixed set of denial values, and resolves no identity off a non-HTTP wire
|
|
77
|
-
- [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
|
|
78
|
-
- [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
|
|
79
|
-
- [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
|
|
80
|
-
- [CORS defaults to any origin and refuses the wildcard when credentials are on](cors-defaults-to-any-origin-and-refuses-the-wildcard-with-credentials.md) — The cors middleware's origin defaults to *, and a resolved wildcard origin combined with credentials throws when the middleware is built
|
|
81
|
-
- [Credential backends are checked by one conformance suite that proves per-user isolation](credential-backends-share-one-conformance-suite-for-per-user-isolation.md) — defineCredentialServiceTests asserts that one user's credential is invisible to another and that KEK rotation re-wraps stored values, for any backend that supplies a credential service
|
|
82
|
-
- [Each stage has one virtual user key pair, and the app holds only the public half](each-stage-has-one-virtual-user-key-pair.md) — The deployer holds the private key, the app reads the public key from an unreserved variable, and users never see either
|
|
83
|
-
- [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
|
|
84
|
-
- [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
|
|
85
|
-
- [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
|
|
86
|
-
- [HMAC verification fails closed on empty or malformed inputs](hmac-verification-fails-closed-on-empty-inputs.md) — verifyHmacSignature returns false for an empty secret, a missing signature, an unknown algorithm, an empty key, a malformed hex signature or an empty signature
|
|
87
|
-
- [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
|
|
88
|
-
- [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
|
|
89
|
-
- [Local content refuses private buckets under the public segment and paths outside the root](local-content-refuses-private-buckets-under-the-public-segment.md) — The local content path resolver returns no target for a private bucket whose first segment is the public URL segment, for NUL bytes, and for any path resolving outside its visibility root
|
|
90
|
-
- [Mail reaches a virtual user run only after sender and link-origin allowlisting](mail-is-allowlisted-before-a-virtual-user-run-sees-it.md) — applyMailboxAllowlist keeps only mail from listed senders and drops links to unlisted origins; the engine ships no mailbox, so every implementation must call this helper
|
|
91
|
-
- [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
|
|
92
|
-
- [MCP resources, tools and prompts take sessionless functions, because an MCP client is not a logged-in user](mcp-resources-tools-and-prompts-take-sessionless-functions.md) — The MCP wiring types default each func to CorePikkuFunctionSessionless, so an MCP entry point is declared without a session type; whether a session is required is decided per function by the runner
|
|
93
|
-
- [Origin-locked routes accept only an exact origin match and refuse a missing one](origin-locked-routes-match-the-origin-exactly.md) — requireOrigin rejects a request whose Origin, or Referer when Origin is absent, is not the host's own origin or a configured origin, compared by equality
|
|
94
|
-
- [Open-source auth works without Pikku Studio Cloud](oss-auth-works-without-pikku-studio-cloud.md) — Every virtual-user sign-in path works in an app that runs no Pikku Studio Cloud code; the signing key is the project's own, never a cloud-issued credential
|
|
95
|
-
- [Outbound webhooks use an allowlist when one is configured, and block private hosts otherwise](outbound-webhooks-use-an-allowlist-when-configured.md) — The webhook delivery reads allowedHosts from config and passes it to safeFetch; without an allowlist safeFetch refuses private hosts, and an allowlist replaces that check
|
|
96
|
-
- [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
|
|
97
|
-
- [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
|
|
98
|
-
- [Core carries virtual user roles as data and provisioning grants them](pikku-carries-virtual-user-scopes-as-data-and-the-app-grants-them.md) — roles on a resolved virtual user are transported by core, never applied there — provisionVirtualUsers grants them in the running stage, through the scope service the app wired
|
|
99
|
-
- [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
|
|
100
|
-
- [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
|
|
101
|
-
- [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
|
|
102
|
-
- [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
|
|
103
|
-
- [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
|
|
104
|
-
- [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
|
|
105
|
-
- [Scoped content access is confined to a root folder, with longest-prefix grants outside it](scoped-content-access-is-a-root-folder-with-longest-prefix-grants.md) — ScopedContentService keeps an addon inside its root folder, lets it reach the wider store only through read or write grants matched by longest prefix, and refuses signURL and copyFile
|
|
106
|
-
- [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
|
|
107
|
-
- [Contexts that must not hold secrets get a services bag whose secrets accessor throws](services-without-secrets-get-a-throwing-secrets-accessor.md) — withoutSecrets replaces the secrets entry with a non-enumerable getter that throws, so code outside the permitted contexts cannot reach the secret service
|
|
108
|
-
- [Shared guards refuse to mutate or shadow a system role, in every store](shared-guards-refuse-to-mutate-or-shadow-a-system-role.md) — system-role-guard holds the guards every ScopeService must call before changing a role, and roleLockReason gives the console a reason to show for a locked role
|
|
109
|
-
- [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
|
|
110
|
-
- [Signed uploads are verified by the same check as signed reads](signed-uploads-are-verified-by-the-same-check-as-signed-reads.md) — The local content handler refuses an upload whose presigned URL does not verify, using the same exported verifier that signed reads use
|
|
111
|
-
- [System role scopes must be declared with defineScope, or the build fails](system-role-scopes-must-be-declared-with-define-scope.md) — A system role may grant only scope ids that defineScope declares, and the bare wildcard is refused; both are critical inspector diagnostics, so a role cannot grant a scope no function checks
|
|
112
|
-
- [System roles are never deleted by a deploy, but their scope set follows the declaration, and the admin API cannot change them](system-roles-are-additive-and-immutable.md) — syncSystemRoles keeps a role row whose declaration has gone (marked declared false) but replaces a declared role's scope set; deleteRole and setRoleScopes refuse system roles
|
|
113
|
-
- [The channel RPC does not put the caller's session on the wire](the-channel-rpc-does-not-send-the-callers-session.md) — ChannelDeploymentService.invoke accepts a session only to satisfy the DeploymentService shape and never sends it, so the peer runs the capability as itself
|
|
114
|
-
- [The remote jobs inbox rejects every caller when its secret is unset](the-remote-jobs-inbox-fails-closed-without-a-secret.md) — remoteJobsSecret refuses all requests when the dispatch secret variable is empty, and compares the header in constant time when it is set
|
|
115
|
-
- [The remote RPC gate fails closed and binds a token to one function](the-remote-rpc-gate-fails-closed-and-binds-the-token-to-its-function.md) — pikkuRemoteAuthMiddleware refuses a remote-RPC request unless the mesh token is audience pikku-remote, names the requested function, and carries a session that decrypts under the mesh secret
|
|
116
|
-
- [The workflow status stream re-checks run ownership on every poll and closes when access is lost](the-workflow-status-stream-rechecks-ownership-on-every-poll.md) — streamWorkflowRunStatus asserts the run owner on each poll rather than once when the stream opens, and closes the channel when that check throws
|
|
117
|
-
- [Two credentials resolving to one key must agree on whether they are per-user or singleton](two-credentials-on-one-key-must-share-a-read-mode.md) — buildCredentialResolutions throws when two credentials resolve to the same stored key but would be read in different modes, so one cannot silently serve the other's value
|
|
118
|
-
- [Verified bearer claims are pass-through data that core never fills](verified-bearer-claims-are-pass-through-data-that-core-never-fills.md) — PikkuHTTP.authInfo is a typed slot for claims a host has already verified; core declares it and no core source assigns it from request headers
|
|
119
|
-
- [The virtual user catalogue excludes any RPC scoped under virtualUser](virtual-user-catalogue-excludes-the-machinery-that-runs-virtual-users.md) — deriveCatalogue drops every function whose scope begins with virtualUser, so a virtual user holding those scopes cannot start runs, read findings or set schedules through the catalogue
|
|
120
|
-
- [A virtual user's missing approval decision defaults to denied](virtual-user-conversation-missing-approval-decisions-default-to-denied.md) — Every pending tool call gets an explicit decision; an id the virtual user LLM omitted is denied, so a dropped field can never read as consent
|
|
121
|
-
- [Virtual user run records are privileged, and reads are gated behind their own scope](virtual-user-run-records-are-gated-behind-a-read-scope.md) — The generated read RPCs carry virtualUser:read while starting a run carries virtualUser:run, because adversarial findings are working exploits with live ids
|
|
122
|
-
- [Virtual user sign-in endpoints do not exist in production](virtual-user-sign-in-does-not-exist-in-production.md) — The plugin registers the statement, in-process and switcher endpoints only when the sign-in gate is open at construction, and creating a row is dev-only
|
|
123
|
-
- [Virtual user sign-in has one door, reached two ways](virtual-user-sign-in-has-one-door.md) — Only the app's virtual user sign-in handler creates a virtual user session; runs reach it in-process and everything else reaches it with a signed statement
|
|
124
|
-
- [Virtual user sign-in is proven by Set-Cookie, not a non-empty jar](virtual-user-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md) — HttpVirtualUser tracks its own signedIn flag and both VirtualUserSignIn implementations require the sign-in response itself to set a cookie, because a populated jar proves nothing
|
|
125
|
-
- [Virtual user sign-in only works for virtual-user-flagged users](virtual-user-sign-in-only-works-for-virtual-user-flagged-users.md) — A virtual user statement mints sessions for user rows flagged virtual user and nothing else, so holding one never impersonates a real user
|
|
126
|
-
- [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
|
|
127
|
-
- [wireAddon expose selects the RPC surface, and a list may widen it](wire-addon-expose-selects-the-rpc-surface.md) — The consuming app decides which addon functions rpc.exposed reaches per instance — unset keeps the addon's own expose, false closes the instance, a list names exactly what is callable — and the deploy analyzer applies the same rule
|
|
128
|
-
- [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
|
|
129
|
-
- [Workflow helper functions are registered sessionless by declaration](workflow-helper-functions-are-sessionless-by-declaration.md) — Queue-worker helpers and the workflow sleeper get meta with sessionless true, so the runner requires a session for them only where their own auth flag says so
|
|
130
|
-
- [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
|
|
131
|
-
- [Scenario sessions are isolated per virtual user and reset between scenarios](workflow-scenario-sessions-are-isolated-per-virtual-user-and-per-scenario.md) — One jar per virtual user keeps two virtual users from sharing a session; a browser reset keeps one scenario from leaving the next signed in as somebody else
|
|
132
|
-
- [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 a virtual user's session, so exposing it as an RPC would put that reach on the network
|
|
133
|
-
- [A scenario virtual user step always goes over the real transport and never through internal dispatch](workflow-virtual-user-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
|
|
134
|
-
- [Writing a virtual user schedule is gated by its own scope, separate from running one](writing-a-virtual-user-schedule-is-gated-by-its-own-scope.md) — setVirtualUserSchedule carries virtualUser:schedule, because a schedule write spends money on every future tick while a run spends it once with a caller watching
|
|
135
|
-
<!-- /pikku:knowledge-index -->
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Local content refuses private buckets under the public segment and paths outside the root
|
|
4
|
-
description: The local content path resolver returns no target for a private bucket whose first segment is the public URL segment, for NUL bytes, and for any path resolving outside its visibility root
|
|
5
|
-
tags: core, content, security
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Local content refuses private buckets under the public segment and paths outside the root
|
|
9
|
-
|
|
10
|
-
The path resolver in `packages/core/src/services/local-content.ts:88-104` returns
|
|
11
|
-
`null` (no target) in four cases:
|
|
12
|
-
|
|
13
|
-
- the visibility is not one of the known visibilities (`:92`);
|
|
14
|
-
- the bucket or key contains a NUL byte (`:93`);
|
|
15
|
-
- the visibility is `private` and the bucket's first path segment is
|
|
16
|
-
`PUBLIC_URL_SEGMENT` (`:94-96`);
|
|
17
|
-
- the scoped path or the final target resolves outside the visibility root
|
|
18
|
-
(`:98-102`). `inside` accepts only the root itself or a path under `root + sep`.
|
|
19
|
-
|
|
20
|
-
**What this rules out:** reaching private content through the public URL segment,
|
|
21
|
-
and a bucket or key with `..` or an absolute component escaping the root.
|
package/knowledge/decisions/security/mail-is-allowlisted-before-a-virtual-user-run-sees-it.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Mail reaches a virtual user run only after sender and link-origin allowlisting
|
|
4
|
-
description: applyMailboxAllowlist keeps only mail from listed senders and drops links to unlisted origins; the engine ships no mailbox, so every implementation must call this helper
|
|
5
|
-
tags: core, virtual-users, email, prompt-injection
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Mail reaches a virtual user run only after sender and link-origin allowlisting
|
|
9
|
-
|
|
10
|
-
`packages/core/src/wirings/virtual-user/virtual-user-mailbox.ts` defines the `VirtualUserMailbox`
|
|
11
|
-
interface and the allowlist helpers.
|
|
12
|
-
|
|
13
|
-
The interface doc says implementations "**must** apply `MailboxAllowlist` before
|
|
14
|
-
returning anything", and calls this the primary defence against prompt injection,
|
|
15
|
-
because a virtual user's address can be reached by anyone who learns it
|
|
16
|
-
(`virtual-user-mailbox.ts:40-44`). The engine ships no mailbox: capturing mail is
|
|
17
|
-
platform-specific, so pikku "declares the interface and ships no default"
|
|
18
|
-
(`virtual-user-mailbox.ts:36-38`).
|
|
19
|
-
|
|
20
|
-
`applyMailboxAllowlist` filters by sender and then trims each email's links
|
|
21
|
-
(`virtual-user-mailbox.ts:150-156`). The doc comment says implementations should call
|
|
22
|
-
it rather than filter themselves (`virtual-user-mailbox.ts:146-148`).
|
|
23
|
-
|
|
24
|
-
- Sender: an entry with `@` must match the whole address. A bare domain must
|
|
25
|
-
equal the part after the last `@` (`virtual-user-mailbox.ts:91-101`). The comment
|
|
26
|
-
says a bare domain never matches as a substring (`virtual-user-mailbox.ts:97-98`).
|
|
27
|
-
- Links: with no `linkOrigins`, every link is kept (`virtual-user-mailbox.ts:122-124`).
|
|
28
|
-
Otherwise a link is kept only when its origin is listed, and a link that does not
|
|
29
|
-
parse is dropped (`virtual-user-mailbox.ts:134-140`).
|
|
30
|
-
- Links are filtered rather than the whole email rejected, because a legitimate
|
|
31
|
-
email may carry an unrelated unsubscribe link (`virtual-user-mailbox.ts:109-116`).
|
|
32
|
-
|
|
33
|
-
The raw `body` is optional and is not meant to reach a model context
|
|
34
|
-
(`virtual-user-mailbox.ts:25-29`).
|
|
35
|
-
|
|
36
|
-
**What this rules out:** a virtual user run acting on mail from an unlisted sender, and
|
|
37
|
-
following a link that leaves the origins the stage allowed. Nothing in this file
|
|
38
|
-
checks that an implementation actually calls the helper.
|
package/knowledge/decisions/security/mcp-internal-error-details-are-double-gated-on-production.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
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.
|
package/knowledge/decisions/security/mcp-resources-tools-and-prompts-take-sessionless-functions.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: MCP resources, tools and prompts take sessionless functions, because an MCP client is not a logged-in user
|
|
4
|
-
description: The MCP wiring types default each func to CorePikkuFunctionSessionless, so an MCP entry point is declared without a session type; whether a session is required is decided per function by the runner
|
|
5
|
-
tags: core, mcp, auth, sessionless
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# MCP resources, tools and prompts take sessionless functions, because an MCP client is not a logged-in user
|
|
9
|
-
|
|
10
|
-
`packages/core/src/wirings/mcp/mcp.types.ts` types the three MCP wirings the same way. Each one's `func` defaults to `CorePikkuFunctionSessionless`:
|
|
11
|
-
|
|
12
|
-
- `CoreMCPResource`: default at line 92, and the `func` field is commented "It is sessionless: an MCP client is not a logged-in user" at line 113.
|
|
13
|
-
- `CoreMCPTool`: default at line 123, and the same comment at line 138.
|
|
14
|
-
- `CoreMCPPrompt`: default at line 150, and the same comment at line 163.
|
|
15
|
-
|
|
16
|
-
So an MCP entry point is declared without a session-carrying function type. The types do not say whether a particular MCP call needs a session. The per-function declaration does that: `mcpTargetRequiresSession`, described in [An MCP refusal is decided before dispatch, not raised from the tool](an-mcp-refusal-is-decided-before-dispatch.md), reads a `pikkuFunc` as always needing a session, and a sessionless function as needing one only where it sets `auth: true`.
|
|
17
|
-
|
|
18
|
-
**What this rules out:** typing an MCP wiring's `func` as a session-carrying function, which would make the MCP surface look as if it always has a logged-in user behind it.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Origin-locked routes accept only an exact origin match and refuse a missing one
|
|
4
|
-
description: requireOrigin rejects a request whose Origin, or Referer when Origin is absent, is not the host's own origin or a configured origin, compared by equality
|
|
5
|
-
tags: core, middleware, security
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Origin-locked routes accept only an exact origin match and refuse a missing one
|
|
9
|
-
|
|
10
|
-
`isAllowedOrigin` in `packages/core/src/middleware/require-origin.ts:25-33`
|
|
11
|
-
returns false when the request has no origin (`:30`). It then accepts only an
|
|
12
|
-
equal host origin (`:31`) or an equal configured origin after `toOrigin`
|
|
13
|
-
normalisation (`:32`). There is no suffix or prefix match.
|
|
14
|
-
|
|
15
|
-
The middleware takes `Origin` first and falls back to `Referer` (`:59-61`),
|
|
16
|
-
derives the host origin from `host` and `x-forwarded-proto`, which defaults to
|
|
17
|
-
`https` (`:63-65`), and throws `InvalidOriginError` when the check fails
|
|
18
|
-
(`:72-75`). The doc comment at `:36-42` says this is not CORS: it rejects before
|
|
19
|
-
the function runs, and a missing origin is rejected on purpose.
|
|
20
|
-
|
|
21
|
-
**What this rules out:** matching origins by suffix, and accepting a request that
|
|
22
|
-
carries neither `Origin` nor `Referer`.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Open-source auth works without Pikku Studio Cloud
|
|
4
|
-
description: Every virtual-user sign-in path works in an app that runs no Pikku Studio Cloud code; the signing key is the project's own, never a cloud-issued credential
|
|
5
|
-
tags: services, auth
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Open-source auth works without Pikku Studio Cloud
|
|
9
|
-
|
|
10
|
-
Virtual users and staged impersonation must all work in an open-source app with
|
|
11
|
-
no Pikku Studio Cloud service and no cloud-issued token. The cloud may add its
|
|
12
|
-
own way in, but nothing in core, the CLI or the better-auth service may require
|
|
13
|
-
it.
|
|
14
|
-
|
|
15
|
-
The code holds to this. A virtual user signs in either in-process
|
|
16
|
-
(`inProcessSignIn`, no credential at all) or with a signed statement
|
|
17
|
-
(`statementSignIn`), both in `packages/core/src/services/virtual-user-sign-in.ts`.
|
|
18
|
-
The statement is signed with an Ed25519 key the project owns:
|
|
19
|
-
`packages/cli/src/utils/virtual-user-signer.ts` generates it for `pikku dev`, or
|
|
20
|
-
reads `VIRTUAL_USER_SIGNING_KEY` from whoever deploys the stage. The app is given
|
|
21
|
-
only the public half, as `VIRTUAL_USER_VERIFY_KEY`
|
|
22
|
-
(`packages/services/better-auth/src/virtual-user-plugin.ts`).
|
|
23
|
-
|
|
24
|
-
**What this rules out:** any design where Pikku Studio Cloud is the issuer of the
|
|
25
|
-
credential a virtual user presents. A signer that only exists in the cloud cannot
|
|
26
|
-
be the basis for an open-source feature.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Outbound webhooks use an allowlist when one is configured, and block private hosts otherwise
|
|
4
|
-
description: The webhook delivery reads allowedHosts from config and passes it to safeFetch; without an allowlist safeFetch refuses private hosts, and an allowlist replaces that check
|
|
5
|
-
tags: core, webhooks, ssrf, security
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Outbound webhooks use an allowlist when one is configured, and block private hosts otherwise
|
|
9
|
-
|
|
10
|
-
`WebhookServiceConfig.allowedHosts` in `packages/core/src/services/webhook-service.ts:33-44`
|
|
11
|
-
says that when it is set only those hostnames may be delivered to, and when it is
|
|
12
|
-
omitted private and internal hosts are blocked.
|
|
13
|
-
|
|
14
|
-
`queue-webhook-service.ts:104-110` reads it from `config.webhook`, and falls back
|
|
15
|
-
to no allowlist if the singletons are not initialised. The value is passed to
|
|
16
|
-
`safeFetch` (`:112-122`).
|
|
17
|
-
|
|
18
|
-
In `packages/core/src/utils/safe-fetch.ts:265-273`, an allowlist makes the check an
|
|
19
|
-
exact hostname match, and the private-host refusal is in the `else` branch that only
|
|
20
|
-
runs without an allowlist. So with an allowlist, a listed host is not refused for
|
|
21
|
-
being private.
|
|
22
|
-
|
|
23
|
-
**What this rules out:** delivering to a host outside a configured allowlist, and
|
|
24
|
-
delivering to a private host when no allowlist is configured.
|
|
@@ -1,40 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
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.
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Core carries virtual user roles as data and provisioning grants them
|
|
4
|
-
description: roles on a resolved virtual user are transported by core, never applied there — provisionVirtualUsers grants them in the running stage, through the scope service the app wired
|
|
5
|
-
tags: services
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Core carries virtual user roles as data and provisioning grants them
|
|
9
|
-
|
|
10
|
-
A virtual user's `roles` (and the `app` it signs into) are declared in
|
|
11
|
-
`defineVirtualUsers` and carried on `ResolvedVirtualUser`
|
|
12
|
-
(`packages/core/src/services/virtual-users-service.ts`) into the generated
|
|
13
|
-
`virtualUserConfigs`. Nothing in core reads them to grant anything:
|
|
14
|
-
`createHttpVirtualUsers` (`packages/core/src/services/http-virtual-users.ts`) only
|
|
15
|
-
signs the virtual user in.
|
|
16
|
-
|
|
17
|
-
The grant is performed by `provisionVirtualUsers`
|
|
18
|
-
(`packages/services/better-auth/src/provision-virtual-users.ts`), which runs in the
|
|
19
|
-
stage itself and calls `addUserToRole` / `addScopeToUser` on the `scopeService` the
|
|
20
|
-
app passes in. Its doc comment says it is additive — it creates and grants, never
|
|
21
|
-
deletes or revokes — and that it throws rather than grant a virtual user's roles to
|
|
22
|
-
an address a real user already holds.
|
|
23
|
-
|
|
24
|
-
Core cannot do the granting: which scope store exists, whether roles have been
|
|
25
|
-
created, and what a role means are all the application's own. Worse, a framework
|
|
26
|
-
that granted scopes from wherever it was handed a virtual user would be a framework
|
|
27
|
-
that can escalate privileges as a side effect of signing in. Keeping the fields
|
|
28
|
-
inert in core makes the grant an act performed deliberately, against a store the
|
|
29
|
-
app wired.
|
|
30
|
-
|
|
31
|
-
**What this rules out:** teaching `createHttpVirtualUsers` (or any core service)
|
|
32
|
-
to call `ScopeService.addScopeToUser` / `addUserToRole` from these fields, and
|
|
33
|
-
treating a role listed on a virtual user declaration as evidence that the account
|
|
34
|
-
holds it. That evidence comes from reading the roles back
|
|
35
|
-
([a virtual user whose roles drift from the declaration refuses to run](a-virtual-user-whose-roles-drift-from-the-declaration-refuses-to-run.md)).
|
|
@@ -1,69 +0,0 @@
|
|
|
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]].
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Queue jobs carry the producer's pikku user id
|
|
4
|
-
description: A job's pikkuUserId is trusted as identity by the worker, so enqueue rights are effectively act-as-user rights
|
|
5
|
-
tags: queue
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Queue jobs carry the producer's pikku user id
|
|
9
|
-
|
|
10
|
-
`JobOptions.pikkuUserId` (`packages/core/src/wirings/queue/queue.types.ts`) is
|
|
11
|
-
set by whoever enqueues the job. The adapter surfaces it as
|
|
12
|
-
`QueueJob.pikkuUserId`, `runQueueJob` in
|
|
13
|
-
`packages/core/src/wirings/queue/queue-runner.ts` copies it onto the wire as
|
|
14
|
-
`PikkuQueue.pikkuUserId`, and `resolveSession` in
|
|
15
|
-
`packages/core/src/function/function-runner.ts` then resolves it through
|
|
16
|
-
`defaultPikkuUserIdResolver` and loads that user's session out of the
|
|
17
|
-
`sessionStore`. The worker function runs with that session.
|
|
18
|
-
|
|
19
|
-
The claim is now HMAC-signed at enqueue and verified in `runQueueJob` — see
|
|
20
|
-
[[queue-job-identities-are-signed-at-enqueue]], which supersedes the "any
|
|
21
|
-
producer may name any user" reading below. Everything here still holds for the
|
|
22
|
-
_authorization_ question: signing proves the producer held the deployment
|
|
23
|
-
secret, not that it was entitled to name that user.
|
|
24
|
-
|
|
25
|
-
There is no re-authentication at the worker boundary — the queue payload _is_ the
|
|
26
|
-
credential. Workers run with `auth: false`, so nothing downstream re-checks that
|
|
27
|
-
the producer was entitled to name that user. This is deliberate: the whole point
|
|
28
|
-
is to let a background job resolve the same per-user credentials the original
|
|
29
|
-
request had, without persisting a token in the queue. The cost is that write
|
|
30
|
-
access to the queue is equivalent to being able to act as any user whose session
|
|
31
|
-
is in the store.
|
|
32
|
-
|
|
33
|
-
**What this rules out:** exposing job enqueue to untrusted callers, or letting a
|
|
34
|
-
client-supplied field flow into `JobOptions.pikkuUserId` without an authorization
|
|
35
|
-
check first. It also rules out treating the queue as an authorization boundary —
|
|
36
|
-
do not add a "the worker validated the producer's rights" assumption anywhere
|
|
37
|
-
downstream, and do not drop the id from the wire on the theory that workers are
|
|
38
|
-
unauthenticated anyway. Dropping an id that fails _signature_ verification is a
|
|
39
|
-
different matter and is what the worker now does.
|
package/knowledge/decisions/security/remote-addon-tokens-are-client-credentials-not-mesh-trust.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Remote addon tokens are client credentials, not mesh trust
|
|
4
|
-
description: wireRemoteAddon authenticates as a client to a hosted library and fails closed on an empty token; it never uses PIKKU_REMOTE_SECRET
|
|
5
|
-
tags: rpc
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Remote addon tokens are client credentials, not mesh trust
|
|
9
|
-
|
|
10
|
-
`resolveRemoteAddonToken` in
|
|
11
|
-
`packages/core/src/wirings/rpc/remote-addon-auth.ts` resolves the bearer token a
|
|
12
|
-
`wireRemoteAddon` consumer sends to the host. This is a **client** authenticating
|
|
13
|
-
to a hosted library — it is not pikku's trusted machine-to-machine mesh, which
|
|
14
|
-
uses `PIKKU_REMOTE_SECRET`. Conflating the two would hand a third-party addon
|
|
15
|
-
host a credential that grants mesh-level trust inside the consumer's own
|
|
16
|
-
deployment.
|
|
17
|
-
|
|
18
|
-
The consumer binds the addon's declared auth requirement to one of three local
|
|
19
|
-
sources: `credentialId`, a per-user credential read through `wire.getCredential`
|
|
20
|
-
and therefore scoped to `pikkuUserId`; `secretId`, a platform key from the
|
|
21
|
-
secrets service; or `resolve`, a custom escape hatch. The resolved token is never
|
|
22
|
-
logged or traced. Binding a `credentialId` without a wire that supports
|
|
23
|
-
credentials throws rather than falling back to a shared secret.
|
|
24
|
-
|
|
25
|
-
Resolution fails closed. `null` is returned only when no auth is bound at all,
|
|
26
|
-
which is the addon declaring its remote surface public. Once auth _is_ bound, a
|
|
27
|
-
resolved value of `null`, `undefined` or `''` raises `RemoteAddonAuthError` (401)
|
|
28
|
-
instead of dispatching an unauthenticated request that the host might accept as
|
|
29
|
-
anonymous.
|
|
30
|
-
|
|
31
|
-
**What this rules out:** defaulting to `PIKKU_REMOTE_SECRET` or any ambient
|
|
32
|
-
credential when the bound source comes up empty, treating an empty token as
|
|
33
|
-
"public", and adding the token to log lines, trace headers or error messages for
|
|
34
|
-
debugging.
|