@pikku/core 0.12.143 → 0.12.145
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/cli.types.d.ts +4 -0
- package/dist/wirings/cli/command-parser.d.ts +13 -0
- package/dist/wirings/cli/command-parser.js +16 -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
package/knowledge/decisions/security/agent-sessionless-deployments-have-no-thread-ownership.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Agent thread ownership fails closed when there is no principal
|
|
4
|
-
description: A sessionless caller gets an ephemeral owner and reaches no stored thread, rather than reaching all of them
|
|
5
|
-
tags: agent
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Agent thread ownership fails closed when there is no principal
|
|
9
|
-
|
|
10
|
-
`canAccessThread` returns `false` and `threadOwnerConstraint` returns `[]` when
|
|
11
|
-
the session carries neither `userId` nor `orgId`
|
|
12
|
-
(`packages/core/src/wirings/agent/agent-prepare.ts`).
|
|
13
|
-
`resolveOwnerResourceId` never accepts the client-supplied `resourceId` as an
|
|
14
|
-
ownership key; without a principal it mints an ephemeral `anon-<uuid>` owner
|
|
15
|
-
memoized on the request's params object.
|
|
16
|
-
|
|
17
|
-
Agent wirings default to `auth: false`, so "no session" is the common case
|
|
18
|
-
rather than an exotic one. Treating it as "no ownership model to enforce" made
|
|
19
|
-
every ownership check vacuous: a caller named any `resourceId` and reached that
|
|
20
|
-
thread, and because `undefined` on `AgentRunService.listThreads` means _no
|
|
21
|
-
filter_ rather than _no rows_, a sessionless `getAgentThreads` returned every
|
|
22
|
-
thread in the deployment. The ephemeral owner is what keeps one-shot
|
|
23
|
-
conversations working while denying continuity — it is unguessable, so nothing
|
|
24
|
-
stored can be reached, and it is stable within a request, so a sub-agent
|
|
25
|
-
re-entering `resolveOwnerResourceId` reuses its parent's thread.
|
|
26
|
-
|
|
27
|
-
Memoizing on the params object rather than a module-level Map is what keeps this
|
|
28
|
-
serverless-safe: the identity dies with the request and is never shared between
|
|
29
|
-
instances. `canAccessThread` stays a predicate rather than an assertion so it can
|
|
30
|
-
back a `pikkuPermission` — authorization belongs in a function's `permissions`
|
|
31
|
-
field, never in its body.
|
|
32
|
-
|
|
33
|
-
**What this rules out:** returning `undefined` from `threadOwnerConstraint` for
|
|
34
|
-
any caller; deriving `owners` from request input instead of the session; reading
|
|
35
|
-
`auth: false` as permission to skip ownership on a _stored_ thread; and
|
|
36
|
-
persisting the anonymous owner anywhere, which would turn an ephemeral identity
|
|
37
|
-
into a forgeable one. Cross-request thread continuity without a session is not
|
|
38
|
-
available by design — wire a session to get it back. See
|
|
39
|
-
[[an-empty-owners-constraint-matches-nothing]].
|
package/knowledge/decisions/security/agent-thread-ownership-composes-the-session-principal.md
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An agent thread key is always prefixed with the trusted principal
|
|
4
|
-
description: Ownership keys are composed as principal:resourceId, so a client id can sub-divide its own boundary but never widen it
|
|
5
|
-
tags: agent
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An agent thread key is always prefixed with the trusted principal
|
|
9
|
-
|
|
10
|
-
`resolveOwnerResourceId` in `packages/core/src/wirings/agent/agent-prepare.ts`
|
|
11
|
-
composes the ownership key for a thread or run as `principal:resourceId`. The
|
|
12
|
-
principal is the trusted one — `session.userId` for the default `'user'`
|
|
13
|
-
`SessionScope`, `session.orgId` for `'org'` — and the client-supplied
|
|
14
|
-
`resourceId` is only ever a sub-partition inside it. Composition is idempotent:
|
|
15
|
-
a value already inside the caller's namespace (top-level re-entry, sub-agent
|
|
16
|
-
recursion, resume — all of which pass an already-composite id) is returned
|
|
17
|
-
unchanged rather than composed twice.
|
|
18
|
-
|
|
19
|
-
`'org'` scope with no org on the session throws `ForbiddenError` instead of
|
|
20
|
-
falling back to a bare or shared key, because a shared key leaks threads across
|
|
21
|
-
organizations. A sessionless `'user'` wiring has no trusted principal at all and
|
|
22
|
-
falls back to the requested `resourceId` as a best-effort partition.
|
|
23
|
-
`isOwnedByPrincipal` requires the `:` separator when matching sub-partitions so
|
|
24
|
-
that a principal `alice` does not match a lookalike `alice-evil:…` key.
|
|
25
|
-
|
|
26
|
-
**What this rules out:** storing threads under the client's `resourceId`
|
|
27
|
-
directly; making the composition non-idempotent (resume and sub-agent recursion
|
|
28
|
-
would double-prefix and lose their own threads); testing ownership with a bare
|
|
29
|
-
`startsWith(principal)` that omits the separator; and letting `'org'` scope
|
|
30
|
-
degrade to a shared key when the session carries no organization.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: Agent tool permission filtering reads the live function config, not the metadata
|
|
4
|
-
description: The pikkuAuth brand survives only on live permission objects, so a metadata-driven check would silently admit every gated tool
|
|
5
|
-
tags: agent
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Agent tool permission filtering reads the live function config, not the metadata
|
|
9
|
-
|
|
10
|
-
`buildToolDefs` in `packages/core/src/wirings/agent/agent-prepare.ts`
|
|
11
|
-
decides which tools and sub-agents to expose to the model by calling
|
|
12
|
-
`checkAuthPermissions` against the permission objects held in the live function
|
|
13
|
-
(or agent) config in `pikkuState`, using the metadata only to learn that a
|
|
14
|
-
permission exists at all.
|
|
15
|
-
|
|
16
|
-
The `pikkuAuth` brand that marks a permission as an authorization gate exists
|
|
17
|
-
only on those live objects. The metadata's by-name permission registry is never
|
|
18
|
-
populated, so resolving names through it collects no predicates — and a filter
|
|
19
|
-
that collects no predicates admits everything. The failure is silent: every gated
|
|
20
|
-
tool would be handed to the model for any session.
|
|
21
|
-
|
|
22
|
-
**What this rules out:** simplifying the filter to read
|
|
23
|
-
`fnMeta.permissions` / `subMeta.permissions` directly, and caching a
|
|
24
|
-
name-to-predicate map derived from metadata.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An addon header can only narrow the session, and an unknown addon is refused
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, middleware, scopes, addons
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An addon header can only narrow the session, and an unknown addon is refused
|
|
9
|
-
|
|
10
|
-
`packages/core/src/middleware/addon-scope-cap.ts` reads the `x-pikku-addon`
|
|
11
|
-
header (`addon-scope-cap.ts:8`, `31`). When a session is present, it resolves
|
|
12
|
-
the addon's role and replaces the session's scopes with
|
|
13
|
-
`intersectScopes(session.scopes, role)` (`addon-scope-cap.ts:37-45`).
|
|
14
|
-
|
|
15
|
-
The doc comment says the header "is a selector, not a credential: naming an
|
|
16
|
-
addon can only reduce a session" (`addon-scope-cap.ts:16-17`). An addon the host
|
|
17
|
-
does not know throws `ForbiddenError` (`addon-scope-cap.ts:38-40`), because the
|
|
18
|
-
comment says ignoring it "would let a misspelt name run uncapped"
|
|
19
|
-
(`addon-scope-cap.ts:18-19`).
|
|
20
|
-
|
|
21
|
-
With no header, or no session, the middleware calls `next()` and changes
|
|
22
|
-
nothing (`addon-scope-cap.ts:33-34`). The comment says to register it after the
|
|
23
|
-
session middleware (`addon-scope-cap.ts:21-22`).
|
|
24
|
-
|
|
25
|
-
**What this rules out:** using the header to gain scopes, and treating an
|
|
26
|
-
unrecognised addon name as "no cap".
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An addon instance reads only the secrets and credentials it is named
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, addons, secrets, credentials
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An addon instance reads only the secrets and credentials it is named
|
|
9
|
-
|
|
10
|
-
The consuming app decides which secrets and credentials an addon instance may
|
|
11
|
-
read. The grant fields are declared on the wiring as "set by the consuming app"
|
|
12
|
-
(`packages/core/src/wirings/rpc/rpc-types.ts:73-76`) and carried through
|
|
13
|
-
`wireAddon` (`packages/core/src/wirings/addon/wire-addon.ts:123-126`) into the
|
|
14
|
-
instance (`packages/core/src/wirings/addon/addon-runner.ts:20-22`).
|
|
15
|
-
|
|
16
|
-
The allowed set is built by `allowedNames` in `addon-runner.ts:40-49`: the names
|
|
17
|
-
the package declared, plus the grants, plus the keys of the overrides. An
|
|
18
|
-
override key therefore grants the name; the override value is the key the
|
|
19
|
-
lookup is pointed at. `wire-addon.ts:71` says the grant check runs before
|
|
20
|
-
`secretOverrides` rename a name.
|
|
21
|
-
|
|
22
|
-
Scoping is applied when the instance's services are built
|
|
23
|
-
(`addon-runner.ts:200-227`). Unless `globalSecrets` is set, the secret service
|
|
24
|
-
is wrapped in `ScopedSecretService`; unless `globalCredentials` is set, the
|
|
25
|
-
credential service is wrapped in `ScopedCredentialService`. The global options
|
|
26
|
-
hand over the unscoped service, and their value is a reason string
|
|
27
|
-
(`wire-addon.ts:75-82`). The comments say the reason is recorded in the deploy
|
|
28
|
-
manifest; that recording was not traced here.
|
|
29
|
-
|
|
30
|
-
**What this rules out:** an addon reading a name neither it declared nor the
|
|
31
|
-
host named, including through an override value; and an unscoped service
|
|
32
|
-
reaching an addon without the consuming app setting a `globalSecrets` or
|
|
33
|
-
`globalCredentials` reason.
|
package/knowledge/decisions/security/an-addon-may-reach-only-itself-and-the-addons-it-uses.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An addon may reach only itself and the addons it declares in uses
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, addons, rpc, scopes
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An addon may reach only itself and the addons it declares in uses
|
|
9
|
-
|
|
10
|
-
`addonMayReach` in `packages/core/src/wirings/addon/addon-reach.ts` takes the caller addon and the target function name (`addon-reach.ts:6`). It takes the target from the part of the name before `:` (`addon-reach.ts:7`). It returns `false` for an empty target (`:8`), `true` when the target is the caller (`:9`), and otherwise `true` only when the caller's declared `uses` includes the target (`:10-11`).
|
|
11
|
-
|
|
12
|
-
`addonCallerOf` reads the addon header from the HTTP request (`addon-reach.ts:14-16`). A name that is not a registered addon is refused with `ForbiddenError` (`addon-reach.ts:17-18`).
|
|
13
|
-
|
|
14
|
-
`ContextAwareRPCService.rpcExposed` applies the check before resolving the function. A caller that fails `addonMayReach` gets `AddonNotUsedError` (`packages/core/src/wirings/rpc/rpc-runner.ts:189-192`).
|
|
15
|
-
|
|
16
|
-
**What this rules out:** an addon calling into another addon it does not declare in `uses`, and an addon header naming an unregistered addon being treated as anonymous rather than refused.
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An agent approval is claimed before the tool runs
|
|
4
|
-
description: 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
|
|
5
|
-
tags: agent
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An agent approval is claimed before the tool runs
|
|
9
|
-
|
|
10
|
-
Resuming a suspended agent run read the run, checked `status === 'suspended'`,
|
|
11
|
-
snapshotted `pendingApprovals`, called `resolveApproval`, executed the tool, and
|
|
12
|
-
only then wrote `status: 'running'`. Nothing spanned that sequence — no
|
|
13
|
-
transaction, no row lock, no conditional update — so concurrent approvals of the
|
|
14
|
-
same tool call all observed `suspended`, all snapshotted the same list, and all
|
|
15
|
-
reached `execute`. `resolveApproval` returned `void`, so a loser could not even
|
|
16
|
-
tell.
|
|
17
|
-
|
|
18
|
-
`resolveApproval` is now the claim, and returns whether _this_ caller made it.
|
|
19
|
-
The stores implement it as a compare-and-swap: Kysely updates the run row only
|
|
20
|
-
while `status = 'suspended'` and `pendingApprovals` still equals the list it
|
|
21
|
-
read; the tool-call stores move the row off `approvalStatus = 'pending'` and
|
|
22
|
-
count the rows they changed. Both resume paths run the tool only for the ids they
|
|
23
|
-
claimed, and a caller that claimed nothing gets an error rather than a silent
|
|
24
|
-
re-run.
|
|
25
|
-
|
|
26
|
-
The claim is per tool call, not per run, so concurrent approvals of _different_
|
|
27
|
-
tool calls on one run all proceed — which is the case that made a run-level
|
|
28
|
-
`claimSuspendedRun` the worse fit.
|
|
29
|
-
|
|
30
|
-
**What this rules out:** treating `getRun` as a claim, adding a resume path that
|
|
31
|
-
executes a tool without a `true` from `resolveApproval`, and implementing
|
|
32
|
-
`resolveApproval` as a read-modify-write in any new store — the return value is
|
|
33
|
-
a promise about atomicity, not a convenience.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An API key is JWT-decoded into the session, from a header or a query parameter
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, auth, middleware, api-keys
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An API key is JWT-decoded into the session, from a header or a query parameter
|
|
9
|
-
|
|
10
|
-
`packages/core/src/middleware/auth-apikey.ts` takes an API key from the
|
|
11
|
-
`x-api-key` header, the `apiKey` query parameter, or both, depending on the
|
|
12
|
-
`source` option (`auth-apikey.ts:11-13`, `21-27`). The header is tried first;
|
|
13
|
-
the query is read only when no header value was found (`auth-apikey.ts:25`).
|
|
14
|
-
|
|
15
|
-
The key is decoded with `jwtService.decode` and, if that returns a value, passed
|
|
16
|
-
to `setSession` (`auth-apikey.ts:29-33`). The middleware does nothing when a
|
|
17
|
-
session already exists (`auth-apikey.ts:17`), which the doc comment describes as
|
|
18
|
-
leaving "an existing session alone, so it composes with other auth middleware"
|
|
19
|
-
(`auth-apikey.ts:6-7`).
|
|
20
|
-
|
|
21
|
-
Nothing is decoded when no `jwtService` is wired (`auth-apikey.ts:29`).
|
|
22
|
-
|
|
23
|
-
**What this rules out:** an API key overriding a session that is already set,
|
|
24
|
-
and a key being accepted without a JWT service to verify it. The code also
|
|
25
|
-
accepts a key from the query string (`auth-apikey.ts:26`), so keys can appear in
|
|
26
|
-
URLs when `source` includes `query`.
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An approval answer outlives the run it answered
|
|
4
|
-
description: 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
|
|
5
|
-
tags: workflow, audit
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An approval answer outlives the run it answered
|
|
9
|
-
|
|
10
|
-
An approval gate is asked for so it can be answered for afterwards. "Who
|
|
11
|
-
released the funds" is the entire point of four-eyes sign-off, and until this
|
|
12
|
-
change none of it was kept.
|
|
13
|
-
|
|
14
|
-
Run state is where a decision _waits_, not where it is kept. The record under
|
|
15
|
-
`workflowRuns.state.__approval_<hex>` is overwritten by the next write to that
|
|
16
|
-
key, and cleared outright — `decidedBy: undefined` included — whenever a
|
|
17
|
-
decision is refused on replay. A trail that erases exactly the events worth
|
|
18
|
-
keeping is not a trail.
|
|
19
|
-
|
|
20
|
-
So the answer is recorded twice, in two places with different lifetimes.
|
|
21
|
-
|
|
22
|
-
## The settled decision keeps its provenance
|
|
23
|
-
|
|
24
|
-
`ApprovalOutcome<T>` carries `decidedBy` and `decidedAt` alongside `data`, so
|
|
25
|
-
the answer reaches `workflowStep.result` and, through it,
|
|
26
|
-
`workflowStepHistory` — append-only, and already the per-step event log. Both
|
|
27
|
-
are spread in only when present, so a gate answered without a session keeps the
|
|
28
|
-
shape it had before there was anything to record.
|
|
29
|
-
|
|
30
|
-
## The audit sink is what survives deletion
|
|
31
|
-
|
|
32
|
-
`deleteRun` cascades: `workflowStep` is `onDelete('cascade')` from
|
|
33
|
-
`workflowRuns`, and `workflowStepHistory` from `workflowStep`. Deleting a run
|
|
34
|
-
therefore deletes the sign-off with it — and a _refused_ attempt never reaches a
|
|
35
|
-
step at all, so it was never in that trail to begin with.
|
|
36
|
-
|
|
37
|
-
Every answer — accepted, refused at submission, or cleared on replay — is
|
|
38
|
-
written to the `AuditService` as `workflow.approval.decided`, with
|
|
39
|
-
`outcome: 'success' | 'denied'`, the decider under `userIdentity.pikkuUserId`,
|
|
40
|
-
and the run, reason, scopes and refusal in `metadata`. The `audit` table holds
|
|
41
|
-
no foreign key to any workflow table, which is precisely why it is the right
|
|
42
|
-
home.
|
|
43
|
-
|
|
44
|
-
`auditApprovalDecision`
|
|
45
|
-
(`packages/core/src/wirings/workflow/workflow-approval-audit.ts`) is a module of
|
|
46
|
-
its own rather than a method on `PikkuWorkflowService`: the trail is a separate
|
|
47
|
-
concern from running the workflow, and the service is already at the 2000-line
|
|
48
|
-
limit `source-files-stay-composable.test.ts` enforces.
|
|
49
|
-
|
|
50
|
-
**A sink that fails is logged, never thrown.** The trail must not be the reason a
|
|
51
|
-
decision is lost. A project with no `audit` service wired records nothing and is
|
|
52
|
-
otherwise unaffected — the sink is opt-in, and `auditSchema` is deliberately not
|
|
53
|
-
in `pikkuSchemas`.
|
|
54
|
-
|
|
55
|
-
**What this rules out:** a dedicated `workflowApprovals` table. It would have to
|
|
56
|
-
be implemented in all seven backends and would still be deleted with the run
|
|
57
|
-
unless it deliberately broke the cascade — at which point it is the audit table
|
|
58
|
-
with extra steps. If approvals later need to be _queried_ as a first-class
|
|
59
|
-
entity rather than read back from the trail, that is when to revisit.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An empty owners constraint matches nothing
|
|
4
|
-
description: owners is an authorization boundary, so every storage backend must treat [] as no rows rather than no filter
|
|
5
|
-
tags: agent, storage
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An empty owners constraint matches nothing
|
|
9
|
-
|
|
10
|
-
Every `AgentRunService.listThreads` implementation returns `[]` immediately when
|
|
11
|
-
`owners` is present and empty — `kysely-agent-run-service.ts`,
|
|
12
|
-
`redis-agent-run-service.ts`, `mongodb-agent-run-service.ts`. The conformance
|
|
13
|
-
suite in `packages/core/src/testing/service-tests.ts` pins it, so a new backend
|
|
14
|
-
inherits the requirement instead of rediscovering it.
|
|
15
|
-
|
|
16
|
-
`owners` is an authorization boundary, not an optional filter. The natural way
|
|
17
|
-
to write a query builder is to skip a clause when its list is empty, which
|
|
18
|
-
silently converts "this caller may see nothing" into "this caller may see
|
|
19
|
-
everything" — the failure is invisible in review because the omitted clause
|
|
20
|
-
looks like an optimization. Placing the guard before the query is built, in
|
|
21
|
-
every backend, means the dangerous shape never exists.
|
|
22
|
-
|
|
23
|
-
This is what makes the sessionless `[]` from `threadOwnerConstraint` safe to
|
|
24
|
-
return; the two decisions only hold together.
|
|
25
|
-
|
|
26
|
-
**What this rules out:** folding the empty case into the query builder's normal
|
|
27
|
-
`if (owners)` path; treating `owners: []` as equivalent to `owners: undefined`
|
|
28
|
-
at any layer; and adding a backend without the conformance suite.
|
|
29
|
-
|
|
30
|
-
See [[agent-sessionless-deployments-have-no-thread-ownership]].
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An exposed function with no gate is reported at codegen, not at boot
|
|
4
|
-
description: 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
|
|
5
|
-
tags: authorization, inspector, addon, rpc
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An exposed function with no gate is reported at codegen, not at boot
|
|
9
|
-
|
|
10
|
-
The generated `POST /rpc/:rpcName` dispatcher forwards to `rpc.exposed`, which
|
|
11
|
-
refuses anything without `expose: true`. That makes the reachable set small and
|
|
12
|
-
deliberate — someone typed it. What it does not do is check whether the target
|
|
13
|
-
is gated, because a dispatcher cannot know what it is dispatching to. The
|
|
14
|
-
console shipped ~54 privileged functions through exactly this gap, and nothing
|
|
15
|
-
in the toolchain said so.
|
|
16
|
-
|
|
17
|
-
The check belongs next to `validateAuthSessionless`, in the inspector, for one
|
|
18
|
-
reason: it needs two sources that only meet there.
|
|
19
|
-
|
|
20
|
-
`sessionless` records the baseline — a `pikkuFunc` always requires a session —
|
|
21
|
-
but a `pikkuSessionlessFunc` may still tighten itself with `auth: true`, which
|
|
22
|
-
the inspector previously discarded. It is now recorded on function meta, so
|
|
23
|
-
"sessionless and ungated" is distinguishable from "sessionless and
|
|
24
|
-
self-gated" without running anything.
|
|
25
|
-
|
|
26
|
-
The second source is `wireAddon`. Its `scopes` and `auth` apply to every
|
|
27
|
-
function in the package on every wiring path (see
|
|
28
|
-
[addon scopes](./addon-scopes-are-resolved-where-the-function-runs.md)), so a
|
|
29
|
-
function with no gate of its own may still be fully covered. The inspector
|
|
30
|
-
already parsed `wireAddon` for its endpoint and its secret, variable and
|
|
31
|
-
credential overrides, and dropped `scopes`, `auth` and `tags` — the addon's
|
|
32
|
-
plumbing was recorded in full and its authorization was not. Reading function
|
|
33
|
-
meta alone would have reported every console function as open.
|
|
34
|
-
|
|
35
|
-
An addon whose gates are not statically knowable — a spread, a const reference —
|
|
36
|
-
is recorded as absent, and absent is read as **gated**. This is deliberately the
|
|
37
|
-
wrong way round from how a runtime gate fails: a false negative costs one
|
|
38
|
-
missing warning, while a false positive on a correctly-secured addon costs
|
|
39
|
-
confidence in every warning the tool emits, which is worth more than the one
|
|
40
|
-
case it would catch.
|
|
41
|
-
|
|
42
|
-
Severity is `warn`, not `critical`. `expose: true` on an ungated sessionless
|
|
43
|
-
function is legitimate for a genuinely public endpoint — a health check, a
|
|
44
|
-
sign-up — so this cannot block a build without breaking correct programs. It
|
|
45
|
-
fails a build only under `--fail-on-warn`. And the public endpoint is not left
|
|
46
|
-
warning forever: an explicit `auth: false` on a sessionless function declares it
|
|
47
|
-
public on purpose and silences the check (see
|
|
48
|
-
[public is declared with auth: false](./a-public-function-says-so-with-auth-false.md)).
|
|
49
|
-
|
|
50
|
-
**What this rules out:** checking at boot, where the report arrives after
|
|
51
|
-
deployment; checking in `pikku validate` from `.pikku` JSON, which does not
|
|
52
|
-
carry `wireAddon` at all; and treating an unparseable addon gate as no gate.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An external virtual user caller presents one signed statement
|
|
4
|
-
description: 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
|
|
5
|
-
tags: services
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An external virtual user caller presents one signed statement
|
|
9
|
-
|
|
10
|
-
The statement is an EdDSA (Ed25519) JWT: `sub` is the virtual user id, `aud` is the
|
|
11
|
-
app's origin, `iat`, `exp` a few minutes out (default 180 seconds, ceiling 300), and
|
|
12
|
-
`jti`, a one-time id. The CLI signs it with `node:crypto`; the app verifies it with
|
|
13
|
-
`JoseVirtualUserStatementVerifier` (`packages/services/jose`), `jwtVerify` pinned to
|
|
14
|
-
`algorithms: ['EdDSA']`, against a public key. `POST /sign-in/virtual-user` takes
|
|
15
|
-
`{ statement }`, maps `sub` to a declared virtual user, and signs it in.
|
|
16
|
-
|
|
17
|
-
It is rejected for a wrong algorithm (`none` and HS256 included), a wrong key, a wrong
|
|
18
|
-
audience, expiry, a lifetime beyond the ceiling, a missing `sub` or `jti`, and a `jti`
|
|
19
|
-
already accepted. The replay store is a per-process map of `jti` to expiry, purged on
|
|
20
|
-
each call, so it never outgrows the last few minutes of statements.
|
|
21
|
-
|
|
22
|
-
One statement replaces three older credentials — a secret derived per virtual user from
|
|
23
|
-
a root, an operator token with impersonation, and an app-wired sign-in hook — because
|
|
24
|
-
each of them put something the app could mint a session from in the hands of a caller,
|
|
25
|
-
or in the app's own configuration. A public key lets the app check a statement and
|
|
26
|
-
nothing else; capturing the app's environment yields no ability to sign one.
|
|
27
|
-
|
|
28
|
-
The **id-only dev switcher** (`/sign-in/switch-virtual-user`) is unchanged under
|
|
29
|
-
`pikku dev`. Anywhere else it also needs a `statement` naming the same id, so no
|
|
30
|
-
deployed stage has a credential-free way in.
|
|
31
|
-
|
|
32
|
-
The statement endpoint needs the declared virtual users to map an id to an address, so
|
|
33
|
-
it is registered only when the app passes `virtualUserSignIn.virtualUsers`, which the
|
|
34
|
-
generated `virtualUserList` supplies.
|
|
35
|
-
|
|
36
|
-
**What this rules out:** the app holding a private key, a statement without an
|
|
37
|
-
expiry or one-time id, an algorithm negotiated by the token, and a switcher that works
|
|
38
|
-
on a stage without proof.
|
package/knowledge/decisions/security/an-inbound-webhook-source-dispatches-only-verified-requests.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An inbound webhook source dispatches only requests verified against its credential
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, webhooks, trigger, security
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An inbound webhook source dispatches only requests verified against its credential
|
|
9
|
-
|
|
10
|
-
`verifyRequest` in `packages/core/src/wirings/trigger/webhook-source-runner.ts:173-200`
|
|
11
|
-
decides whether a request counts as signed. A source with no `verify` returns
|
|
12
|
-
true (`:178`). Otherwise the secret is read from the credential named by
|
|
13
|
-
`source.credential`, or from `webhookSecretCredentialName` (`:180-182`).
|
|
14
|
-
|
|
15
|
-
When the secret is missing or empty, a body-less request is treated as
|
|
16
|
-
unverified (`:184`), and a request with a body throws `UnauthorizedError`
|
|
17
|
-
(`:185-187`). `signedWith` also fails closed on an empty secret (`:127-128`).
|
|
18
|
-
The token form compares with `timingSafeStringEqual` (`:153-155`), which is
|
|
19
|
-
defined in `packages/core/src/utils/hmac.ts:57-63`.
|
|
20
|
-
|
|
21
|
-
A mismatch on a request with a body throws (`:199`). A body-less request is not
|
|
22
|
-
an error, but the runner refuses any events it produces: if `verified` is false
|
|
23
|
-
and `receive` returned events, it throws `UnauthorizedError` (`:273-276`). The
|
|
24
|
-
same contract is documented for authors at
|
|
25
|
-
`packages/core/src/wirings/trigger/webhook-source.types.ts:130-135`, and the
|
|
26
|
-
helper that runs the source's steps calls `runPikkuFunc` with `auth: false`
|
|
27
|
-
(`webhook-source-runner.ts:96-101`), so the signature, not a session, is the
|
|
28
|
-
gate.
|
|
29
|
-
|
|
30
|
-
**What this rules out:** dispatching events from a body-carrying request that did
|
|
31
|
-
not verify, and treating an empty signing secret as a valid key.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An MCP call with no HTTP request stays anonymous by transport design
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, mcp, auth, transport
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An MCP call with no HTTP request stays anonymous by transport design
|
|
9
|
-
|
|
10
|
-
`RunMCPEndpointParams.http` in `packages/core/src/wirings/mcp/mcp-runner.ts` is the HTTP request an MCP call arrived on (`mcp-runner.ts:39-41`, field at `:52`).
|
|
11
|
-
|
|
12
|
-
The comment gives the mechanism. Auth middleware reads the session off `wire.http.request`, and every implementation returns early when there is no request (`mcp-runner.ts:42-46`). Without the request, an MCP call reaches the function unauthenticated whatever middleware is registered (`mcp-runner.ts:43-46`).
|
|
13
|
-
|
|
14
|
-
The field is left undefined for transports with no request, such as stdio. The comment says those calls remain anonymous, and that this is a property of the transport rather than a default chosen in the runner (`mcp-runner.ts:48-50`).
|
|
15
|
-
|
|
16
|
-
The surrounding decision about sessionless MCP entry points is in [MCP resources, tools and prompts take sessionless functions](mcp-resources-tools-and-prompts-take-sessionless-functions.md). The refusal for an HTTP call that needs a session is in [an MCP refusal is decided before dispatch](an-mcp-refusal-is-decided-before-dispatch.md).
|
|
17
|
-
|
|
18
|
-
**What this rules out:** treating a request-less MCP call as carrying a session, and filling in a fake HTTP request to make middleware run for a stdio caller.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An MCP refusal is decided before dispatch, not raised from the tool
|
|
4
|
-
description: 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
|
|
5
|
-
tags: mcp, permissions, transport
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An MCP refusal is decided before dispatch, not raised from the tool
|
|
9
|
-
|
|
10
|
-
An MCP client discovers how to authenticate from a `401` carrying a
|
|
11
|
-
`WWW-Authenticate` challenge — that header names the RFC 9728 Protected Resource
|
|
12
|
-
Metadata document, which names the authorization server, which is where OAuth
|
|
13
|
-
starts. Delivered any other way, the refusal is not a refusal: a `MissingSessionError`
|
|
14
|
-
flattened into a JSON-RPC result arrives as `200` with `isError: true`, which a
|
|
15
|
-
client reads as a tool that failed, and no discovery happens.
|
|
16
|
-
|
|
17
|
-
The obvious implementation — catch the refusal in `tools/call` and set the status
|
|
18
|
-
afterwards — cannot work, and not for a reason a test makes obvious. The MCP HTTP
|
|
19
|
-
response **streams**: the entry returns a `Response` whose body the transport is
|
|
20
|
-
still writing when the JSON-RPC handler runs. Instrumenting both ends shows the
|
|
21
|
-
entry observing its own flag as unset *before* the tool ever refuses. Status and
|
|
22
|
-
headers are chosen at the top of the response; by the time anything knows a
|
|
23
|
-
session was missing, they are already on the wire.
|
|
24
|
-
|
|
25
|
-
So the decision moves earlier, to the only two things knowable before dispatch:
|
|
26
|
-
which target the request body names, and whether the request presents anything to
|
|
27
|
-
authenticate with. `mcpTargetRequiresSession` answers the first from the same
|
|
28
|
-
declarations the runner enforces — a `pikkuFunc` always needs a session, a
|
|
29
|
-
`pikkuSessionlessFunc` needs one only where it says `auth: true` — so the
|
|
30
|
-
transport's answer and the runner's cannot disagree. An unregistered name is
|
|
31
|
-
treated as open, because a name nobody registered is a `Method not found`, and
|
|
32
|
-
challenging it invites a client to authenticate its way towards a tool that does
|
|
33
|
-
not exist.
|
|
34
|
-
|
|
35
|
-
This is what makes a single endpoint serve public and private tools together,
|
|
36
|
-
which the MCP spec allows and pikku's per-function `auth` flag already describes.
|
|
37
|
-
`tools/list` stays ungated, so a client can see the menu before it has a token.
|
|
38
|
-
|
|
39
|
-
**What this rules out:** verifying tokens in the transport. Only a request with
|
|
40
|
-
*no* credentials is challenged; a present-but-expired token is dispatched and its
|
|
41
|
-
refusal reaches the client as a tool error. Doing better means resolving the
|
|
42
|
-
session before dispatch, which runs the app's middleware twice per call and puts
|
|
43
|
-
the transport in the business of second-guessing the middleware that owns session
|
|
44
|
-
resolution. The cheaper half — no credentials at all — covers the case that
|
|
45
|
-
actually matters, because that is the state every client starts in.
|
|
46
|
-
|
|
47
|
-
Also ruled out: gating the endpoint as a whole with `requireBearerAuth`. It would
|
|
48
|
-
work, and it would make every public tool private, which is a different product.
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An OAuth2 credential is read by its account row, not by its provider name
|
|
4
|
-
description: 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
|
|
5
|
-
tags: better-auth, credentials, oauth2
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An OAuth2 credential is read by its account row, not by its provider name
|
|
9
|
-
|
|
10
|
-
A pikku OAuth2 credential is named — `user-oauth`, `company-slack` — and that
|
|
11
|
-
name is the better-auth `providerId` its account row carries. Reading the
|
|
12
|
-
credential used to mean handing better-auth the name:
|
|
13
|
-
`getAccessToken({ body: { providerId, userId } })`.
|
|
14
|
-
|
|
15
|
-
better-auth 1.7.5 stopped accepting that. `get-access-token` and
|
|
16
|
-
`unlink-account` both take a strict union selecting the account by its **own row
|
|
17
|
-
id**, so a provider name is not merely ignored, it is rejected — `[body] Invalid
|
|
18
|
-
input`, a 500 on every read of a linked credential: a tool's token, an agent's,
|
|
19
|
-
the console's status card. An agent that cannot resolve its credential never
|
|
20
|
-
makes a model call, so the failure surfaces far from its cause.
|
|
21
|
-
|
|
22
|
-
So the name is resolved to a row first, through
|
|
23
|
-
`context.internalAdapter.findAccounts(userId)` — the same lookup the link
|
|
24
|
-
callback writes the row with and `unlinkAccount` already used. That is also the
|
|
25
|
-
only account lookup that takes a userId at all: `listUserAccounts` resolves the
|
|
26
|
-
*caller's session* and throws UNAUTHORIZED server-side, which is no use for the
|
|
27
|
-
two revocations that have no session — a platform credential, whose owner never
|
|
28
|
-
signs in, and an admin acting on someone else's.
|
|
29
|
-
|
|
30
|
-
A useful consequence: an unlinked provider is now answered before better-auth is
|
|
31
|
-
asked, rather than by catching its `ACCOUNT_NOT_FOUND`. The catch stays, because
|
|
32
|
-
a failed refresh must still not read as "not connected yet" — that would show a
|
|
33
|
-
connect button for an account that is linked but broken.
|
|
34
|
-
|
|
35
|
-
**What this rules out:** treating `providerId` as a credential's address
|
|
36
|
-
anywhere a token is read or revoked. It remains the credential's *name* — what
|
|
37
|
-
an app declares and what the link flow writes — but the row id is what
|
|
38
|
-
better-auth is spoken to in. A fake of better-auth that keys tokens by provider
|
|
39
|
-
name will pass while the real thing rejects every call, which is how this
|
|
40
|
-
survived a bump with 222 green unit tests behind it.
|
package/knowledge/decisions/security/an-omitted-user-id-addresses-the-platform-credential.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An omitted userId on CredentialService addresses the platform credential
|
|
4
|
-
description: 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
|
|
5
|
-
tags: core, credentials, security, interface
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An omitted userId on CredentialService addresses the platform credential
|
|
9
|
-
|
|
10
|
-
`CredentialService` in `packages/core/src/services/credential-service.ts` is the store for dynamic
|
|
11
|
-
credentials such as OAuth tokens and per-user keys. Its doc comment says that throughout the interface,
|
|
12
|
-
an omitted `userId` means the platform-level credential (`packages/core/src/services/credential-service.ts:4`).
|
|
13
|
-
|
|
14
|
-
The per-credential methods take an optional `userId`: `get` (`credential-service.ts:7`), `set` (`:9`),
|
|
15
|
-
`delete` (`:11`) and `has` (`:13`). Omitting it selects the platform credential, so the caller chooses
|
|
16
|
-
the identity by whether it passes one.
|
|
17
|
-
|
|
18
|
-
The methods that enumerate users are different. `getAll` requires `userId` (`credential-service.ts:15`),
|
|
19
|
-
and `getUsersWithCredential` and `getAllUsers` take a credential name or nothing
|
|
20
|
-
(`credential-service.ts:17` and `:19`). They do not have a platform default.
|
|
21
|
-
|
|
22
|
-
The better-auth implementation applies the platform default only to some credentials. It takes an optional
|
|
23
|
-
`platformUserId` that falls back to `PLATFORM_USER_ID` (`packages/services/better-auth/src/better-auth-credential.service.ts:58`).
|
|
24
|
-
Its owner lookup returns null for a name that is not an OAuth2 credential (`better-auth-credential.service.ts:69-70`),
|
|
25
|
-
returns that platform owner only for a singleton OAuth2 name (`better-auth-credential.service.ts:72-73`),
|
|
26
|
-
and otherwise returns the `userId` passed in, or null when none was passed (`better-auth-credential.service.ts:75`).
|
|
27
|
-
So for a non-singleton credential, an omitted `userId` reads as no owner, not as the platform credential. The
|
|
28
|
-
interface comment describes the platform case more widely than this implementation does. Who may link the
|
|
29
|
-
singleton is covered by [a singleton credential belongs to a reserved platform user](../../../../services/better-auth/knowledge/decisions/a-singleton-credential-belongs-to-a-reserved-platform-user.md).
|
|
30
|
-
|
|
31
|
-
**What this rules out:** a sentinel string or an empty value to mean the platform credential, and relying on an
|
|
32
|
-
omitted `userId` to reach the platform credential for a credential that is not a singleton.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: decision
|
|
3
|
-
title: An upload is counted as it arrives, not buffered and then measured
|
|
4
|
-
description: Reading the whole body before checking its size hands an unauthenticated caller a way to spend the server's memory
|
|
5
|
-
tags: core, content
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# An upload is counted as it arrives, not buffered and then measured
|
|
9
|
-
|
|
10
|
-
The local content request handler accumulates the request body chunk by chunk,
|
|
11
|
-
tracking the running total, and abandons the read the moment it crosses the
|
|
12
|
-
limit. An oversized upload therefore costs the limit, not its own size.
|
|
13
|
-
|
|
14
|
-
The obvious alternative — `await request.arrayBuffer()` and then check
|
|
15
|
-
`byteLength` — has to hold the entire body in memory before it can decide the
|
|
16
|
-
body is too large. For an endpoint reachable before authentication that is a
|
|
17
|
-
way to spend the server's memory for the price of one request, and no size limit
|
|
18
|
-
configured anywhere prevents it.
|
|
19
|
-
|
|
20
|
-
`readRequestBody` in the node HTTP server aborts on the same terms, for the same
|
|
21
|
-
reason.
|
|
22
|
-
|
|
23
|
-
**What this rules out:** replacing the streaming accumulation with a single
|
|
24
|
-
buffered read because the limit check "still happens".
|