@xopcai/xopc 0.0.81 → 0.0.83
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +2 -3
- package/dist/extensions/feishu/src/outbound/media-load.js.map +1 -1
- package/dist/extensions/feishu/src/schema/config-schema.d.ts +6 -6
- package/dist/extensions/telegram/src/config-schema.d.ts +6 -6
- package/dist/extensions/telegram/src/plugin.d.ts +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +3 -3
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/config-schema.d.ts +3 -3
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/gateway/static/root/assets/agents-CrpYTHJS.js +222 -0
- package/dist/gateway/static/root/assets/{apps-page-Ci17oA_o.js → apps-page-1mcKh5Rh.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-zd6QNKPx.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-CUU3faST.js → channels-status-swr-uRAuhiUo.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-BVQ2n75R.js → cron-api-O2Q_ruV6.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-x582Y6D5.js → cron-page-By09AQD-.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-XT96cQdR.js → dist-BpQxde0t.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-Czzfrtt5.js → extension-debug-page-CY27wj_p.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-B_c5UIqX.js → extension-page-C-Ed5ZmP.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-Ckvjgw0_.js → extension-settings-page-raLux7E7.js} +1 -1
- package/dist/gateway/static/root/assets/fetch-2iRFmd3n.js +3 -0
- package/dist/gateway/static/root/assets/{field-primitives-DQpT8iVa.js → field-primitives-fa_hiQcX.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-DKqOuQ0V.js → heartbeat-config-api-BVl5VHvL.js} +1 -1
- package/dist/gateway/static/root/assets/index-BuFldCsB.css +1 -0
- package/dist/gateway/static/root/assets/{index-Bq3Lg4bG.js → index-Y-iqo-gL.js} +95 -86
- package/dist/gateway/static/root/assets/{logs-page-B3CwJNBq.js → logs-page-BdH2n7ZW.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Vpchzdp-.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-CjjEpVYM.js → settings-form-section-Kk1yAGBl.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-KBm0u6Dz.js +3 -0
- package/dist/gateway/static/root/assets/skills-page-BjeXXaOn.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-DnwYutiX.js → theme-store-D01dJt95.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-DQehHvlm.js → utils-DpTxN4AF.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-CwO8Cf01.js +1 -0
- package/dist/gateway/static/root/index.html +4 -4
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-instance-gateway.d.ts +50 -0
- package/dist/src/agent/agent-instance-gateway.js +1 -0
- package/dist/src/agent/agent-manager.d.ts +20 -14
- package/dist/src/agent/agent-manager.js +74 -186
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/coordinator.d.ts +61 -0
- package/dist/src/agent/background-review/coordinator.js +120 -0
- package/dist/src/agent/background-review/coordinator.js.map +1 -0
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/child-agent-factory.d.ts +14 -0
- package/dist/src/agent/child-agent-factory.js +2 -8
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/context/workspace-seed.js +3 -3
- package/dist/src/agent/embedded/index.d.ts +1 -2
- package/dist/src/agent/embedded/index.js +2 -3
- package/dist/src/agent/embedded/run-for-session.d.ts +2 -2
- package/dist/src/agent/embedded/run-for-session.js.map +1 -1
- package/dist/src/agent/embedded/runs.d.ts +32 -0
- package/dist/src/agent/embedded/runs.js +79 -19
- package/dist/src/agent/embedded/runs.js.map +1 -1
- package/dist/src/agent/embedded/session-manager-cache.d.ts +14 -0
- package/dist/src/agent/embedded/session-manager-cache.js +32 -11
- package/dist/src/agent/embedded/session-manager-cache.js.map +1 -1
- package/dist/src/agent/embedded/session-runner.d.ts +37 -7
- package/dist/src/agent/embedded/session-runner.js +184 -153
- package/dist/src/agent/embedded/session-runner.js.map +1 -1
- package/dist/src/agent/embedded/session-tool-result-guard.d.ts +57 -9
- package/dist/src/agent/embedded/session-tool-result-guard.js +159 -67
- package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-service.d.ts +84 -0
- package/dist/src/agent/goals/persistent-goal-service.js +139 -0
- package/dist/src/agent/goals/persistent-goal-service.js.map +1 -0
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/goals/state.d.ts +1 -1
- package/dist/src/agent/goals/state.js.map +1 -1
- package/dist/src/agent/image/load-image-media.js +1 -1
- package/dist/src/agent/inbound/inbound-loop.d.ts +77 -0
- package/dist/src/agent/inbound/inbound-loop.js +226 -0
- package/dist/src/agent/inbound/inbound-loop.js.map +1 -0
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +80 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +138 -0
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -0
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.d.ts +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
- package/dist/src/agent/lifecycle/manager.d.ts +1 -1
- package/dist/src/agent/lifecycle/manager.js.map +1 -1
- package/dist/src/agent/lifecycle/types.d.ts +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.d.ts +12 -2
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js.map +1 -1
- package/dist/src/agent/memory/index.js +3 -3
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/memory/prefetch-coordinator.d.ts +37 -0
- package/dist/src/agent/memory/prefetch-coordinator.js +45 -0
- package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -0
- package/dist/src/agent/messaging/command-handler.d.ts +5 -1
- package/dist/src/agent/messaging/command-handler.js +24 -96
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/messaging/index.d.ts +1 -0
- package/dist/src/agent/messaging/index.js +2 -1
- package/dist/src/agent/messaging/message-router.d.ts +1 -1
- package/dist/src/agent/messaging/message-router.js.map +1 -1
- package/dist/src/agent/messaging/outbound-coordinator.d.ts +82 -0
- package/dist/src/agent/messaging/outbound-coordinator.js +123 -0
- package/dist/src/agent/messaging/outbound-coordinator.js.map +1 -0
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-event-handler.d.ts +36 -33
- package/dist/src/agent/orchestration/agent-event-handler.js +212 -174
- package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.d.ts +4 -4
- package/dist/src/agent/orchestration/agent-orchestrator.js +4 -8
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/orchestration/index.d.ts +1 -1
- package/dist/src/agent/orchestration/index.js +2 -2
- package/dist/src/agent/prompt/service-prompt-builder.js +4 -4
- package/dist/src/agent/reply/post-compaction-context.js +1 -1
- package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
- package/dist/src/agent/sandbox/path-policy.js +1 -1
- package/dist/src/agent/service/async-queue.d.ts +20 -0
- package/dist/src/agent/service/async-queue.js +53 -0
- package/dist/src/agent/service/async-queue.js.map +1 -0
- package/dist/src/agent/service/build-direct-message-content.d.ts +2 -2
- package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +70 -0
- package/dist/src/agent/service/direct-turn-helpers.js +90 -0
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -0
- package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -3
- package/dist/src/agent/service/process-direct-one-shot.js +17 -34
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +2 -2
- package/dist/src/agent/service/process-direct-streaming.js +133 -167
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/webchat-tts.d.ts +2 -2
- package/dist/src/agent/service/webchat-tts.js +1 -1
- package/dist/src/agent/service/webchat-tts.js.map +1 -1
- package/dist/src/agent/service.d.ts +62 -167
- package/dist/src/agent/service.js +177 -786
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/session/index.d.ts +4 -0
- package/dist/src/agent/session/index.js +5 -1
- package/dist/src/agent/session/session-config-service.d.ts +68 -0
- package/dist/src/agent/session/session-config-service.js +172 -0
- package/dist/src/agent/session/session-config-service.js.map +1 -0
- package/dist/src/agent/session/session-context.d.ts +27 -19
- package/dist/src/agent/session/session-context.js +39 -24
- package/dist/src/agent/session/session-context.js.map +1 -1
- package/dist/src/agent/session/session-hydrator.d.ts +42 -0
- package/dist/src/agent/session/session-hydrator.js +66 -0
- package/dist/src/agent/session/session-hydrator.js.map +1 -0
- package/dist/src/agent/session/session-inspector.d.ts +80 -0
- package/dist/src/agent/session/session-inspector.js +119 -0
- package/dist/src/agent/session/session-inspector.js.map +1 -0
- package/dist/src/agent/session/session-state-bag.d.ts +83 -0
- package/dist/src/agent/session/session-state-bag.js +192 -0
- package/dist/src/agent/session/session-state-bag.js.map +1 -0
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +2 -2
- package/dist/src/agent/skills/index.d.ts +0 -2
- package/dist/src/agent/skills/index.js +3 -5
- package/dist/src/agent/skills/index.js.map +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/marketplace/adapters/clawhub/adapter.js +11 -6
- package/dist/src/agent/skills/marketplace/adapters/clawhub/adapter.js.map +1 -1
- package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js +35 -7
- package/dist/src/agent/skills/marketplace/adapters/skillhub/adapter.js.map +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +2 -2
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +7 -0
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js +37 -0
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
- package/dist/src/agent/tools/delegate-tool.d.ts +7 -0
- package/dist/src/agent/tools/delegate-tool.js +2 -1
- package/dist/src/agent/tools/delegate-tool.js.map +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/executor.d.ts +34 -15
- package/dist/src/agent/tools/executor.js +44 -79
- package/dist/src/agent/tools/executor.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +6 -0
- package/dist/src/agent/tools/factory.js +63 -4
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/skills-tools.js +1 -1
- package/dist/src/agent/tools/tts-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workspace-runtime/registry.d.ts +48 -0
- package/dist/src/agent/workspace-runtime/registry.js +59 -0
- package/dist/src/agent/workspace-runtime/registry.js.map +1 -0
- package/dist/src/auth/credentials.js +3 -3
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/cdp-local-launcher.js +4 -3
- package/dist/src/browser/cdp-local-launcher.js.map +1 -1
- package/dist/src/browser/index.d.ts +1 -0
- package/dist/src/browser/index.js +2 -1
- package/dist/src/browser/manager.js +3 -2
- package/dist/src/browser/manager.js.map +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +4 -4
- package/dist/src/browser/providers/browser-use.js +2 -1
- package/dist/src/browser/providers/browser-use.js.map +1 -1
- package/dist/src/browser/providers/browserbase.js +2 -1
- package/dist/src/browser/providers/browserbase.js.map +1 -1
- package/dist/src/browser/providers/cloakbrowser.js +7 -6
- package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
- package/dist/src/browser/providers/playwright-doctor.d.ts +2 -0
- package/dist/src/browser/providers/playwright-doctor.js +7 -3
- package/dist/src/browser/providers/playwright-doctor.js.map +1 -1
- package/dist/src/browser/readiness.d.ts +33 -0
- package/dist/src/browser/readiness.js +138 -0
- package/dist/src/browser/readiness.js.map +1 -0
- package/dist/src/browser/stealth.js +2 -2
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/channel-domain.d.ts +1 -1
- package/dist/src/channels/config-helpers.d.ts +1 -1
- package/dist/src/channels/config-helpers.js.map +1 -1
- package/dist/src/channels/heartbeat-scheduler.d.ts +40 -0
- package/dist/src/channels/heartbeat-scheduler.js +94 -0
- package/dist/src/channels/heartbeat-scheduler.js.map +1 -0
- package/dist/src/channels/lifecycle-supervisor.d.ts +81 -0
- package/dist/src/channels/lifecycle-supervisor.js +263 -0
- package/dist/src/channels/lifecycle-supervisor.js.map +1 -0
- package/dist/src/channels/manager.d.ts +34 -68
- package/dist/src/channels/manager.js +107 -477
- package/dist/src/channels/manager.js.map +1 -1
- package/dist/src/channels/outbound/deliver.d.ts +1 -1
- package/dist/src/channels/outbound/deliver.js.map +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/outbound-sender.d.ts +51 -0
- package/dist/src/channels/outbound-sender.js +125 -0
- package/dist/src/channels/outbound-sender.js.map +1 -0
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-service.d.ts +3 -10
- package/dist/src/channels/pairing/pairing-service.js.map +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/channels/pairing/pairing-types.d.ts +15 -0
- package/dist/src/channels/pairing/pairing-types.js +1 -0
- package/dist/src/channels/plugin-registry.d.ts +22 -0
- package/dist/src/channels/plugin-registry.js +44 -0
- package/dist/src/channels/plugin-registry.js.map +1 -0
- package/dist/src/channels/plugin-types.d.ts +1 -1
- package/dist/src/channels/plugins/types.adapters.d.ts +2 -2
- package/dist/src/channels/security-helpers.d.ts +1 -1
- package/dist/src/channels/security-helpers.js.map +1 -1
- package/dist/src/channels/setup-wizard.d.ts +1 -1
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/commands/agent/stream-renderer.js +1 -1
- package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -1
- package/dist/src/cli/commands/agent.js +4 -4
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/browser-cli-helpers.js +2 -1
- package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +2 -2
- package/dist/src/cli/commands/extension-dev.js.map +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +2 -2
- package/dist/src/cli/commands/extension-marketplace.js.map +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/call.js +1 -1
- package/dist/src/cli/commands/gateway/call.js.map +1 -1
- package/dist/src/cli/commands/gateway/health.js +1 -1
- package/dist/src/cli/commands/gateway/health.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.d.ts +31 -12
- package/dist/src/cli/commands/gateway/lifecycle-core.js +167 -116
- package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.d.ts +11 -0
- package/dist/src/cli/commands/gateway/lifecycle.js +102 -0
- package/dist/src/cli/commands/gateway/lifecycle.js.map +1 -0
- package/dist/src/cli/commands/gateway/logs.js +1 -1
- package/dist/src/cli/commands/gateway/logs.js.map +1 -1
- package/dist/src/cli/commands/gateway/probe.js +1 -1
- package/dist/src/cli/commands/gateway/probe.js.map +1 -1
- package/dist/src/cli/commands/gateway/restart-health.d.ts +12 -0
- package/dist/src/cli/commands/gateway/restart-health.js +45 -1
- package/dist/src/cli/commands/gateway/restart-health.js.map +1 -1
- package/dist/src/cli/commands/gateway/restart.js +3 -3
- package/dist/src/cli/commands/gateway/restart.js.map +1 -1
- package/dist/src/cli/commands/gateway/run-foreground.d.ts +0 -1
- package/dist/src/cli/commands/gateway/run-foreground.js +0 -35
- package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
- package/dist/src/cli/commands/gateway/service.js +1 -1
- package/dist/src/cli/commands/gateway/service.js.map +1 -1
- package/dist/src/cli/commands/gateway/shared.d.ts +3 -0
- package/dist/src/cli/commands/gateway/shared.js +54 -0
- package/dist/src/cli/commands/gateway/shared.js.map +1 -0
- package/dist/src/cli/commands/gateway/status.js +1 -1
- package/dist/src/cli/commands/gateway/status.js.map +1 -1
- package/dist/src/cli/commands/gateway/stop.js +2 -2
- package/dist/src/cli/commands/gateway/stop.js.map +1 -1
- package/dist/src/cli/commands/gateway/token.js +1 -1
- package/dist/src/cli/commands/gateway/token.js.map +1 -1
- package/dist/src/cli/commands/gateway.js +5 -5
- package/dist/src/cli/commands/gateway.js.map +1 -1
- package/dist/src/cli/commands/image.js +2 -2
- package/dist/src/cli/commands/image.js.map +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/models.js.map +1 -1
- package/dist/src/cli/commands/onboard/gateway.d.ts +0 -8
- package/dist/src/cli/commands/onboard/gateway.js +48 -49
- package/dist/src/cli/commands/onboard/gateway.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +9 -64
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js +1 -1
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/skills.js +1 -1
- package/dist/src/cli/commands/tailscale.js +1 -1
- package/dist/src/cli/commands/tailscale.js.map +1 -1
- package/dist/src/cli/context.d.ts +20 -0
- package/dist/src/cli/context.js +23 -0
- package/dist/src/cli/context.js.map +1 -0
- package/dist/src/cli/extension-cli-register.js +3 -3
- package/dist/src/cli/gateway-run-argv.js +1 -4
- package/dist/src/cli/gateway-run-argv.js.map +1 -1
- package/dist/src/cli/gateway-run-fast-path.js +1 -1
- package/dist/src/cli/gateway-run-fast-path.js.map +1 -1
- package/dist/src/cli/index.d.ts +1 -7
- package/dist/src/cli/index.js +4 -6
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/config/commands.flags.d.ts +3 -0
- package/dist/src/config/commands.flags.js +11 -0
- package/dist/src/config/commands.flags.js.map +1 -0
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.js +6 -5
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/schema.d.ts +11 -4
- package/dist/src/config/schema.js +13 -12
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/workspace-path-helpers.d.ts +15 -0
- package/dist/src/config/workspace-path-helpers.js +14 -0
- package/dist/src/config/workspace-path-helpers.js.map +1 -0
- package/dist/src/cron/executor.js +4 -4
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/index.d.ts +0 -1
- package/dist/src/daemon/index.js +1 -2
- package/dist/src/daemon/install-plan.js +3 -2
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/daemon/types.d.ts +0 -6
- package/dist/src/extensions/api.d.ts +1 -1
- package/dist/src/extensions/api.js +2 -2
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/bundle-mcp.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/extension-registry-impl.d.ts +51 -0
- package/dist/src/extensions/extension-registry-impl.js +117 -0
- package/dist/src/extensions/extension-registry-impl.js.map +1 -0
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/index.js +3 -2
- package/dist/src/extensions/loader.d.ts +3 -43
- package/dist/src/extensions/loader.js +3 -110
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/extensions/sdk/index.js +2 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/events.d.ts +7 -1
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/heartbeat/service.js +2 -2
- package/dist/src/gateway/heartbeat/service.js.map +1 -1
- package/dist/src/gateway/hono/app.js +40 -37
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +1 -1
- package/dist/src/gateway/hono/lib/static-ui.js +2 -2
- package/dist/src/gateway/hono/middleware/auth.d.ts +5 -14
- package/dist/src/gateway/hono/middleware/auth.js +92 -105
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/middleware/logger.d.ts +5 -1
- package/dist/src/gateway/hono/middleware/logger.js +41 -5
- package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
- package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +14 -0
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js +62 -0
- package/dist/src/gateway/hono/middleware/strict-rate-limit.js.map +1 -0
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +4 -4
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js.map +1 -1
- package/dist/src/gateway/hono/routes/browser.d.ts +20 -0
- package/dist/src/gateway/hono/routes/browser.js +626 -0
- package/dist/src/gateway/hono/routes/browser.js.map +1 -0
- package/dist/src/gateway/hono/routes/commands-skills.js +13 -13
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.d.ts +18 -0
- package/dist/src/gateway/hono/routes/config-patch/agents.js +418 -0
- package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -0
- package/dist/src/gateway/hono/routes/config-patch/channels.d.ts +12 -0
- package/dist/src/gateway/hono/routes/config-patch/channels.js +186 -0
- package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -0
- package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +18 -0
- package/dist/src/gateway/hono/routes/config-patch/gateway.js +264 -0
- package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -0
- package/dist/src/gateway/hono/routes/config-patch/index.d.ts +9 -0
- package/dist/src/gateway/hono/routes/config-patch/index.js +6 -0
- package/dist/src/gateway/hono/routes/config-patch/misc.d.ts +23 -0
- package/dist/src/gateway/hono/routes/config-patch/misc.js +139 -0
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -0
- package/dist/src/gateway/hono/routes/config-patch/result.d.ts +18 -0
- package/dist/src/gateway/hono/routes/config-patch/result.js +13 -0
- package/dist/src/gateway/hono/routes/config-patch/result.js.map +1 -0
- package/dist/src/gateway/hono/routes/config.js +20 -1764
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +2 -3
- package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
- package/dist/src/gateway/hono/routes/exposure.js +2 -1
- package/dist/src/gateway/hono/routes/exposure.js.map +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +10 -5
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/mcp.js +1 -2
- package/dist/src/gateway/hono/routes/mcp.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +32 -32
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +4 -4
- package/dist/src/gateway/hono/routes/shares.js.map +1 -1
- package/dist/src/gateway/hono/routes/tunnel.js +1 -1
- package/dist/src/gateway/hono/routes/tunnel.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +6 -7
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.d.ts +1 -0
- package/dist/src/gateway/hono/sse.js +3 -2
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/index.d.ts +1 -1
- package/dist/src/gateway/index.js +4 -2
- package/dist/src/gateway/lock.js +3 -3
- package/dist/src/gateway/rate-limit/auth-policy.d.ts +34 -0
- package/dist/src/gateway/rate-limit/auth-policy.js +49 -0
- package/dist/src/gateway/rate-limit/auth-policy.js.map +1 -0
- package/dist/src/gateway/rate-limit/buckets.d.ts +63 -0
- package/dist/src/gateway/rate-limit/buckets.js +143 -0
- package/dist/src/gateway/rate-limit/buckets.js.map +1 -0
- package/dist/src/gateway/rate-limit/env-flags.d.ts +13 -0
- package/dist/src/gateway/rate-limit/env-flags.js +16 -0
- package/dist/src/gateway/rate-limit/env-flags.js.map +1 -0
- package/dist/src/gateway/rate-limit/index.d.ts +3 -0
- package/dist/src/gateway/rate-limit/index.js +4 -0
- package/dist/src/gateway/run-loop.d.ts +1 -1
- package/dist/src/gateway/run-loop.js +24 -4
- package/dist/src/gateway/run-loop.js.map +1 -1
- package/dist/src/gateway/runtime-config.js +2 -1
- package/dist/src/gateway/runtime-config.js.map +1 -1
- package/dist/src/gateway/security/audit.js +2 -1
- package/dist/src/gateway/security/audit.js.map +1 -1
- package/dist/src/gateway/security/index.d.ts +0 -1
- package/dist/src/gateway/security/index.js +1 -2
- package/dist/src/gateway/security/loopback.d.ts +13 -0
- package/dist/src/gateway/security/loopback.js +45 -0
- package/dist/src/gateway/security/loopback.js.map +1 -0
- package/dist/src/gateway/service/agent-runner.d.ts +108 -0
- package/dist/src/gateway/service/agent-runner.js +184 -0
- package/dist/src/gateway/service/agent-runner.js.map +1 -0
- package/dist/src/gateway/service/config-coordinator.d.ts +119 -0
- package/dist/src/gateway/service/config-coordinator.js +351 -0
- package/dist/src/gateway/service/config-coordinator.js.map +1 -0
- package/dist/src/gateway/service/marketplace-service.d.ts +85 -0
- package/dist/src/gateway/service/marketplace-service.js +239 -0
- package/dist/src/gateway/service/marketplace-service.js.map +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +5 -5
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +125 -0
- package/dist/src/gateway/service/sessions-api.js +135 -0
- package/dist/src/gateway/service/sessions-api.js.map +1 -0
- package/dist/src/gateway/service.d.ts +30 -360
- package/dist/src/gateway/service.js +122 -904
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -2
- package/dist/src/gateway/workspace-heartbeat-path.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/gateway-process-argv.d.ts +4 -0
- package/dist/src/infra/gateway-process-argv.js +26 -0
- package/dist/src/infra/gateway-process-argv.js.map +1 -0
- package/dist/src/infra/gateway-processes.d.ts +5 -0
- package/dist/src/infra/gateway-processes.js +65 -0
- package/dist/src/infra/gateway-processes.js.map +1 -0
- package/dist/src/infra/rate-limit/failure-limiter.d.ts +50 -0
- package/dist/src/infra/rate-limit/failure-limiter.js +100 -0
- package/dist/src/infra/rate-limit/failure-limiter.js.map +1 -0
- package/dist/src/infra/rate-limit/index.d.ts +5 -0
- package/dist/src/infra/rate-limit/index.js +3 -0
- package/dist/src/infra/rate-limit/keyed-store.d.ts +34 -0
- package/dist/src/infra/rate-limit/keyed-store.js +44 -0
- package/dist/src/infra/rate-limit/keyed-store.js.map +1 -0
- package/dist/src/infra/rate-limit/rate-limiter.d.ts +39 -0
- package/dist/src/infra/rate-limit/rate-limiter.js +65 -0
- package/dist/src/infra/rate-limit/rate-limiter.js.map +1 -0
- package/dist/src/infra/restart.d.ts +21 -0
- package/dist/src/infra/restart.js +122 -0
- package/dist/src/infra/restart.js.map +1 -0
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/mcp/channel-bridge.d.ts +0 -6
- package/dist/src/mcp/channel-bridge.js +1 -5
- package/dist/src/mcp/channel-bridge.js.map +1 -1
- package/dist/src/media-shared/http/ssrf-guard.js +1 -1
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +2 -2
- package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
- package/dist/src/session/parity/sessions-json-file-read.d.ts +2 -1
- package/dist/src/session/parity/sessions-json-file-read.js.map +1 -1
- package/dist/src/session/parity/sessions-json-file.js +1 -1
- package/dist/src/session/parity/transcript-file-lock.js +2 -2
- package/dist/src/session/parity/transcript-paths.js +1 -1
- package/dist/src/session/search-index-cache.js +1 -1
- package/dist/src/session/search-index.js +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.js +5 -5
- package/dist/src/share/share-rate-limit.d.ts +10 -2
- package/dist/src/share/share-rate-limit.js +39 -27
- package/dist/src/share/share-rate-limit.js.map +1 -1
- package/dist/src/share/share-store.js +3 -3
- package/dist/src/tui/backends/embedded-backend.js +16 -12
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/clipboard-image.js +2 -2
- package/dist/src/tui/extension-host/load-extensions.js +1 -1
- package/dist/src/tui/format-tui-hotkeys.js +1 -1
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -1
- package/dist/src/tui/tui-scoped-models.js +1 -1
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui-skills-autocomplete.js +1 -1
- package/dist/src/tui/tui.js +1 -2
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/tui/xopc-tui-keybindings.d.ts +0 -1
- package/dist/src/tui/xopc-tui-keybindings.js +1 -2
- package/dist/src/tui/xopc-tui-keybindings.js.map +1 -1
- package/dist/src/tunnel/frpc-binary.js +2 -2
- package/dist/src/tunnel/frpc-config.js +1 -1
- package/dist/src/tunnel/frpc-extract.js +1 -1
- package/dist/src/tunnel/pairing-rate-limit.d.ts +10 -2
- package/dist/src/tunnel/pairing-rate-limit.js +19 -15
- package/dist/src/tunnel/pairing-rate-limit.js.map +1 -1
- package/dist/src/tunnel/tunnel-rate-limit.d.ts +6 -3
- package/dist/src/tunnel/tunnel-rate-limit.js +19 -18
- package/dist/src/tunnel/tunnel-rate-limit.js.map +1 -1
- package/dist/src/tunnel/tunnel-state.js +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/utils/logger/stats.d.ts +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/factory.js +1 -1
- package/dist/src/voice/tts/index.js +2 -2
- package/dist/src/voice/tts/merge-config.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +1 -1
- package/dist/src/voice/tts/service.js +1 -1
- package/dist/src/voice/tts/service.js.map +1 -1
- package/dist/src/voice/tts/speak-core.js +1 -1
- package/package.json +10 -5
- package/dist/gateway/static/root/assets/agents-DOONGaKz.js +0 -222
- package/dist/gateway/static/root/assets/channels-settings-CARdL-ys.js +0 -1
- package/dist/gateway/static/root/assets/fetch-BAAh_kXG.js +0 -3
- package/dist/gateway/static/root/assets/index-C8yHX-AA.css +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BCNnhz9g.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-B7_PjiHL.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-VrL9TeVF.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-k4FWwgkk.js +0 -1
- package/dist/src/agent/embedded/session-raw-append-message.d.ts +0 -11
- package/dist/src/agent/embedded/session-raw-append-message.js +0 -15
- package/dist/src/agent/embedded/session-raw-append-message.js.map +0 -1
- package/dist/src/agent/embedded/session-tool-result-guard-wrapper.d.ts +0 -15
- package/dist/src/agent/embedded/session-tool-result-guard-wrapper.js +0 -24
- package/dist/src/agent/embedded/session-tool-result-guard-wrapper.js.map +0 -1
- package/dist/src/agent/embedded/session-tool-result-state.d.ts +0 -17
- package/dist/src/agent/embedded/session-tool-result-state.js +0 -26
- package/dist/src/agent/embedded/session-tool-result-state.js.map +0 -1
- package/dist/src/daemon/launchd-restart-handoff.d.ts +0 -25
- package/dist/src/daemon/launchd-restart-handoff.js +0 -132
- package/dist/src/daemon/launchd-restart-handoff.js.map +0 -1
- package/dist/src/gateway/auth-rate-limit.d.ts +0 -71
- package/dist/src/gateway/auth-rate-limit.js +0 -192
- package/dist/src/gateway/auth-rate-limit.js.map +0 -1
- package/dist/src/gateway/restart-handler.d.ts +0 -14
- package/dist/src/gateway/restart-handler.js +0 -64
- package/dist/src/gateway/restart-handler.js.map +0 -1
- package/dist/src/gateway/security/flood-guard.d.ts +0 -28
- package/dist/src/gateway/security/flood-guard.js +0 -42
- package/dist/src/gateway/security/flood-guard.js.map +0 -1
- package/dist/src/infra/rate-limit.d.ts +0 -38
- package/dist/src/infra/rate-limit.js +0 -60
- package/dist/src/infra/rate-limit.js.map +0 -1
- package/dist/src/infra/restart-intent.d.ts +0 -13
- package/dist/src/infra/restart-intent.js +0 -40
- package/dist/src/infra/restart-intent.js.map +0 -1
- package/dist/src/infra/restart-sentinel.d.ts +0 -23
- package/dist/src/infra/restart-sentinel.js +0 -75
- package/dist/src/infra/restart-sentinel.js.map +0 -1
- package/skills/creative/canvas-design/LICENSE.txt +0 -202
- package/skills/creative/canvas-design/SKILL-zh.md +0 -130
- package/skills/creative/canvas-design/SKILL.md +0 -130
- package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
- package/skills/creative/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/skills/creative/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
- package/skills/creative/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named rate-limit buckets used across the gateway. Each is a thin wrapper
|
|
3
|
+
* over a primitive limiter ({@link RateLimiter} or {@link FailureLimiter})
|
|
4
|
+
* that hard-codes its config and exposes a small surface.
|
|
5
|
+
*
|
|
6
|
+
* All buckets are owned by a process-level singleton {@link BucketRegistry}
|
|
7
|
+
* so that:
|
|
8
|
+
* - lifecycle (cleanup timers) is centralized — `destroyAll()` on shutdown,
|
|
9
|
+
* - tests get one obvious reset entry point (`resetAllForTests`),
|
|
10
|
+
* - new buckets are added in exactly one place.
|
|
11
|
+
*
|
|
12
|
+
* Tuning policy:
|
|
13
|
+
* - `auth_failure` is read from runtime config because users tune it per
|
|
14
|
+
* deployment. Other buckets keep static defaults until a real need
|
|
15
|
+
* emerges to expose them. (YAGNI > premature configurability.)
|
|
16
|
+
*/
|
|
17
|
+
import { FailureLimiter, RateLimiter } from '../../infra/rate-limit/index.js';
|
|
18
|
+
import type { GatewayAuthRateLimitConfig } from '../../config/schema.js';
|
|
19
|
+
import type { AuthRateLimitPolicyConfig } from './auth-policy.js';
|
|
20
|
+
/**
|
|
21
|
+
* Auth-failure config validated/normalized into the shape both the limiter
|
|
22
|
+
* primitive and the policy layer need.
|
|
23
|
+
*/
|
|
24
|
+
export type ResolvedAuthRateLimitConfig = {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
maxFailures: number;
|
|
27
|
+
windowMs: number;
|
|
28
|
+
blockDurationMs: number;
|
|
29
|
+
burstCoalesceMs: number;
|
|
30
|
+
exemptLoopback: boolean;
|
|
31
|
+
};
|
|
32
|
+
export declare function resolveAuthRateLimit(input: GatewayAuthRateLimitConfig | undefined): ResolvedAuthRateLimitConfig;
|
|
33
|
+
export declare function authPolicyConfig(cfg: ResolvedAuthRateLimitConfig): AuthRateLimitPolicyConfig;
|
|
34
|
+
declare class BucketRegistry {
|
|
35
|
+
private authFailureLimiter?;
|
|
36
|
+
private authFailureSignature?;
|
|
37
|
+
private strictApiLimiter?;
|
|
38
|
+
private tunnelMutateLimiter?;
|
|
39
|
+
private pairingExchangeLimiter?;
|
|
40
|
+
private sharePublicShortLimiter?;
|
|
41
|
+
private sharePublicLongLimiter?;
|
|
42
|
+
/**
|
|
43
|
+
* Reconfigured on the fly when `gateway.auth.rateLimit` is reloaded —
|
|
44
|
+
* compares a stable signature to avoid swapping out a hot bucket on every
|
|
45
|
+
* config read.
|
|
46
|
+
*/
|
|
47
|
+
authFailure(cfg: ResolvedAuthRateLimitConfig): FailureLimiter;
|
|
48
|
+
/** Sensitive admin / mutation endpoints — 15 req / 60 s per client IP. */
|
|
49
|
+
strictApi(): RateLimiter;
|
|
50
|
+
/** Tunnel mutation calls — 12 req / 5 min per gateway-token fingerprint. */
|
|
51
|
+
tunnelMutate(): RateLimiter;
|
|
52
|
+
/** Failed pairing-exchange attempts — 30 failures / 5 min per client IP. */
|
|
53
|
+
pairingExchange(): FailureLimiter;
|
|
54
|
+
/** Public share download short window — 60 req / min per client IP. */
|
|
55
|
+
sharePublicShort(): RateLimiter;
|
|
56
|
+
/** Public share download long window — 300 req / 15 min per client IP. */
|
|
57
|
+
sharePublicLong(): RateLimiter;
|
|
58
|
+
destroyAll(): void;
|
|
59
|
+
/** @internal */
|
|
60
|
+
resetAllForTests(): void;
|
|
61
|
+
}
|
|
62
|
+
export declare const buckets: BucketRegistry;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { RateLimiter } from "../../infra/rate-limit/rate-limiter.js";
|
|
2
|
+
import { FailureLimiter } from "../../infra/rate-limit/failure-limiter.js";
|
|
3
|
+
//#region src/gateway/rate-limit/buckets.ts
|
|
4
|
+
/**
|
|
5
|
+
* Named rate-limit buckets used across the gateway. Each is a thin wrapper
|
|
6
|
+
* over a primitive limiter ({@link RateLimiter} or {@link FailureLimiter})
|
|
7
|
+
* that hard-codes its config and exposes a small surface.
|
|
8
|
+
*
|
|
9
|
+
* All buckets are owned by a process-level singleton {@link BucketRegistry}
|
|
10
|
+
* so that:
|
|
11
|
+
* - lifecycle (cleanup timers) is centralized — `destroyAll()` on shutdown,
|
|
12
|
+
* - tests get one obvious reset entry point (`resetAllForTests`),
|
|
13
|
+
* - new buckets are added in exactly one place.
|
|
14
|
+
*
|
|
15
|
+
* Tuning policy:
|
|
16
|
+
* - `auth_failure` is read from runtime config because users tune it per
|
|
17
|
+
* deployment. Other buckets keep static defaults until a real need
|
|
18
|
+
* emerges to expose them. (YAGNI > premature configurability.)
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_AUTH_RATE_LIMIT = {
|
|
21
|
+
enabled: true,
|
|
22
|
+
maxFailures: 5,
|
|
23
|
+
windowMs: 900 * 1e3,
|
|
24
|
+
blockDurationMs: 300 * 1e3,
|
|
25
|
+
burstCoalesceMs: 1e3,
|
|
26
|
+
exemptLoopback: true
|
|
27
|
+
};
|
|
28
|
+
function resolveAuthRateLimit(input) {
|
|
29
|
+
if (!input) return { ...DEFAULT_AUTH_RATE_LIMIT };
|
|
30
|
+
const blockDurationMs = input.blockDurationMs ?? input.lockoutMs ?? DEFAULT_AUTH_RATE_LIMIT.blockDurationMs;
|
|
31
|
+
return {
|
|
32
|
+
enabled: input.enabled ?? DEFAULT_AUTH_RATE_LIMIT.enabled,
|
|
33
|
+
maxFailures: Math.max(1, Math.floor(input.maxAttempts ?? DEFAULT_AUTH_RATE_LIMIT.maxFailures)),
|
|
34
|
+
windowMs: Math.max(1e3, Math.floor(input.windowMs ?? DEFAULT_AUTH_RATE_LIMIT.windowMs)),
|
|
35
|
+
blockDurationMs: Math.max(1e3, Math.floor(blockDurationMs)),
|
|
36
|
+
burstCoalesceMs: Math.max(0, Math.floor(input.burstCoalesceMs ?? DEFAULT_AUTH_RATE_LIMIT.burstCoalesceMs)),
|
|
37
|
+
exemptLoopback: input.exemptLoopback ?? DEFAULT_AUTH_RATE_LIMIT.exemptLoopback
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function authPolicyConfig(cfg) {
|
|
41
|
+
return {
|
|
42
|
+
enabled: cfg.enabled,
|
|
43
|
+
exemptLoopback: cfg.exemptLoopback
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
var BucketRegistry = class {
|
|
47
|
+
authFailureLimiter;
|
|
48
|
+
authFailureSignature;
|
|
49
|
+
strictApiLimiter;
|
|
50
|
+
tunnelMutateLimiter;
|
|
51
|
+
pairingExchangeLimiter;
|
|
52
|
+
sharePublicShortLimiter;
|
|
53
|
+
sharePublicLongLimiter;
|
|
54
|
+
/**
|
|
55
|
+
* Reconfigured on the fly when `gateway.auth.rateLimit` is reloaded —
|
|
56
|
+
* compares a stable signature to avoid swapping out a hot bucket on every
|
|
57
|
+
* config read.
|
|
58
|
+
*/
|
|
59
|
+
authFailure(cfg) {
|
|
60
|
+
const sig = `${cfg.maxFailures}|${cfg.windowMs}|${cfg.blockDurationMs}|${cfg.burstCoalesceMs}`;
|
|
61
|
+
if (!this.authFailureLimiter || this.authFailureSignature !== sig) {
|
|
62
|
+
this.authFailureLimiter?.destroy();
|
|
63
|
+
this.authFailureLimiter = new FailureLimiter({
|
|
64
|
+
maxFailures: cfg.maxFailures,
|
|
65
|
+
windowMs: cfg.windowMs,
|
|
66
|
+
blockDurationMs: cfg.blockDurationMs,
|
|
67
|
+
burstCoalesceMs: cfg.burstCoalesceMs
|
|
68
|
+
});
|
|
69
|
+
this.authFailureSignature = sig;
|
|
70
|
+
}
|
|
71
|
+
return this.authFailureLimiter;
|
|
72
|
+
}
|
|
73
|
+
/** Sensitive admin / mutation endpoints — 15 req / 60 s per client IP. */
|
|
74
|
+
strictApi() {
|
|
75
|
+
if (!this.strictApiLimiter) this.strictApiLimiter = new RateLimiter({
|
|
76
|
+
maxRequests: 15,
|
|
77
|
+
windowMs: 6e4
|
|
78
|
+
});
|
|
79
|
+
return this.strictApiLimiter;
|
|
80
|
+
}
|
|
81
|
+
/** Tunnel mutation calls — 12 req / 5 min per gateway-token fingerprint. */
|
|
82
|
+
tunnelMutate() {
|
|
83
|
+
if (!this.tunnelMutateLimiter) this.tunnelMutateLimiter = new RateLimiter({
|
|
84
|
+
maxRequests: 12,
|
|
85
|
+
windowMs: 5 * 6e4
|
|
86
|
+
});
|
|
87
|
+
return this.tunnelMutateLimiter;
|
|
88
|
+
}
|
|
89
|
+
/** Failed pairing-exchange attempts — 30 failures / 5 min per client IP. */
|
|
90
|
+
pairingExchange() {
|
|
91
|
+
if (!this.pairingExchangeLimiter) this.pairingExchangeLimiter = new FailureLimiter({
|
|
92
|
+
maxFailures: 30,
|
|
93
|
+
windowMs: 5 * 6e4,
|
|
94
|
+
blockDurationMs: 5 * 6e4
|
|
95
|
+
});
|
|
96
|
+
return this.pairingExchangeLimiter;
|
|
97
|
+
}
|
|
98
|
+
/** Public share download short window — 60 req / min per client IP. */
|
|
99
|
+
sharePublicShort() {
|
|
100
|
+
if (!this.sharePublicShortLimiter) this.sharePublicShortLimiter = new RateLimiter({
|
|
101
|
+
maxRequests: 60,
|
|
102
|
+
windowMs: 6e4
|
|
103
|
+
});
|
|
104
|
+
return this.sharePublicShortLimiter;
|
|
105
|
+
}
|
|
106
|
+
/** Public share download long window — 300 req / 15 min per client IP. */
|
|
107
|
+
sharePublicLong() {
|
|
108
|
+
if (!this.sharePublicLongLimiter) this.sharePublicLongLimiter = new RateLimiter({
|
|
109
|
+
maxRequests: 300,
|
|
110
|
+
windowMs: 15 * 6e4
|
|
111
|
+
});
|
|
112
|
+
return this.sharePublicLongLimiter;
|
|
113
|
+
}
|
|
114
|
+
destroyAll() {
|
|
115
|
+
this.authFailureLimiter?.destroy();
|
|
116
|
+
this.strictApiLimiter?.destroy();
|
|
117
|
+
this.tunnelMutateLimiter?.destroy();
|
|
118
|
+
this.pairingExchangeLimiter?.destroy();
|
|
119
|
+
this.sharePublicShortLimiter?.destroy();
|
|
120
|
+
this.sharePublicLongLimiter?.destroy();
|
|
121
|
+
this.authFailureLimiter = void 0;
|
|
122
|
+
this.strictApiLimiter = void 0;
|
|
123
|
+
this.tunnelMutateLimiter = void 0;
|
|
124
|
+
this.pairingExchangeLimiter = void 0;
|
|
125
|
+
this.sharePublicShortLimiter = void 0;
|
|
126
|
+
this.sharePublicLongLimiter = void 0;
|
|
127
|
+
this.authFailureSignature = void 0;
|
|
128
|
+
}
|
|
129
|
+
/** @internal */
|
|
130
|
+
resetAllForTests() {
|
|
131
|
+
this.authFailureLimiter?.resetForTests();
|
|
132
|
+
this.strictApiLimiter?.resetForTests();
|
|
133
|
+
this.tunnelMutateLimiter?.resetForTests();
|
|
134
|
+
this.pairingExchangeLimiter?.resetForTests();
|
|
135
|
+
this.sharePublicShortLimiter?.resetForTests();
|
|
136
|
+
this.sharePublicLongLimiter?.resetForTests();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const buckets = new BucketRegistry();
|
|
140
|
+
//#endregion
|
|
141
|
+
export { authPolicyConfig, buckets, resolveAuthRateLimit };
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=buckets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buckets.js","names":[],"sources":["../../../../src/gateway/rate-limit/buckets.ts"],"sourcesContent":["/**\n * Named rate-limit buckets used across the gateway. Each is a thin wrapper\n * over a primitive limiter ({@link RateLimiter} or {@link FailureLimiter})\n * that hard-codes its config and exposes a small surface.\n *\n * All buckets are owned by a process-level singleton {@link BucketRegistry}\n * so that:\n * - lifecycle (cleanup timers) is centralized — `destroyAll()` on shutdown,\n * - tests get one obvious reset entry point (`resetAllForTests`),\n * - new buckets are added in exactly one place.\n *\n * Tuning policy:\n * - `auth_failure` is read from runtime config because users tune it per\n * deployment. Other buckets keep static defaults until a real need\n * emerges to expose them. (YAGNI > premature configurability.)\n */\n\nimport { FailureLimiter, RateLimiter } from '../../infra/rate-limit/index.js';\n\nimport type { GatewayAuthRateLimitConfig } from '../../config/schema.js';\nimport type { AuthRateLimitPolicyConfig } from './auth-policy.js';\n\n/**\n * Auth-failure config validated/normalized into the shape both the limiter\n * primitive and the policy layer need.\n */\nexport type ResolvedAuthRateLimitConfig = {\n enabled: boolean;\n maxFailures: number;\n windowMs: number;\n blockDurationMs: number;\n burstCoalesceMs: number;\n exemptLoopback: boolean;\n};\n\nconst DEFAULT_AUTH_RATE_LIMIT: ResolvedAuthRateLimitConfig = {\n enabled: true,\n maxFailures: 5,\n windowMs: 15 * 60 * 1000,\n blockDurationMs: 5 * 60 * 1000,\n burstCoalesceMs: 1000,\n exemptLoopback: true,\n};\n\nexport function resolveAuthRateLimit(\n input: GatewayAuthRateLimitConfig | undefined,\n): ResolvedAuthRateLimitConfig {\n if (!input) return { ...DEFAULT_AUTH_RATE_LIMIT };\n const blockDurationMs =\n input.blockDurationMs ?? input.lockoutMs ?? DEFAULT_AUTH_RATE_LIMIT.blockDurationMs;\n return {\n enabled: input.enabled ?? DEFAULT_AUTH_RATE_LIMIT.enabled,\n maxFailures: Math.max(1, Math.floor(input.maxAttempts ?? DEFAULT_AUTH_RATE_LIMIT.maxFailures)),\n windowMs: Math.max(1000, Math.floor(input.windowMs ?? DEFAULT_AUTH_RATE_LIMIT.windowMs)),\n blockDurationMs: Math.max(1000, Math.floor(blockDurationMs)),\n burstCoalesceMs: Math.max(\n 0,\n Math.floor(input.burstCoalesceMs ?? DEFAULT_AUTH_RATE_LIMIT.burstCoalesceMs),\n ),\n exemptLoopback: input.exemptLoopback ?? DEFAULT_AUTH_RATE_LIMIT.exemptLoopback,\n };\n}\n\nexport function authPolicyConfig(\n cfg: ResolvedAuthRateLimitConfig,\n): AuthRateLimitPolicyConfig {\n return { enabled: cfg.enabled, exemptLoopback: cfg.exemptLoopback };\n}\n\nclass BucketRegistry {\n private authFailureLimiter?: FailureLimiter;\n private authFailureSignature?: string;\n\n private strictApiLimiter?: RateLimiter;\n private tunnelMutateLimiter?: RateLimiter;\n private pairingExchangeLimiter?: FailureLimiter;\n private sharePublicShortLimiter?: RateLimiter;\n private sharePublicLongLimiter?: RateLimiter;\n\n /**\n * Reconfigured on the fly when `gateway.auth.rateLimit` is reloaded —\n * compares a stable signature to avoid swapping out a hot bucket on every\n * config read.\n */\n authFailure(cfg: ResolvedAuthRateLimitConfig): FailureLimiter {\n const sig = `${cfg.maxFailures}|${cfg.windowMs}|${cfg.blockDurationMs}|${cfg.burstCoalesceMs}`;\n if (!this.authFailureLimiter || this.authFailureSignature !== sig) {\n this.authFailureLimiter?.destroy();\n this.authFailureLimiter = new FailureLimiter({\n maxFailures: cfg.maxFailures,\n windowMs: cfg.windowMs,\n blockDurationMs: cfg.blockDurationMs,\n burstCoalesceMs: cfg.burstCoalesceMs,\n });\n this.authFailureSignature = sig;\n }\n return this.authFailureLimiter;\n }\n\n /** Sensitive admin / mutation endpoints — 15 req / 60 s per client IP. */\n strictApi(): RateLimiter {\n if (!this.strictApiLimiter) {\n this.strictApiLimiter = new RateLimiter({ maxRequests: 15, windowMs: 60_000 });\n }\n return this.strictApiLimiter;\n }\n\n /** Tunnel mutation calls — 12 req / 5 min per gateway-token fingerprint. */\n tunnelMutate(): RateLimiter {\n if (!this.tunnelMutateLimiter) {\n this.tunnelMutateLimiter = new RateLimiter({\n maxRequests: 12,\n windowMs: 5 * 60_000,\n });\n }\n return this.tunnelMutateLimiter;\n }\n\n /** Failed pairing-exchange attempts — 30 failures / 5 min per client IP. */\n pairingExchange(): FailureLimiter {\n if (!this.pairingExchangeLimiter) {\n this.pairingExchangeLimiter = new FailureLimiter({\n maxFailures: 30,\n windowMs: 5 * 60_000,\n blockDurationMs: 5 * 60_000,\n });\n }\n return this.pairingExchangeLimiter;\n }\n\n /** Public share download short window — 60 req / min per client IP. */\n sharePublicShort(): RateLimiter {\n if (!this.sharePublicShortLimiter) {\n this.sharePublicShortLimiter = new RateLimiter({\n maxRequests: 60,\n windowMs: 60_000,\n });\n }\n return this.sharePublicShortLimiter;\n }\n\n /** Public share download long window — 300 req / 15 min per client IP. */\n sharePublicLong(): RateLimiter {\n if (!this.sharePublicLongLimiter) {\n this.sharePublicLongLimiter = new RateLimiter({\n maxRequests: 300,\n windowMs: 15 * 60_000,\n });\n }\n return this.sharePublicLongLimiter;\n }\n\n destroyAll(): void {\n this.authFailureLimiter?.destroy();\n this.strictApiLimiter?.destroy();\n this.tunnelMutateLimiter?.destroy();\n this.pairingExchangeLimiter?.destroy();\n this.sharePublicShortLimiter?.destroy();\n this.sharePublicLongLimiter?.destroy();\n this.authFailureLimiter = undefined;\n this.strictApiLimiter = undefined;\n this.tunnelMutateLimiter = undefined;\n this.pairingExchangeLimiter = undefined;\n this.sharePublicShortLimiter = undefined;\n this.sharePublicLongLimiter = undefined;\n this.authFailureSignature = undefined;\n }\n\n /** @internal */\n resetAllForTests(): void {\n this.authFailureLimiter?.resetForTests();\n this.strictApiLimiter?.resetForTests();\n this.tunnelMutateLimiter?.resetForTests();\n this.pairingExchangeLimiter?.resetForTests();\n this.sharePublicShortLimiter?.resetForTests();\n this.sharePublicLongLimiter?.resetForTests();\n }\n}\n\nexport const buckets = new BucketRegistry();\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level env-var feature flags that influence rate-limit and gateway
|
|
3
|
+
* security behavior. Kept as a tiny module so non-gateway packages can read
|
|
4
|
+
* them without pulling the rate-limit subsystem.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isAuthRateLimitGloballyDisabled(): boolean;
|
|
7
|
+
export declare function isGatewayStrictSecurityEnabled(cfg?: {
|
|
8
|
+
gateway?: {
|
|
9
|
+
security?: {
|
|
10
|
+
strict?: boolean;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
}): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/gateway/rate-limit/env-flags.ts
|
|
2
|
+
/**
|
|
3
|
+
* Process-level env-var feature flags that influence rate-limit and gateway
|
|
4
|
+
* security behavior. Kept as a tiny module so non-gateway packages can read
|
|
5
|
+
* them without pulling the rate-limit subsystem.
|
|
6
|
+
*/
|
|
7
|
+
function isAuthRateLimitGloballyDisabled() {
|
|
8
|
+
return process.env.XOPC_AUTH_RATE_LIMIT === "false";
|
|
9
|
+
}
|
|
10
|
+
function isGatewayStrictSecurityEnabled(cfg) {
|
|
11
|
+
return cfg?.gateway?.security?.strict === true || process.env.XOPC_GATEWAY_STRICT_SECURITY === "1";
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=env-flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-flags.js","names":[],"sources":["../../../../src/gateway/rate-limit/env-flags.ts"],"sourcesContent":["/**\n * Process-level env-var feature flags that influence rate-limit and gateway\n * security behavior. Kept as a tiny module so non-gateway packages can read\n * them without pulling the rate-limit subsystem.\n */\n\nexport function isAuthRateLimitGloballyDisabled(): boolean {\n return process.env.XOPC_AUTH_RATE_LIMIT === 'false';\n}\n\nexport function isGatewayStrictSecurityEnabled(cfg?: {\n gateway?: { security?: { strict?: boolean } };\n}): boolean {\n return (\n cfg?.gateway?.security?.strict === true ||\n process.env.XOPC_GATEWAY_STRICT_SECURITY === '1'\n );\n}\n"],"mappings":";;;;;;AAMA,SAAgB,kCAA2C;AACzD,QAAO,QAAQ,IAAI,yBAAyB;;AAG9C,SAAgB,+BAA+B,KAEnC;AACV,QACE,KAAK,SAAS,UAAU,WAAW,QACnC,QAAQ,IAAI,iCAAiC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { buckets, resolveAuthRateLimit, authPolicyConfig, type ResolvedAuthRateLimitConfig, } from './buckets.js';
|
|
2
|
+
export { resolveAuthTracking, buildBrowserOriginKey, type AuthRateLimitPolicyConfig, type AuthRateLimitTracking, } from './auth-policy.js';
|
|
3
|
+
export { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled, } from './env-flags.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { authPolicyConfig, buckets, resolveAuthRateLimit } from "./buckets.js";
|
|
2
|
+
import { buildBrowserOriginKey, resolveAuthTracking } from "./auth-policy.js";
|
|
3
|
+
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "./env-flags.js";
|
|
4
|
+
export { authPolicyConfig, buckets, buildBrowserOriginKey, isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled, resolveAuthRateLimit, resolveAuthTracking };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gateway Run Loop - Core run loop for gateway process management
|
|
3
3
|
*/
|
|
4
|
-
import type { GatewayServer } from
|
|
4
|
+
import type { GatewayServer } from './server.js';
|
|
5
5
|
export type RunGatewayLoopOptions = {
|
|
6
6
|
start: () => Promise<GatewayServer>;
|
|
7
7
|
configPath: string;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { loadConfig } from "../config/loader.js";
|
|
2
|
+
import "../config/index.js";
|
|
3
|
+
import { isRestartEnabled } from "../config/commands.flags.js";
|
|
1
4
|
import { restartGatewayProcessWithFreshPid } from "./respawn.js";
|
|
2
5
|
import { acquireGatewayLock } from "./lock.js";
|
|
6
|
+
import { consumeGatewayRestartIntentSync, consumeGatewaySigusr1RestartAuthorization, isGatewaySigusr1RestartExternallyAllowed, resetGatewayRestartStateForInProcessRestart, scheduleGatewaySigusr1Restart, setGatewaySigusr1RestartPolicy } from "../infra/restart.js";
|
|
3
7
|
//#region src/gateway/run-loop.ts
|
|
8
|
+
/**
|
|
9
|
+
* Gateway Run Loop - Core run loop for gateway process management
|
|
10
|
+
*/
|
|
4
11
|
async function runGatewayLoop(opts) {
|
|
12
|
+
setGatewaySigusr1RestartPolicy({ allowExternal: isRestartEnabled(loadConfig(opts.configPath)) });
|
|
5
13
|
let lock = await acquireGatewayLock(opts.configPath, { port: opts.port });
|
|
6
14
|
let server = null;
|
|
7
15
|
let shuttingDown = false;
|
|
@@ -32,6 +40,7 @@ async function runGatewayLoop(opts) {
|
|
|
32
40
|
}
|
|
33
41
|
if (respawn.mode === "failed") console.warn(`[GatewayRunLoop] Full process restart failed: ${respawn.detail ?? "unknown error"}`);
|
|
34
42
|
else console.log("[GatewayRunLoop] Restart mode: in-process restart (XOPC_NO_RESPAWN)");
|
|
43
|
+
resetGatewayRestartStateForInProcessRestart();
|
|
35
44
|
try {
|
|
36
45
|
lock = await acquireGatewayLock(opts.configPath, { port: opts.port });
|
|
37
46
|
} catch (err) {
|
|
@@ -47,7 +56,6 @@ async function runGatewayLoop(opts) {
|
|
|
47
56
|
exitProcess(0);
|
|
48
57
|
};
|
|
49
58
|
const DRAIN_TIMEOUT_MS = 3e4;
|
|
50
|
-
/** Allow slow channel plugin `stop()` + HTTP close; primary path should finish sooner after bus shutdown. */
|
|
51
59
|
const SHUTDOWN_TIMEOUT_MS = 15e3;
|
|
52
60
|
const requestShutdown = (action, signal) => {
|
|
53
61
|
if (shuttingDown) return;
|
|
@@ -79,7 +87,7 @@ async function runGatewayLoop(opts) {
|
|
|
79
87
|
};
|
|
80
88
|
const onSigterm = () => {
|
|
81
89
|
console.log("[GatewayRunLoop] SIGTERM received");
|
|
82
|
-
requestShutdown("stop", "SIGTERM");
|
|
90
|
+
requestShutdown(consumeGatewayRestartIntentSync() ? "restart" : "stop", "SIGTERM");
|
|
83
91
|
};
|
|
84
92
|
const onSigint = () => {
|
|
85
93
|
console.log("[GatewayRunLoop] SIGINT received");
|
|
@@ -87,8 +95,20 @@ async function runGatewayLoop(opts) {
|
|
|
87
95
|
};
|
|
88
96
|
const onSigusr1 = () => {
|
|
89
97
|
console.log("[GatewayRunLoop] SIGUSR1 received");
|
|
90
|
-
if (
|
|
91
|
-
|
|
98
|
+
if (!consumeGatewaySigusr1RestartAuthorization()) {
|
|
99
|
+
if (!isGatewaySigusr1RestartExternallyAllowed()) {
|
|
100
|
+
console.warn("[GatewayRunLoop] SIGUSR1 restart ignored (commands.restart=false or unauthorized).");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (shuttingDown) {
|
|
104
|
+
console.log("[GatewayRunLoop] Received SIGUSR1 during shutdown; ignoring");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
scheduleGatewaySigusr1Restart({
|
|
108
|
+
delayMs: 0,
|
|
109
|
+
reason: "SIGUSR1",
|
|
110
|
+
onRestart: () => requestShutdown("restart", "SIGUSR1")
|
|
111
|
+
});
|
|
92
112
|
return;
|
|
93
113
|
}
|
|
94
114
|
requestShutdown("restart", "SIGUSR1");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-loop.js","names":[],"sources":["../../../src/gateway/run-loop.ts"],"sourcesContent":["/**\n * Gateway Run Loop - Core run loop for gateway process management\n */\n\nimport type { GatewayServer } from
|
|
1
|
+
{"version":3,"file":"run-loop.js","names":[],"sources":["../../../src/gateway/run-loop.ts"],"sourcesContent":["/**\n * Gateway Run Loop - Core run loop for gateway process management\n */\n\nimport { isRestartEnabled } from '../config/commands.flags.js';\nimport { loadConfig } from '../config/index.js';\nimport type { GatewayServer } from './server.js';\nimport { acquireGatewayLock } from './lock.js';\nimport { restartGatewayProcessWithFreshPid } from './respawn.js';\nimport {\n consumeGatewayRestartIntentSync,\n consumeGatewaySigusr1RestartAuthorization,\n isGatewaySigusr1RestartExternallyAllowed,\n resetGatewayRestartStateForInProcessRestart,\n scheduleGatewaySigusr1Restart,\n setGatewaySigusr1RestartPolicy,\n} from '../infra/restart.js';\n\ntype GatewayRunSignalAction = 'stop' | 'restart';\n\nexport type RunGatewayLoopOptions = {\n start: () => Promise<GatewayServer>;\n configPath: string;\n port: number;\n};\n\nexport async function runGatewayLoop(opts: RunGatewayLoopOptions): Promise<void> {\n const config = loadConfig(opts.configPath);\n setGatewaySigusr1RestartPolicy({ allowExternal: isRestartEnabled(config) });\n\n let lock = await acquireGatewayLock(opts.configPath, { port: opts.port });\n let server: GatewayServer | null = null;\n let shuttingDown = false;\n let restartResolver: (() => void) | null = null;\n\n const cleanupSignals = () => {\n process.removeListener('SIGTERM', onSigterm);\n process.removeListener('SIGINT', onSigint);\n process.removeListener('SIGUSR1', onSigusr1);\n };\n\n const exitProcess = (code: number) => {\n cleanupSignals();\n process.exit(code);\n };\n\n const releaseLock = async (): Promise<void> => {\n if (lock) {\n await lock.release();\n lock = null;\n }\n };\n\n const handleRestartAfterClose = async () => {\n await releaseLock();\n\n const respawn = restartGatewayProcessWithFreshPid();\n\n if (respawn.mode === 'spawned' || respawn.mode === 'supervised') {\n const modeLabel = respawn.mode === 'spawned'\n ? `spawned pid ${respawn.pid ?? 'unknown'}`\n : 'supervisor restart';\n console.log(`[GatewayRunLoop] Restart mode: full process restart (${modeLabel})`);\n exitProcess(0);\n return;\n }\n\n if (respawn.mode === 'failed') {\n console.warn(`[GatewayRunLoop] Full process restart failed: ${respawn.detail ?? 'unknown error'}`);\n } else {\n console.log('[GatewayRunLoop] Restart mode: in-process restart (XOPC_NO_RESPAWN)');\n }\n\n resetGatewayRestartStateForInProcessRestart();\n\n try {\n lock = await acquireGatewayLock(opts.configPath, { port: opts.port });\n } catch (err) {\n console.error(`[GatewayRunLoop] Failed to reacquire lock: ${String(err)}`);\n exitProcess(1);\n return;\n }\n\n shuttingDown = false;\n restartResolver?.();\n };\n\n const handleStopAfterClose = async () => {\n await releaseLock();\n exitProcess(0);\n };\n\n const DRAIN_TIMEOUT_MS = 30_000;\n const SHUTDOWN_TIMEOUT_MS = 15_000;\n\n const requestShutdown = (action: GatewayRunSignalAction, signal: string) => {\n if (shuttingDown) {\n return;\n }\n\n shuttingDown = true;\n const isRestart = action === 'restart';\n console.log(`[GatewayRunLoop] Received ${signal}; ${isRestart ? 'restarting' : 'shutting down'}`);\n\n cleanupSignals();\n\n const forceExitMs = isRestart ? DRAIN_TIMEOUT_MS + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;\n const forceExitTimer = setTimeout(() => {\n console.error('[GatewayRunLoop] Shutdown timed out; force exiting');\n exitProcess(0);\n }, forceExitMs);\n\n void (async () => {\n try {\n if (isRestart) {\n console.log('[GatewayRunLoop] Draining active tasks before restart...');\n }\n\n await server?.close?.({\n reason: isRestart ? 'gateway restarting' : 'gateway stopping',\n restartExpectedMs: isRestart ? 1500 : null,\n });\n } catch (err) {\n console.error(`[GatewayRunLoop] Shutdown error: ${String(err)}`);\n } finally {\n clearTimeout(forceExitTimer);\n server = null;\n\n if (isRestart) {\n await handleRestartAfterClose();\n } else {\n await handleStopAfterClose();\n }\n }\n })();\n };\n\n const onSigterm = () => {\n console.log('[GatewayRunLoop] SIGTERM received');\n const isRestart = consumeGatewayRestartIntentSync();\n requestShutdown(isRestart ? 'restart' : 'stop', 'SIGTERM');\n };\n\n const onSigint = () => {\n console.log('[GatewayRunLoop] SIGINT received');\n requestShutdown('stop', 'SIGINT');\n };\n\n const onSigusr1 = () => {\n console.log('[GatewayRunLoop] SIGUSR1 received');\n const authorized = consumeGatewaySigusr1RestartAuthorization();\n if (!authorized) {\n if (!isGatewaySigusr1RestartExternallyAllowed()) {\n console.warn(\n '[GatewayRunLoop] SIGUSR1 restart ignored (commands.restart=false or unauthorized).',\n );\n return;\n }\n if (shuttingDown) {\n console.log('[GatewayRunLoop] Received SIGUSR1 during shutdown; ignoring');\n return;\n }\n scheduleGatewaySigusr1Restart({\n delayMs: 0,\n reason: 'SIGUSR1',\n onRestart: () => requestShutdown('restart', 'SIGUSR1'),\n });\n return;\n }\n requestShutdown('restart', 'SIGUSR1');\n };\n\n process.on('SIGTERM', onSigterm);\n process.on('SIGINT', onSigint);\n process.on('SIGUSR1', onSigusr1);\n\n try {\n while (true) {\n console.log('[GatewayRunLoop] Starting gateway server...');\n try {\n server = await opts.start();\n } catch (err) {\n console.error('[GatewayRunLoop] Failed to start gateway server:', err);\n await releaseLock();\n exitProcess(1);\n return;\n }\n\n await new Promise<void>((resolve) => {\n restartResolver = resolve;\n });\n\n console.log('[GatewayRunLoop] Restart signal received, restarting gateway...');\n }\n } finally {\n await releaseLock();\n cleanupSignals();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { isAllInterfacesHost, isLoopbackHost } from "./host.js";
|
|
2
|
-
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "./auth-rate-limit.js";
|
|
3
2
|
import { resolveGatewayListenPlan } from "./listen.js";
|
|
4
3
|
import { assertTailscaleExposureCompatible } from "./tailscale-lifecycle.js";
|
|
4
|
+
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "./rate-limit/env-flags.js";
|
|
5
|
+
import "./rate-limit/index.js";
|
|
5
6
|
//#region src/gateway/runtime-config.ts
|
|
6
7
|
function normalizeCorsOrigins(cfg) {
|
|
7
8
|
return (cfg.gateway?.corsOrigins ?? []).map((value) => value.trim()).filter(Boolean);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-config.js","names":[],"sources":["../../../src/gateway/runtime-config.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport type { GatewayBindMode } from '../config/schema.js';\nimport type { ResolvedGatewayAuth } from './auth.js';\nimport { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from './
|
|
1
|
+
{"version":3,"file":"runtime-config.js","names":[],"sources":["../../../src/gateway/runtime-config.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport type { GatewayBindMode } from '../config/schema.js';\nimport type { ResolvedGatewayAuth } from './auth.js';\nimport { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from './rate-limit/index.js';\nimport {\n isAllInterfacesHost,\n isLoopbackHost,\n} from './host.js';\nimport { resolveGatewayListenPlan } from './listen.js';\nimport { assertTailscaleExposureCompatible } from './tailscale-lifecycle.js';\n\nexport type GatewayRuntimeConfig = {\n bindMode: GatewayBindMode;\n bindHost: string;\n customBindHost?: string;\n loopback: boolean;\n auth: ResolvedGatewayAuth;\n corsOrigins: string[];\n dangerouslyAllowHostHeaderOriginFallback: boolean;\n rateLimitEnabled: boolean;\n tlsEnabled: boolean;\n};\n\nfunction normalizeCorsOrigins(cfg: Config): string[] {\n return (cfg.gateway?.corsOrigins ?? [])\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction hasSharedSecret(auth: ResolvedGatewayAuth): boolean {\n if (auth.mode === 'trusted-proxy') {\n return true;\n }\n if (auth.mode === 'token') {\n return typeof auth.token === 'string' && auth.token.trim().length > 0;\n }\n if (auth.mode === 'password') {\n return typeof auth.password === 'string' && auth.password.trim().length > 0;\n }\n return false;\n}\n\n/**\n * Fail-closed gateway startup guards (OpenClaw-aligned).\n * Throws when bind/auth/origin combinations are unsafe for network exposure.\n */\nexport function assertGatewayRuntimeConfig(params: {\n cfg: Config;\n auth: ResolvedGatewayAuth;\n bindOverride?: GatewayBindMode;\n port: number;\n}): GatewayRuntimeConfig {\n const plan = resolveGatewayListenPlan({\n cfg: params.cfg,\n bindOverride: params.bindOverride,\n });\n const { bindMode, bindHost, customBindHost } = plan;\n const loopback = isLoopbackHost(bindHost);\n const corsOrigins = normalizeCorsOrigins(params.cfg);\n const dangerouslyAllowHostHeaderOriginFallback =\n params.cfg.gateway?.dangerouslyAllowHostHeaderOriginFallback === true;\n\n if (bindMode === 'loopback' && !loopback) {\n throw new Error(\n `gateway bind=loopback resolved to non-loopback host ${bindHost}; refusing fallback to a network bind`,\n );\n }\n\n if (bindMode === 'custom') {\n const configuredCustom = params.cfg.gateway?.customBindHost?.trim();\n if (!configuredCustom) {\n throw new Error('gateway.bind=custom requires gateway.customBindHost');\n }\n if (bindHost !== configuredCustom) {\n throw new Error(\n `gateway bind=custom requested ${configuredCustom} but resolved ${bindHost}`,\n );\n }\n }\n\n if (!loopback) {\n if (params.auth.mode === 'none') {\n throw new Error(\n `refusing to bind gateway to ${bindHost}:${params.port} without auth ` +\n '(set gateway.auth.mode to \"token\", \"password\", or \"trusted-proxy\" and configure credentials)',\n );\n }\n\n if (!hasSharedSecret(params.auth)) {\n throw new Error(\n `refusing to bind gateway to ${bindHost}:${params.port} without auth ` +\n '(set gateway.auth.token/password, XOPC_GATEWAY_TOKEN/XOPC_GATEWAY_PASSWORD, or trusted-proxy config)',\n );\n }\n\n if (params.auth.mode === 'trusted-proxy') {\n const trustedProxies = (params.cfg.gateway?.trustedProxies ?? [])\n .map((entry) => entry.trim())\n .filter(Boolean);\n if (trustedProxies.length === 0) {\n throw new Error(\n 'gateway auth mode=trusted-proxy requires gateway.trustedProxies to be configured with at least one proxy IP',\n );\n }\n }\n\n // Empty corsOrigins is allowed: resolveGatewayCorsOrigins applies loopback defaults\n // (localhost + 127.0.0.1 on the gateway port). LAN browser access still needs explicit origins.\n\n if (corsOrigins.some((origin) => origin === '*')) {\n throw new Error(\n 'gateway.corsOrigins must not include \"*\" when binding to a network-accessible address',\n );\n }\n }\n\n if (isAllInterfacesHost(bindHost) && params.auth.mode === 'none') {\n throw new Error(\n 'refusing to bind gateway to all interfaces (0.0.0.0) without authentication',\n );\n }\n\n assertTailscaleExposureCompatible(params.cfg);\n\n const rateLimitConfigured = params.cfg.gateway?.auth?.rateLimit !== undefined;\n const rateLimitEnabled =\n params.cfg.gateway?.auth?.rateLimit?.enabled !== false &&\n !isAuthRateLimitGloballyDisabled();\n const tailscaleMode = params.cfg.gateway?.tailscale?.mode ?? 'off';\n const tlsEnabled =\n params.cfg.tunnel?.enabled === true ||\n tailscaleMode !== 'off' ||\n params.cfg.gateway?.tls?.enabled === true;\n\n if (\n !loopback &&\n isGatewayStrictSecurityEnabled(params.cfg) &&\n !rateLimitConfigured\n ) {\n throw new Error(\n 'gateway.security.strict requires gateway.auth.rateLimit on network-accessible binds ' +\n '(e.g. { maxAttempts: 10, windowMs: 60000, blockDurationMs: 300000 })',\n );\n }\n\n return {\n bindMode,\n bindHost,\n customBindHost,\n loopback,\n auth: params.auth,\n corsOrigins,\n dangerouslyAllowHostHeaderOriginFallback,\n rateLimitEnabled,\n tlsEnabled,\n };\n}\n"],"mappings":";;;;;;AAuBA,SAAS,qBAAqB,KAAuB;AACnD,SAAQ,IAAI,SAAS,eAAe,EAAE,EACnC,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,OAAO,QAAQ;;AAGpB,SAAS,gBAAgB,MAAoC;AAC3D,KAAI,KAAK,SAAS,gBAChB,QAAO;AAET,KAAI,KAAK,SAAS,QAChB,QAAO,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,MAAM,CAAC,SAAS;AAEtE,KAAI,KAAK,SAAS,WAChB,QAAO,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,MAAM,CAAC,SAAS;AAE5E,QAAO;;;;;;AAOT,SAAgB,2BAA2B,QAKlB;CAKvB,MAAM,EAAE,UAAU,UAAU,mBAJf,yBAAyB;EACpC,KAAK,OAAO;EACZ,cAAc,OAAO;EACtB,CACkD;CACnD,MAAM,WAAW,eAAe,SAAS;CACzC,MAAM,cAAc,qBAAqB,OAAO,IAAI;CACpD,MAAM,2CACJ,OAAO,IAAI,SAAS,6CAA6C;AAEnE,KAAI,aAAa,cAAc,CAAC,SAC9B,OAAM,IAAI,MACR,uDAAuD,SAAS,uCACjE;AAGH,KAAI,aAAa,UAAU;EACzB,MAAM,mBAAmB,OAAO,IAAI,SAAS,gBAAgB,MAAM;AACnE,MAAI,CAAC,iBACH,OAAM,IAAI,MAAM,sDAAsD;AAExE,MAAI,aAAa,iBACf,OAAM,IAAI,MACR,iCAAiC,iBAAiB,gBAAgB,WACnE;;AAIL,KAAI,CAAC,UAAU;AACb,MAAI,OAAO,KAAK,SAAS,OACvB,OAAM,IAAI,MACR,+BAA+B,SAAS,GAAG,OAAO,KAAK,4GAExD;AAGH,MAAI,CAAC,gBAAgB,OAAO,KAAK,CAC/B,OAAM,IAAI,MACR,+BAA+B,SAAS,GAAG,OAAO,KAAK,oHAExD;AAGH,MAAI,OAAO,KAAK,SAAS;QACC,OAAO,IAAI,SAAS,kBAAkB,EAAE,EAC7D,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,OAAO,QACQ,CAAC,WAAW,EAC5B,OAAM,IAAI,MACR,8GACD;;AAOL,MAAI,YAAY,MAAM,WAAW,WAAW,IAAI,CAC9C,OAAM,IAAI,MACR,0FACD;;AAIL,KAAI,oBAAoB,SAAS,IAAI,OAAO,KAAK,SAAS,OACxD,OAAM,IAAI,MACR,8EACD;AAGH,mCAAkC,OAAO,IAAI;CAE7C,MAAM,sBAAsB,OAAO,IAAI,SAAS,MAAM,cAAc,KAAA;CACpE,MAAM,mBACJ,OAAO,IAAI,SAAS,MAAM,WAAW,YAAY,SACjD,CAAC,iCAAiC;CACpC,MAAM,gBAAgB,OAAO,IAAI,SAAS,WAAW,QAAQ;CAC7D,MAAM,aACJ,OAAO,IAAI,QAAQ,YAAY,QAC/B,kBAAkB,SAClB,OAAO,IAAI,SAAS,KAAK,YAAY;AAEvC,KACE,CAAC,YACD,+BAA+B,OAAO,IAAI,IAC1C,CAAC,oBAED,OAAM,IAAI,MACR,2JAED;AAGH,QAAO;EACL;EACA;EACA;EACA;EACA,MAAM,OAAO;EACb;EACA;EACA;EACA;EACD"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
-
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "../auth-rate-limit.js";
|
|
4
3
|
import { assertGatewayAuthConfigured, resolveGatewayAuth } from "../auth.js";
|
|
5
4
|
import { resolveGatewayListenPlan } from "../listen.js";
|
|
6
5
|
import { collectExposureConflicts, isRemoteGatewayInsecure, isTailnetBindUnavailable } from "../../remote-access/exposure-guards.js";
|
|
6
|
+
import { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from "../rate-limit/env-flags.js";
|
|
7
|
+
import "../rate-limit/index.js";
|
|
7
8
|
import { assertGatewayRuntimeConfig } from "../runtime-config.js";
|
|
8
9
|
//#region src/gateway/security/audit.ts
|
|
9
10
|
init_logger();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","names":[],"sources":["../../../../src/gateway/security/audit.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport type { ResolvedGatewayAuth } from '../auth.js';\nimport { resolveGatewayAuth, assertGatewayAuthConfigured } from '../auth.js';\nimport { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from '../auth-rate-limit.js';\nimport { assertGatewayRuntimeConfig } from '../runtime-config.js';\nimport { resolveGatewayListenPlan } from '../listen.js';\nimport {\n collectExposureConflicts,\n isRemoteGatewayInsecure,\n isTailnetBindUnavailable,\n} from '../../remote-access/exposure-guards.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SecurityAudit');\n\nexport type SecurityAuditFinding = {\n checkId: string;\n severity: 'critical' | 'warn' | 'info';\n title: string;\n detail: string;\n /** Actionable remediation suggestion (aligned with OpenClaw audit format). */\n remediation?: string;\n};\n\n/** Minimum token length to resist brute-force even with rate limiting. */\nconst MIN_AUDIT_TOKEN_LENGTH = 22;\n\nfunction isLoopbackHost(host: string | undefined): boolean {\n return !host ||\n host === '127.0.0.1' ||\n host === 'localhost' ||\n host === '::1';\n}\n\nfunction normalizeCorsOrigins(cfg: Config): string[] {\n return (cfg.gateway?.corsOrigins ?? [])\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction resolveAuditInputs(cfg: Config, env: NodeJS.ProcessEnv = process.env): {\n auth: ResolvedGatewayAuth;\n bindHost: string;\n corsOrigins: string[];\n rateLimitEnabled: boolean;\n tlsEnabled: boolean;\n trustedProxies?: string[];\n allowRealIpFallback: boolean;\n dangerouslyAllowHostHeaderOriginFallback: boolean;\n loopback: boolean;\n} {\n const auth = resolveGatewayAuth({ authConfig: cfg.gateway?.auth, env });\n const plan = resolveGatewayListenPlan({ cfg });\n const corsOrigins = normalizeCorsOrigins(cfg);\n const rateLimitEnabled =\n cfg.gateway?.auth?.rateLimit?.enabled !== false &&\n !isAuthRateLimitGloballyDisabled();\n const tlsEnabled =\n cfg.tunnel?.enabled === true ||\n (cfg.gateway?.tailscale?.mode ?? 'off') !== 'off' ||\n cfg.gateway?.tls?.enabled === true;\n const loopback = isLoopbackHost(plan.bindHost);\n\n return {\n auth,\n bindHost: plan.bindHost,\n corsOrigins,\n rateLimitEnabled,\n tlsEnabled,\n trustedProxies: cfg.gateway?.trustedProxies,\n allowRealIpFallback: cfg.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n cfg.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n loopback,\n };\n}\n\n/**\n * Pure gateway config audit (no logging). Shared by startup audit and `xopc doctor`.\n */\nexport function collectGatewayConfigFindings(params: {\n auth: ResolvedGatewayAuth;\n bindHost?: string;\n corsOrigins?: string[];\n rateLimitEnabled?: boolean;\n tlsEnabled?: boolean;\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n dangerouslyAllowHostHeaderOriginFallback?: boolean;\n strictSecurityEnabled?: boolean;\n rateLimitConfigured?: boolean;\n}): SecurityAuditFinding[] {\n const findings: SecurityAuditFinding[] = [];\n const loopback = isLoopbackHost(params.bindHost);\n\n if (params.auth.mode === 'none') {\n if (!loopback) {\n findings.push({\n checkId: 'gateway.auth.none_on_network',\n severity: 'critical',\n title: 'Gateway has no authentication on a network-accessible address',\n detail: `Auth mode is \"none\" but gateway binds to ${params.bindHost}. ` +\n 'Any host on the network can access the gateway without credentials.',\n remediation: 'Set gateway.auth.mode to \"token\" and configure a strong token ' +\n '(e.g. `openssl rand -hex 32`).',\n });\n } else {\n findings.push({\n checkId: 'gateway.auth.none_loopback',\n severity: 'warn',\n title: 'Gateway authentication is disabled',\n detail: 'Auth mode is \"none\". This is acceptable for local development ' +\n 'but should not be used in production.',\n remediation: 'Set gateway.auth.mode to \"token\" for production use.',\n });\n }\n }\n\n if (\n !loopback &&\n params.auth.mode === 'token' &&\n !params.auth.token?.trim()\n ) {\n findings.push({\n checkId: 'gateway.auth.missing_token_on_network',\n severity: 'critical',\n title: 'Network-accessible gateway has no auth token configured',\n detail: 'gateway.auth.mode is \"token\" but no token is configured for a non-loopback bind.',\n remediation: 'Set gateway.auth.token or XOPC_GATEWAY_TOKEN before binding to the network.',\n });\n }\n\n if (\n !loopback &&\n params.auth.mode === 'password' &&\n !params.auth.password?.trim()\n ) {\n findings.push({\n checkId: 'gateway.auth.missing_password_on_network',\n severity: 'critical',\n title: 'Network-accessible gateway has no auth password configured',\n detail: 'gateway.auth.mode is \"password\" but no password is configured for a non-loopback bind.',\n remediation: 'Set gateway.auth.password or XOPC_GATEWAY_PASSWORD before binding to the network.',\n });\n }\n\n if (params.auth.mode === 'token' && params.auth.token) {\n const token = params.auth.token;\n\n if (token.length < MIN_AUDIT_TOKEN_LENGTH) {\n findings.push({\n checkId: 'gateway.auth.short_token',\n severity: 'warn',\n title: 'Gateway token is short',\n detail: `Token length is ${token.length} characters. Short tokens are vulnerable ` +\n 'to brute-force even with rate limiting.',\n remediation: `Use a token of at least ${MIN_AUDIT_TOKEN_LENGTH} characters ` +\n '(e.g. `openssl rand -hex 32`).',\n });\n }\n\n if (/^(.)\\1+$/.test(token)) {\n findings.push({\n checkId: 'gateway.auth.low_entropy_token',\n severity: 'critical',\n title: 'Gateway token has extremely low entropy',\n detail: 'Token consists of a single repeated character, making it trivially guessable.',\n remediation: 'Generate a cryptographically random token: `openssl rand -hex 32`.',\n });\n }\n\n const envToken = process.env.XOPC_GATEWAY_TOKEN;\n if (!envToken) {\n findings.push({\n checkId: 'gateway.auth.auto_generated_token',\n severity: 'info',\n title: 'Gateway token was auto-generated',\n detail: 'No explicit XOPC_GATEWAY_TOKEN set. The token was auto-generated and will ' +\n 'change on each restart.',\n remediation: 'Set XOPC_GATEWAY_TOKEN environment variable for a stable token.',\n });\n }\n }\n\n if (params.corsOrigins?.includes('*')) {\n findings.push({\n checkId: 'gateway.cors.wildcard',\n severity: !loopback ? 'critical' : 'warn',\n title: 'CORS allows all origins',\n detail: 'corsOrigins includes \"*\". Any website can make authenticated API calls ' +\n 'to the gateway if it can obtain the token.',\n remediation: 'Replace \"*\" with explicit allowed origins ' +\n '(e.g. [\"http://localhost:18790\"]).',\n });\n }\n\n if (params.corsOrigins && params.corsOrigins.length > 20) {\n findings.push({\n checkId: 'gateway.cors.excessive_origins',\n severity: 'info',\n title: 'Large number of CORS origins configured',\n detail: `${params.corsOrigins.length} CORS origins configured. Review whether all are necessary.`,\n });\n }\n\n if (\n !loopback &&\n (!params.corsOrigins || params.corsOrigins.length === 0) &&\n params.dangerouslyAllowHostHeaderOriginFallback !== true\n ) {\n findings.push({\n checkId: 'gateway.cors.no_explicit_origins',\n severity: 'info',\n title: 'No custom CORS origins — loopback browsers only',\n detail:\n 'Gateway listens on a network address with empty gateway.corsOrigins. ' +\n 'localhost and 127.0.0.1 on the gateway port are allowed by default; ' +\n 'LAN or remote browser URLs must be added explicitly.',\n remediation:\n 'Add gateway.corsOrigins entries (e.g. http://192.168.x.x:<port>) for non-loopback browser access, ' +\n 'or enable gateway.dangerouslyAllowHostHeaderOriginFallback only if you understand the CSRF risk.',\n });\n }\n\n if (\n !loopback &&\n params.auth.mode !== 'none' &&\n params.auth.mode !== 'trusted-proxy' &&\n params.rateLimitEnabled === false\n ) {\n findings.push({\n checkId: 'gateway.auth.no_rate_limit',\n severity: 'warn',\n title: 'No auth rate limiting on network-accessible gateway',\n detail: 'Gateway is bound to a non-loopback address but auth rate limiting is disabled. ' +\n 'This allows unlimited brute-force authentication attempts.',\n remediation: 'Set gateway.auth.rateLimit ' +\n '(e.g. { maxAttempts: 10, windowMs: 60000, lockoutMs: 300000 }).',\n });\n }\n\n if (\n !loopback &&\n params.strictSecurityEnabled === true &&\n params.rateLimitConfigured !== true\n ) {\n findings.push({\n checkId: 'gateway.security.strict_no_rate_limit',\n severity: 'critical',\n title: 'Strict security requires explicit auth rate limit configuration',\n detail: 'gateway.security.strict is enabled on a network-accessible bind but gateway.auth.rateLimit is missing.',\n remediation:\n 'Set gateway.auth.rateLimit (e.g. { maxAttempts: 10, windowMs: 60000, blockDurationMs: 300000 }).',\n });\n }\n\n if (!loopback && !params.tlsEnabled) {\n findings.push({\n checkId: 'gateway.transport.no_tls',\n severity: 'warn',\n title: 'No TLS on network-accessible gateway',\n detail: 'Gateway is bound to a non-loopback address without TLS termination. ' +\n 'Tokens and data are transmitted in plaintext unless a reverse proxy or tunnel handles HTTPS.',\n remediation:\n 'Enable the tunnel feature (`tunnel.enabled`), terminate TLS at a reverse proxy (Caddy/nginx), ' +\n 'or bind to loopback and access via SSH/VPN.',\n });\n }\n\n if (params.bindHost === '0.0.0.0' || params.bindHost === '::') {\n findings.push({\n checkId: 'gateway.bind.all_interfaces',\n severity: 'warn',\n title: 'Gateway binds to all network interfaces',\n detail: 'Binding to all interfaces exposes the gateway on every network interface. ' +\n 'Prefer loopback unless remote access is required.',\n remediation: 'Set gateway.bind to \"loopback\" unless remote access is required.',\n });\n }\n\n if (params.auth.mode === 'trusted-proxy') {\n const trustedProxies = params.trustedProxies ?? [];\n const trustedProxyConfig = params.auth.trustedProxy;\n\n findings.push({\n checkId: 'gateway.trusted_proxy_auth',\n severity: 'critical',\n title: 'Trusted-proxy auth mode enabled',\n detail:\n 'gateway.auth.mode=\"trusted-proxy\" delegates authentication to a reverse proxy. ' +\n 'Ensure your proxy terminates TLS and authenticates users; gateway.trustedProxies ' +\n 'must only list your proxy server IPs.',\n remediation:\n 'Verify: (1) Proxy terminates TLS and authenticates users. ' +\n '(2) gateway.trustedProxies is restricted to proxy IPs only. ' +\n '(3) Direct access to the gateway port is blocked by firewall.',\n });\n\n if (trustedProxies.length === 0) {\n findings.push({\n checkId: 'gateway.trusted_proxy_no_proxies',\n severity: 'critical',\n title: 'Trusted-proxy auth enabled but no trusted proxies configured',\n detail:\n 'gateway.auth.mode=\"trusted-proxy\" but gateway.trustedProxies is empty. ' +\n 'All requests will be rejected and startup guards will fail.',\n remediation: 'Set gateway.trustedProxies to the IP(s) of your reverse proxy.',\n });\n }\n\n if (!trustedProxyConfig?.userHeader) {\n findings.push({\n checkId: 'gateway.trusted_proxy_no_user_header',\n severity: 'critical',\n title: 'Trusted-proxy auth missing userHeader config',\n detail:\n 'gateway.auth.mode=\"trusted-proxy\" but gateway.auth.trustedProxy.userHeader is not configured.',\n remediation:\n 'Set gateway.auth.trustedProxy.userHeader to the header your proxy uses ' +\n '(e.g. \"x-forwarded-user\", \"x-pomerium-claim-email\").',\n });\n }\n\n if (trustedProxyConfig?.allowLoopback === true) {\n findings.push({\n checkId: 'gateway.trusted_proxy_allow_loopback',\n severity: 'warn',\n title: 'Trusted-proxy auth allows loopback proxy sources',\n detail:\n 'gateway.auth.trustedProxy.allowLoopback=true allows loopback-source requests ' +\n 'from configured gateway.trustedProxies entries to satisfy trusted-proxy auth.',\n remediation:\n 'Enable only when a same-host reverse proxy is the intended trust boundary.',\n });\n }\n\n const allowUsers = trustedProxyConfig?.allowUsers ?? [];\n if (allowUsers.length === 0) {\n findings.push({\n checkId: 'gateway.trusted_proxy_no_allowlist',\n severity: 'warn',\n title: 'Trusted-proxy auth allows all authenticated users',\n detail:\n 'gateway.auth.trustedProxy.allowUsers is empty, so any user authenticated by your proxy can access the gateway.',\n remediation:\n 'Consider setting gateway.auth.trustedProxy.allowUsers to restrict access to specific users.',\n });\n }\n\n if (params.allowRealIpFallback === true) {\n findings.push({\n checkId: 'gateway.trusted_proxy_real_ip_fallback',\n severity: 'warn',\n title: 'X-Real-IP fallback is enabled for trusted-proxy client IP resolution',\n detail:\n 'gateway.allowRealIpFallback=true uses X-Real-IP when X-Forwarded-For chain parsing fails.',\n remediation:\n 'Keep gateway.allowRealIpFallback=false unless your trusted proxy only sets X-Real-IP.',\n });\n }\n }\n\n return findings;\n}\n\nexport function collectExposureAuditFindings(cfg: Config): SecurityAuditFinding[] {\n const findings: SecurityAuditFinding[] = [];\n const tailscaleMode = cfg.gateway?.tailscale?.mode ?? 'off';\n const bindMode = cfg.gateway?.bind ?? 'loopback';\n\n for (const conflict of collectExposureConflicts(cfg)) {\n findings.push({\n checkId: `gateway.exposure.${conflict.code}`,\n severity: 'critical',\n title: 'Remote exposure configuration conflict',\n detail: conflict.message,\n remediation: 'Adjust gateway.tailscale and tunnel settings so only one auto-exposure path is active.',\n });\n }\n\n if (tailscaleMode === 'funnel' && cfg.gateway?.auth?.mode !== 'password') {\n findings.push({\n checkId: 'gateway.tailscale.funnel_without_password',\n severity: 'critical',\n title: 'Tailscale Funnel requires password auth',\n detail: 'gateway.tailscale.mode=funnel exposes the gateway on the public internet and requires gateway.auth.mode=password.',\n remediation: 'Set gateway.auth.mode to password and configure gateway.auth.password.',\n });\n }\n\n if (tailscaleMode !== 'off' && bindMode !== 'loopback') {\n findings.push({\n checkId: 'gateway.tailscale.serve_with_non_loopback_bind',\n severity: 'critical',\n title: 'Tailscale exposure requires loopback bind',\n detail: `Tailscale ${tailscaleMode} is enabled but gateway.bind=${bindMode}.`,\n remediation: 'Set gateway.bind to loopback when using Tailscale Serve or Funnel.',\n });\n }\n\n if (isTailnetBindUnavailable(cfg)) {\n findings.push({\n checkId: 'gateway.bind.tailnet_ip_unavailable',\n severity: 'warn',\n title: 'Tailnet bind requested but Tailscale IP unavailable',\n detail: 'gateway.bind=tailnet but no Tailscale IPv4 (100.x) was detected; gateway falls back to loopback.',\n remediation: 'Install and connect Tailscale, or use gateway.tailscale.mode=serve instead.',\n });\n }\n\n if (isRemoteGatewayInsecure(cfg)) {\n findings.push({\n checkId: 'gateway.remote.insecure_url',\n severity: 'warn',\n title: 'Remote gateway URL uses plaintext HTTP',\n detail: 'gateway.mode=remote points to a non-loopback http:// URL without TLS.',\n remediation: 'Use https://, SSH tunnel to loopback, or Tailscale Serve.',\n });\n }\n\n if (tailscaleMode === 'funnel') {\n findings.push({\n checkId: 'gateway.tailscale.funnel_public',\n severity: 'critical',\n title: 'Tailscale Funnel exposes gateway to the public internet',\n detail: 'Funnel publishes HTTPS endpoints reachable from the public internet.',\n remediation: 'Prefer Tailscale Serve for tailnet-only access, or use FRP with consent for controlled public exposure.',\n });\n }\n\n return findings;\n}\n\n/** Findings from fail-closed startup guards (`assertGatewayRuntimeConfig`). */\nexport function collectGatewayStartupGuardFindings(\n cfg: Config,\n env: NodeJS.ProcessEnv = process.env,\n): SecurityAuditFinding[] {\n try {\n const auth = resolveGatewayAuth({ authConfig: cfg.gateway?.auth, env });\n assertGatewayAuthConfigured(auth);\n assertGatewayRuntimeConfig({\n cfg,\n auth,\n port: cfg.gateway?.port ?? 18790,\n });\n return [];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return [{\n checkId: 'gateway.runtime_config.blocked',\n severity: 'critical',\n title: 'Gateway startup guards would reject this configuration',\n detail: message,\n remediation: 'Fix the configuration issue above, then run `xopc gateway` again.',\n }];\n }\n}\n\nfunction mergeFindings(findings: SecurityAuditFinding[]): SecurityAuditFinding[] {\n const byId = new Map<string, SecurityAuditFinding>();\n const severityRank = { critical: 3, warn: 2, info: 1 } as const;\n\n for (const finding of findings) {\n const existing = byId.get(finding.checkId);\n if (!existing || severityRank[finding.severity] > severityRank[existing.severity]) {\n byId.set(finding.checkId, finding);\n }\n }\n return [...byId.values()];\n}\n\n/**\n * Full gateway security findings for doctor / CLI audit (config + startup guards).\n */\nexport function collectGatewaySecurityFindings(\n cfg: Config,\n env: NodeJS.ProcessEnv = process.env,\n): SecurityAuditFinding[] {\n const inputs = resolveAuditInputs(cfg, env);\n const configFindings = collectGatewayConfigFindings({\n auth: inputs.auth,\n bindHost: inputs.bindHost,\n corsOrigins: inputs.corsOrigins,\n rateLimitEnabled: inputs.rateLimitEnabled,\n tlsEnabled: inputs.tlsEnabled,\n trustedProxies: inputs.trustedProxies,\n allowRealIpFallback: inputs.allowRealIpFallback,\n dangerouslyAllowHostHeaderOriginFallback: inputs.dangerouslyAllowHostHeaderOriginFallback,\n strictSecurityEnabled: isGatewayStrictSecurityEnabled(cfg),\n rateLimitConfigured: cfg.gateway?.auth?.rateLimit !== undefined,\n });\n const startupFindings = collectGatewayStartupGuardFindings(cfg, env);\n const exposureFindings = collectExposureAuditFindings(cfg);\n return mergeFindings([...configFindings, ...startupFindings, ...exposureFindings]);\n}\n\nfunction emitFindings(findings: SecurityAuditFinding[]): void {\n for (const finding of findings) {\n const logData = {\n checkId: finding.checkId,\n detail: finding.detail,\n ...(finding.remediation ? { remediation: finding.remediation } : {}),\n };\n switch (finding.severity) {\n case 'critical':\n log.error(logData, `Security audit: ${finding.title}`);\n break;\n case 'warn':\n log.warn(logData, `Security audit: ${finding.title}`);\n break;\n case 'info':\n log.info(logData, `Security audit: ${finding.title}`);\n break;\n }\n }\n}\n\n/**\n * Audit the gateway configuration at startup and log security findings.\n */\nexport function auditGatewayConfig(params: {\n auth: ResolvedGatewayAuth;\n bindHost?: string;\n corsOrigins?: string[];\n rateLimitEnabled?: boolean;\n tlsEnabled?: boolean;\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n dangerouslyAllowHostHeaderOriginFallback?: boolean;\n strictSecurityEnabled?: boolean;\n rateLimitConfigured?: boolean;\n}): SecurityAuditFinding[] {\n const findings = collectGatewayConfigFindings(params);\n emitFindings(findings);\n return findings;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audit.js","names":[],"sources":["../../../../src/gateway/security/audit.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport type { ResolvedGatewayAuth } from '../auth.js';\nimport { resolveGatewayAuth, assertGatewayAuthConfigured } from '../auth.js';\nimport { isAuthRateLimitGloballyDisabled, isGatewayStrictSecurityEnabled } from '../rate-limit/index.js';\nimport { assertGatewayRuntimeConfig } from '../runtime-config.js';\nimport { resolveGatewayListenPlan } from '../listen.js';\nimport {\n collectExposureConflicts,\n isRemoteGatewayInsecure,\n isTailnetBindUnavailable,\n} from '../../remote-access/exposure-guards.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SecurityAudit');\n\nexport type SecurityAuditFinding = {\n checkId: string;\n severity: 'critical' | 'warn' | 'info';\n title: string;\n detail: string;\n /** Actionable remediation suggestion (aligned with OpenClaw audit format). */\n remediation?: string;\n};\n\n/** Minimum token length to resist brute-force even with rate limiting. */\nconst MIN_AUDIT_TOKEN_LENGTH = 22;\n\nfunction isLoopbackHost(host: string | undefined): boolean {\n return !host ||\n host === '127.0.0.1' ||\n host === 'localhost' ||\n host === '::1';\n}\n\nfunction normalizeCorsOrigins(cfg: Config): string[] {\n return (cfg.gateway?.corsOrigins ?? [])\n .map((value) => value.trim())\n .filter(Boolean);\n}\n\nfunction resolveAuditInputs(cfg: Config, env: NodeJS.ProcessEnv = process.env): {\n auth: ResolvedGatewayAuth;\n bindHost: string;\n corsOrigins: string[];\n rateLimitEnabled: boolean;\n tlsEnabled: boolean;\n trustedProxies?: string[];\n allowRealIpFallback: boolean;\n dangerouslyAllowHostHeaderOriginFallback: boolean;\n loopback: boolean;\n} {\n const auth = resolveGatewayAuth({ authConfig: cfg.gateway?.auth, env });\n const plan = resolveGatewayListenPlan({ cfg });\n const corsOrigins = normalizeCorsOrigins(cfg);\n const rateLimitEnabled =\n cfg.gateway?.auth?.rateLimit?.enabled !== false &&\n !isAuthRateLimitGloballyDisabled();\n const tlsEnabled =\n cfg.tunnel?.enabled === true ||\n (cfg.gateway?.tailscale?.mode ?? 'off') !== 'off' ||\n cfg.gateway?.tls?.enabled === true;\n const loopback = isLoopbackHost(plan.bindHost);\n\n return {\n auth,\n bindHost: plan.bindHost,\n corsOrigins,\n rateLimitEnabled,\n tlsEnabled,\n trustedProxies: cfg.gateway?.trustedProxies,\n allowRealIpFallback: cfg.gateway?.allowRealIpFallback === true,\n dangerouslyAllowHostHeaderOriginFallback:\n cfg.gateway?.dangerouslyAllowHostHeaderOriginFallback === true,\n loopback,\n };\n}\n\n/**\n * Pure gateway config audit (no logging). Shared by startup audit and `xopc doctor`.\n */\nexport function collectGatewayConfigFindings(params: {\n auth: ResolvedGatewayAuth;\n bindHost?: string;\n corsOrigins?: string[];\n rateLimitEnabled?: boolean;\n tlsEnabled?: boolean;\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n dangerouslyAllowHostHeaderOriginFallback?: boolean;\n strictSecurityEnabled?: boolean;\n rateLimitConfigured?: boolean;\n}): SecurityAuditFinding[] {\n const findings: SecurityAuditFinding[] = [];\n const loopback = isLoopbackHost(params.bindHost);\n\n if (params.auth.mode === 'none') {\n if (!loopback) {\n findings.push({\n checkId: 'gateway.auth.none_on_network',\n severity: 'critical',\n title: 'Gateway has no authentication on a network-accessible address',\n detail: `Auth mode is \"none\" but gateway binds to ${params.bindHost}. ` +\n 'Any host on the network can access the gateway without credentials.',\n remediation: 'Set gateway.auth.mode to \"token\" and configure a strong token ' +\n '(e.g. `openssl rand -hex 32`).',\n });\n } else {\n findings.push({\n checkId: 'gateway.auth.none_loopback',\n severity: 'warn',\n title: 'Gateway authentication is disabled',\n detail: 'Auth mode is \"none\". This is acceptable for local development ' +\n 'but should not be used in production.',\n remediation: 'Set gateway.auth.mode to \"token\" for production use.',\n });\n }\n }\n\n if (\n !loopback &&\n params.auth.mode === 'token' &&\n !params.auth.token?.trim()\n ) {\n findings.push({\n checkId: 'gateway.auth.missing_token_on_network',\n severity: 'critical',\n title: 'Network-accessible gateway has no auth token configured',\n detail: 'gateway.auth.mode is \"token\" but no token is configured for a non-loopback bind.',\n remediation: 'Set gateway.auth.token or XOPC_GATEWAY_TOKEN before binding to the network.',\n });\n }\n\n if (\n !loopback &&\n params.auth.mode === 'password' &&\n !params.auth.password?.trim()\n ) {\n findings.push({\n checkId: 'gateway.auth.missing_password_on_network',\n severity: 'critical',\n title: 'Network-accessible gateway has no auth password configured',\n detail: 'gateway.auth.mode is \"password\" but no password is configured for a non-loopback bind.',\n remediation: 'Set gateway.auth.password or XOPC_GATEWAY_PASSWORD before binding to the network.',\n });\n }\n\n if (params.auth.mode === 'token' && params.auth.token) {\n const token = params.auth.token;\n\n if (token.length < MIN_AUDIT_TOKEN_LENGTH) {\n findings.push({\n checkId: 'gateway.auth.short_token',\n severity: 'warn',\n title: 'Gateway token is short',\n detail: `Token length is ${token.length} characters. Short tokens are vulnerable ` +\n 'to brute-force even with rate limiting.',\n remediation: `Use a token of at least ${MIN_AUDIT_TOKEN_LENGTH} characters ` +\n '(e.g. `openssl rand -hex 32`).',\n });\n }\n\n if (/^(.)\\1+$/.test(token)) {\n findings.push({\n checkId: 'gateway.auth.low_entropy_token',\n severity: 'critical',\n title: 'Gateway token has extremely low entropy',\n detail: 'Token consists of a single repeated character, making it trivially guessable.',\n remediation: 'Generate a cryptographically random token: `openssl rand -hex 32`.',\n });\n }\n\n const envToken = process.env.XOPC_GATEWAY_TOKEN;\n if (!envToken) {\n findings.push({\n checkId: 'gateway.auth.auto_generated_token',\n severity: 'info',\n title: 'Gateway token was auto-generated',\n detail: 'No explicit XOPC_GATEWAY_TOKEN set. The token was auto-generated and will ' +\n 'change on each restart.',\n remediation: 'Set XOPC_GATEWAY_TOKEN environment variable for a stable token.',\n });\n }\n }\n\n if (params.corsOrigins?.includes('*')) {\n findings.push({\n checkId: 'gateway.cors.wildcard',\n severity: !loopback ? 'critical' : 'warn',\n title: 'CORS allows all origins',\n detail: 'corsOrigins includes \"*\". Any website can make authenticated API calls ' +\n 'to the gateway if it can obtain the token.',\n remediation: 'Replace \"*\" with explicit allowed origins ' +\n '(e.g. [\"http://localhost:18790\"]).',\n });\n }\n\n if (params.corsOrigins && params.corsOrigins.length > 20) {\n findings.push({\n checkId: 'gateway.cors.excessive_origins',\n severity: 'info',\n title: 'Large number of CORS origins configured',\n detail: `${params.corsOrigins.length} CORS origins configured. Review whether all are necessary.`,\n });\n }\n\n if (\n !loopback &&\n (!params.corsOrigins || params.corsOrigins.length === 0) &&\n params.dangerouslyAllowHostHeaderOriginFallback !== true\n ) {\n findings.push({\n checkId: 'gateway.cors.no_explicit_origins',\n severity: 'info',\n title: 'No custom CORS origins — loopback browsers only',\n detail:\n 'Gateway listens on a network address with empty gateway.corsOrigins. ' +\n 'localhost and 127.0.0.1 on the gateway port are allowed by default; ' +\n 'LAN or remote browser URLs must be added explicitly.',\n remediation:\n 'Add gateway.corsOrigins entries (e.g. http://192.168.x.x:<port>) for non-loopback browser access, ' +\n 'or enable gateway.dangerouslyAllowHostHeaderOriginFallback only if you understand the CSRF risk.',\n });\n }\n\n if (\n !loopback &&\n params.auth.mode !== 'none' &&\n params.auth.mode !== 'trusted-proxy' &&\n params.rateLimitEnabled === false\n ) {\n findings.push({\n checkId: 'gateway.auth.no_rate_limit',\n severity: 'warn',\n title: 'No auth rate limiting on network-accessible gateway',\n detail: 'Gateway is bound to a non-loopback address but auth rate limiting is disabled. ' +\n 'This allows unlimited brute-force authentication attempts.',\n remediation: 'Set gateway.auth.rateLimit ' +\n '(e.g. { maxAttempts: 10, windowMs: 60000, lockoutMs: 300000 }).',\n });\n }\n\n if (\n !loopback &&\n params.strictSecurityEnabled === true &&\n params.rateLimitConfigured !== true\n ) {\n findings.push({\n checkId: 'gateway.security.strict_no_rate_limit',\n severity: 'critical',\n title: 'Strict security requires explicit auth rate limit configuration',\n detail: 'gateway.security.strict is enabled on a network-accessible bind but gateway.auth.rateLimit is missing.',\n remediation:\n 'Set gateway.auth.rateLimit (e.g. { maxAttempts: 10, windowMs: 60000, blockDurationMs: 300000 }).',\n });\n }\n\n if (!loopback && !params.tlsEnabled) {\n findings.push({\n checkId: 'gateway.transport.no_tls',\n severity: 'warn',\n title: 'No TLS on network-accessible gateway',\n detail: 'Gateway is bound to a non-loopback address without TLS termination. ' +\n 'Tokens and data are transmitted in plaintext unless a reverse proxy or tunnel handles HTTPS.',\n remediation:\n 'Enable the tunnel feature (`tunnel.enabled`), terminate TLS at a reverse proxy (Caddy/nginx), ' +\n 'or bind to loopback and access via SSH/VPN.',\n });\n }\n\n if (params.bindHost === '0.0.0.0' || params.bindHost === '::') {\n findings.push({\n checkId: 'gateway.bind.all_interfaces',\n severity: 'warn',\n title: 'Gateway binds to all network interfaces',\n detail: 'Binding to all interfaces exposes the gateway on every network interface. ' +\n 'Prefer loopback unless remote access is required.',\n remediation: 'Set gateway.bind to \"loopback\" unless remote access is required.',\n });\n }\n\n if (params.auth.mode === 'trusted-proxy') {\n const trustedProxies = params.trustedProxies ?? [];\n const trustedProxyConfig = params.auth.trustedProxy;\n\n findings.push({\n checkId: 'gateway.trusted_proxy_auth',\n severity: 'critical',\n title: 'Trusted-proxy auth mode enabled',\n detail:\n 'gateway.auth.mode=\"trusted-proxy\" delegates authentication to a reverse proxy. ' +\n 'Ensure your proxy terminates TLS and authenticates users; gateway.trustedProxies ' +\n 'must only list your proxy server IPs.',\n remediation:\n 'Verify: (1) Proxy terminates TLS and authenticates users. ' +\n '(2) gateway.trustedProxies is restricted to proxy IPs only. ' +\n '(3) Direct access to the gateway port is blocked by firewall.',\n });\n\n if (trustedProxies.length === 0) {\n findings.push({\n checkId: 'gateway.trusted_proxy_no_proxies',\n severity: 'critical',\n title: 'Trusted-proxy auth enabled but no trusted proxies configured',\n detail:\n 'gateway.auth.mode=\"trusted-proxy\" but gateway.trustedProxies is empty. ' +\n 'All requests will be rejected and startup guards will fail.',\n remediation: 'Set gateway.trustedProxies to the IP(s) of your reverse proxy.',\n });\n }\n\n if (!trustedProxyConfig?.userHeader) {\n findings.push({\n checkId: 'gateway.trusted_proxy_no_user_header',\n severity: 'critical',\n title: 'Trusted-proxy auth missing userHeader config',\n detail:\n 'gateway.auth.mode=\"trusted-proxy\" but gateway.auth.trustedProxy.userHeader is not configured.',\n remediation:\n 'Set gateway.auth.trustedProxy.userHeader to the header your proxy uses ' +\n '(e.g. \"x-forwarded-user\", \"x-pomerium-claim-email\").',\n });\n }\n\n if (trustedProxyConfig?.allowLoopback === true) {\n findings.push({\n checkId: 'gateway.trusted_proxy_allow_loopback',\n severity: 'warn',\n title: 'Trusted-proxy auth allows loopback proxy sources',\n detail:\n 'gateway.auth.trustedProxy.allowLoopback=true allows loopback-source requests ' +\n 'from configured gateway.trustedProxies entries to satisfy trusted-proxy auth.',\n remediation:\n 'Enable only when a same-host reverse proxy is the intended trust boundary.',\n });\n }\n\n const allowUsers = trustedProxyConfig?.allowUsers ?? [];\n if (allowUsers.length === 0) {\n findings.push({\n checkId: 'gateway.trusted_proxy_no_allowlist',\n severity: 'warn',\n title: 'Trusted-proxy auth allows all authenticated users',\n detail:\n 'gateway.auth.trustedProxy.allowUsers is empty, so any user authenticated by your proxy can access the gateway.',\n remediation:\n 'Consider setting gateway.auth.trustedProxy.allowUsers to restrict access to specific users.',\n });\n }\n\n if (params.allowRealIpFallback === true) {\n findings.push({\n checkId: 'gateway.trusted_proxy_real_ip_fallback',\n severity: 'warn',\n title: 'X-Real-IP fallback is enabled for trusted-proxy client IP resolution',\n detail:\n 'gateway.allowRealIpFallback=true uses X-Real-IP when X-Forwarded-For chain parsing fails.',\n remediation:\n 'Keep gateway.allowRealIpFallback=false unless your trusted proxy only sets X-Real-IP.',\n });\n }\n }\n\n return findings;\n}\n\nexport function collectExposureAuditFindings(cfg: Config): SecurityAuditFinding[] {\n const findings: SecurityAuditFinding[] = [];\n const tailscaleMode = cfg.gateway?.tailscale?.mode ?? 'off';\n const bindMode = cfg.gateway?.bind ?? 'loopback';\n\n for (const conflict of collectExposureConflicts(cfg)) {\n findings.push({\n checkId: `gateway.exposure.${conflict.code}`,\n severity: 'critical',\n title: 'Remote exposure configuration conflict',\n detail: conflict.message,\n remediation: 'Adjust gateway.tailscale and tunnel settings so only one auto-exposure path is active.',\n });\n }\n\n if (tailscaleMode === 'funnel' && cfg.gateway?.auth?.mode !== 'password') {\n findings.push({\n checkId: 'gateway.tailscale.funnel_without_password',\n severity: 'critical',\n title: 'Tailscale Funnel requires password auth',\n detail: 'gateway.tailscale.mode=funnel exposes the gateway on the public internet and requires gateway.auth.mode=password.',\n remediation: 'Set gateway.auth.mode to password and configure gateway.auth.password.',\n });\n }\n\n if (tailscaleMode !== 'off' && bindMode !== 'loopback') {\n findings.push({\n checkId: 'gateway.tailscale.serve_with_non_loopback_bind',\n severity: 'critical',\n title: 'Tailscale exposure requires loopback bind',\n detail: `Tailscale ${tailscaleMode} is enabled but gateway.bind=${bindMode}.`,\n remediation: 'Set gateway.bind to loopback when using Tailscale Serve or Funnel.',\n });\n }\n\n if (isTailnetBindUnavailable(cfg)) {\n findings.push({\n checkId: 'gateway.bind.tailnet_ip_unavailable',\n severity: 'warn',\n title: 'Tailnet bind requested but Tailscale IP unavailable',\n detail: 'gateway.bind=tailnet but no Tailscale IPv4 (100.x) was detected; gateway falls back to loopback.',\n remediation: 'Install and connect Tailscale, or use gateway.tailscale.mode=serve instead.',\n });\n }\n\n if (isRemoteGatewayInsecure(cfg)) {\n findings.push({\n checkId: 'gateway.remote.insecure_url',\n severity: 'warn',\n title: 'Remote gateway URL uses plaintext HTTP',\n detail: 'gateway.mode=remote points to a non-loopback http:// URL without TLS.',\n remediation: 'Use https://, SSH tunnel to loopback, or Tailscale Serve.',\n });\n }\n\n if (tailscaleMode === 'funnel') {\n findings.push({\n checkId: 'gateway.tailscale.funnel_public',\n severity: 'critical',\n title: 'Tailscale Funnel exposes gateway to the public internet',\n detail: 'Funnel publishes HTTPS endpoints reachable from the public internet.',\n remediation: 'Prefer Tailscale Serve for tailnet-only access, or use FRP with consent for controlled public exposure.',\n });\n }\n\n return findings;\n}\n\n/** Findings from fail-closed startup guards (`assertGatewayRuntimeConfig`). */\nexport function collectGatewayStartupGuardFindings(\n cfg: Config,\n env: NodeJS.ProcessEnv = process.env,\n): SecurityAuditFinding[] {\n try {\n const auth = resolveGatewayAuth({ authConfig: cfg.gateway?.auth, env });\n assertGatewayAuthConfigured(auth);\n assertGatewayRuntimeConfig({\n cfg,\n auth,\n port: cfg.gateway?.port ?? 18790,\n });\n return [];\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return [{\n checkId: 'gateway.runtime_config.blocked',\n severity: 'critical',\n title: 'Gateway startup guards would reject this configuration',\n detail: message,\n remediation: 'Fix the configuration issue above, then run `xopc gateway` again.',\n }];\n }\n}\n\nfunction mergeFindings(findings: SecurityAuditFinding[]): SecurityAuditFinding[] {\n const byId = new Map<string, SecurityAuditFinding>();\n const severityRank = { critical: 3, warn: 2, info: 1 } as const;\n\n for (const finding of findings) {\n const existing = byId.get(finding.checkId);\n if (!existing || severityRank[finding.severity] > severityRank[existing.severity]) {\n byId.set(finding.checkId, finding);\n }\n }\n return [...byId.values()];\n}\n\n/**\n * Full gateway security findings for doctor / CLI audit (config + startup guards).\n */\nexport function collectGatewaySecurityFindings(\n cfg: Config,\n env: NodeJS.ProcessEnv = process.env,\n): SecurityAuditFinding[] {\n const inputs = resolveAuditInputs(cfg, env);\n const configFindings = collectGatewayConfigFindings({\n auth: inputs.auth,\n bindHost: inputs.bindHost,\n corsOrigins: inputs.corsOrigins,\n rateLimitEnabled: inputs.rateLimitEnabled,\n tlsEnabled: inputs.tlsEnabled,\n trustedProxies: inputs.trustedProxies,\n allowRealIpFallback: inputs.allowRealIpFallback,\n dangerouslyAllowHostHeaderOriginFallback: inputs.dangerouslyAllowHostHeaderOriginFallback,\n strictSecurityEnabled: isGatewayStrictSecurityEnabled(cfg),\n rateLimitConfigured: cfg.gateway?.auth?.rateLimit !== undefined,\n });\n const startupFindings = collectGatewayStartupGuardFindings(cfg, env);\n const exposureFindings = collectExposureAuditFindings(cfg);\n return mergeFindings([...configFindings, ...startupFindings, ...exposureFindings]);\n}\n\nfunction emitFindings(findings: SecurityAuditFinding[]): void {\n for (const finding of findings) {\n const logData = {\n checkId: finding.checkId,\n detail: finding.detail,\n ...(finding.remediation ? { remediation: finding.remediation } : {}),\n };\n switch (finding.severity) {\n case 'critical':\n log.error(logData, `Security audit: ${finding.title}`);\n break;\n case 'warn':\n log.warn(logData, `Security audit: ${finding.title}`);\n break;\n case 'info':\n log.info(logData, `Security audit: ${finding.title}`);\n break;\n }\n }\n}\n\n/**\n * Audit the gateway configuration at startup and log security findings.\n */\nexport function auditGatewayConfig(params: {\n auth: ResolvedGatewayAuth;\n bindHost?: string;\n corsOrigins?: string[];\n rateLimitEnabled?: boolean;\n tlsEnabled?: boolean;\n trustedProxies?: string[];\n allowRealIpFallback?: boolean;\n dangerouslyAllowHostHeaderOriginFallback?: boolean;\n strictSecurityEnabled?: boolean;\n rateLimitConfigured?: boolean;\n}): SecurityAuditFinding[] {\n const findings = collectGatewayConfigFindings(params);\n emitFindings(findings);\n return findings;\n}\n"],"mappings":"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"}
|
|
@@ -5,6 +5,5 @@ export { computeInlineScriptHashes, buildGatewayConsoleCspHeader } from './csp.j
|
|
|
5
5
|
export { ADMIN_SCOPE, READ_SCOPE, WRITE_SCOPE, KNOWN_OPERATOR_SCOPES, DEFAULT_OPERATOR_SCOPES, isOperatorScope, authorizeRouteScope, authorizeScope, parseScopesHeader, type OperatorScope, } from './operator-scopes.js';
|
|
6
6
|
export { DEFAULT_GATEWAY_HTTP_TOOL_DENY, isDangerousHttpTool, filterDangerousHttpTools, } from './dangerous-tools.js';
|
|
7
7
|
export { createPreauthConnectionBudget, getMaxPreauthConnectionsPerIp, type PreauthConnectionBudget, } from './preauth-connection-budget.js';
|
|
8
|
-
export { UnauthorizedFloodGuard, type FloodGuardOptions, type FloodGuardDecision, } from './flood-guard.js';
|
|
9
8
|
export { auditGatewayConfig, collectGatewayConfigFindings, collectGatewaySecurityFindings, collectGatewayStartupGuardFindings, type SecurityAuditFinding, } from './audit.js';
|
|
10
9
|
export { wrapExternalContent, wrapWebContent, detectSuspiciousPatterns, type ExternalContentSource, type WrapExternalContentOptions, } from './external-content.js';
|
|
@@ -6,6 +6,5 @@ import { checkBrowserOrigin } from "./origin-check.js";
|
|
|
6
6
|
import { ADMIN_SCOPE, DEFAULT_OPERATOR_SCOPES, KNOWN_OPERATOR_SCOPES, READ_SCOPE, WRITE_SCOPE, authorizeRouteScope, authorizeScope, isOperatorScope, parseScopesHeader } from "./operator-scopes.js";
|
|
7
7
|
import { DEFAULT_GATEWAY_HTTP_TOOL_DENY, filterDangerousHttpTools, isDangerousHttpTool } from "./dangerous-tools.js";
|
|
8
8
|
import { detectSuspiciousPatterns, wrapExternalContent, wrapWebContent } from "./external-content.js";
|
|
9
|
-
import { UnauthorizedFloodGuard } from "./flood-guard.js";
|
|
10
9
|
import { createPreauthConnectionBudget, getMaxPreauthConnectionsPerIp } from "./preauth-connection-budget.js";
|
|
11
|
-
export { ADMIN_SCOPE, DEFAULT_GATEWAY_HTTP_TOOL_DENY, DEFAULT_OPERATOR_SCOPES, KNOWN_OPERATOR_SCOPES, KNOWN_WEAK_GATEWAY_TOKEN_PLACEHOLDERS, READ_SCOPE,
|
|
10
|
+
export { ADMIN_SCOPE, DEFAULT_GATEWAY_HTTP_TOOL_DENY, DEFAULT_OPERATOR_SCOPES, KNOWN_OPERATOR_SCOPES, KNOWN_WEAK_GATEWAY_TOKEN_PLACEHOLDERS, READ_SCOPE, WRITE_SCOPE, assertGatewayAuthNotKnownWeak, auditGatewayConfig, authorizeRouteScope, authorizeScope, buildGatewayConsoleCspHeader, checkBrowserOrigin, collectGatewayConfigFindings, collectGatewaySecurityFindings, collectGatewayStartupGuardFindings, computeInlineScriptHashes, createPreauthConnectionBudget, detectSuspiciousPatterns, filterDangerousHttpTools, getMaxPreauthConnectionsPerIp, isDangerousHttpTool, isOperatorScope, parseScopesHeader, safeEqualSecret, wrapExternalContent, wrapWebContent };
|