@antasphere/chassis-server 1.0.0
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/LICENSE +82 -0
- package/dist/accounts/deletion.d.ts +163 -0
- package/dist/accounts/deletion.d.ts.map +1 -0
- package/dist/accounts/deletion.js +360 -0
- package/dist/accounts/deletion.js.map +1 -0
- package/dist/accounts/erasure-log.d.ts +66 -0
- package/dist/accounts/erasure-log.d.ts.map +1 -0
- package/dist/accounts/erasure-log.js +117 -0
- package/dist/accounts/erasure-log.js.map +1 -0
- package/dist/accounts/index.d.ts +6 -0
- package/dist/accounts/index.d.ts.map +1 -0
- package/dist/accounts/index.js +6 -0
- package/dist/accounts/index.js.map +1 -0
- package/dist/accounts/mint-refusal.d.ts +47 -0
- package/dist/accounts/mint-refusal.d.ts.map +1 -0
- package/dist/accounts/mint-refusal.js +59 -0
- package/dist/accounts/mint-refusal.js.map +1 -0
- package/dist/accounts/signup-duplicate.d.ts +14 -0
- package/dist/accounts/signup-duplicate.d.ts.map +1 -0
- package/dist/accounts/signup-duplicate.js +25 -0
- package/dist/accounts/signup-duplicate.js.map +1 -0
- package/dist/accounts/superadmin.d.ts +27 -0
- package/dist/accounts/superadmin.d.ts.map +1 -0
- package/dist/accounts/superadmin.js +46 -0
- package/dist/accounts/superadmin.js.map +1 -0
- package/dist/api/apikeys.d.ts +14 -0
- package/dist/api/apikeys.d.ts.map +1 -0
- package/dist/api/apikeys.js +124 -0
- package/dist/api/apikeys.js.map +1 -0
- package/dist/api/audit.d.ts +4 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +75 -0
- package/dist/api/audit.js.map +1 -0
- package/dist/api/break-glass.d.ts +48 -0
- package/dist/api/break-glass.d.ts.map +1 -0
- package/dist/api/break-glass.js +217 -0
- package/dist/api/break-glass.js.map +1 -0
- package/dist/api/cli-auth.d.ts +84 -0
- package/dist/api/cli-auth.d.ts.map +1 -0
- package/dist/api/cli-auth.js +177 -0
- package/dist/api/cli-auth.js.map +1 -0
- package/dist/api/create-api.d.ts +123 -0
- package/dist/api/create-api.d.ts.map +1 -0
- package/dist/api/create-api.js +1165 -0
- package/dist/api/create-api.js.map +1 -0
- package/dist/api/demo-passes.d.ts +45 -0
- package/dist/api/demo-passes.d.ts.map +1 -0
- package/dist/api/demo-passes.js +143 -0
- package/dist/api/demo-passes.js.map +1 -0
- package/dist/api/errors.d.ts +5 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +5 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/export.d.ts +63 -0
- package/dist/api/export.d.ts.map +1 -0
- package/dist/api/export.js +393 -0
- package/dist/api/export.js.map +1 -0
- package/dist/api/files.d.ts +40 -0
- package/dist/api/files.d.ts.map +1 -0
- package/dist/api/files.js +150 -0
- package/dist/api/files.js.map +1 -0
- package/dist/api/index.d.ts +17 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +17 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/invitations.d.ts +50 -0
- package/dist/api/invitations.d.ts.map +1 -0
- package/dist/api/invitations.js +313 -0
- package/dist/api/invitations.js.map +1 -0
- package/dist/api/members.d.ts +25 -0
- package/dist/api/members.d.ts.map +1 -0
- package/dist/api/members.js +522 -0
- package/dist/api/members.js.map +1 -0
- package/dist/api/onboarding.d.ts +26 -0
- package/dist/api/onboarding.d.ts.map +1 -0
- package/dist/api/onboarding.js +23 -0
- package/dist/api/onboarding.js.map +1 -0
- package/dist/api/openapi-doc.d.ts +21 -0
- package/dist/api/openapi-doc.d.ts.map +1 -0
- package/dist/api/openapi-doc.js +25 -0
- package/dist/api/openapi-doc.js.map +1 -0
- package/dist/api/projects.d.ts +24 -0
- package/dist/api/projects.d.ts.map +1 -0
- package/dist/api/projects.js +624 -0
- package/dist/api/projects.js.map +1 -0
- package/dist/api/scope-routes.d.ts +12 -0
- package/dist/api/scope-routes.d.ts.map +1 -0
- package/dist/api/scope-routes.js +2 -0
- package/dist/api/scope-routes.js.map +1 -0
- package/dist/api/sso-connect.d.ts +70 -0
- package/dist/api/sso-connect.d.ts.map +1 -0
- package/dist/api/sso-connect.js +164 -0
- package/dist/api/sso-connect.js.map +1 -0
- package/dist/api/sso-logout.d.ts +41 -0
- package/dist/api/sso-logout.d.ts.map +1 -0
- package/dist/api/sso-logout.js +34 -0
- package/dist/api/sso-logout.js.map +1 -0
- package/dist/api/teams.d.ts +43 -0
- package/dist/api/teams.d.ts.map +1 -0
- package/dist/api/teams.js +447 -0
- package/dist/api/teams.js.map +1 -0
- package/dist/api/workspaces.d.ts +114 -0
- package/dist/api/workspaces.d.ts.map +1 -0
- package/dist/api/workspaces.js +462 -0
- package/dist/api/workspaces.js.map +1 -0
- package/dist/apikeys/index.d.ts +3 -0
- package/dist/apikeys/index.d.ts.map +1 -0
- package/dist/apikeys/index.js +3 -0
- package/dist/apikeys/index.js.map +1 -0
- package/dist/apikeys/peppers.d.ts +53 -0
- package/dist/apikeys/peppers.d.ts.map +1 -0
- package/dist/apikeys/peppers.js +97 -0
- package/dist/apikeys/peppers.js.map +1 -0
- package/dist/apikeys/service.d.ts +56 -0
- package/dist/apikeys/service.d.ts.map +1 -0
- package/dist/apikeys/service.js +196 -0
- package/dist/apikeys/service.js.map +1 -0
- package/dist/app.d.ts +65 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +142 -0
- package/dist/app.js.map +1 -0
- package/dist/audit/filters.d.ts +38 -0
- package/dist/audit/filters.d.ts.map +1 -0
- package/dist/audit/filters.js +97 -0
- package/dist/audit/filters.js.map +1 -0
- package/dist/audit/index.d.ts +3 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +3 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/service.d.ts +65 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +103 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/boot.d.ts +8 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +822 -0
- package/dist/boot.js.map +1 -0
- package/dist/constant-time.d.ts +3 -0
- package/dist/constant-time.d.ts.map +1 -0
- package/dist/constant-time.js +10 -0
- package/dist/constant-time.js.map +1 -0
- package/dist/email/driver.d.ts +16 -0
- package/dist/email/driver.d.ts.map +1 -0
- package/dist/email/driver.js +136 -0
- package/dist/email/driver.js.map +1 -0
- package/dist/email/index.d.ts +4 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +4 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/shell.d.ts +72 -0
- package/dist/email/shell.d.ts.map +1 -0
- package/dist/email/shell.js +146 -0
- package/dist/email/shell.js.map +1 -0
- package/dist/email/templates.d.ts +83 -0
- package/dist/email/templates.d.ts.map +1 -0
- package/dist/email/templates.js +96 -0
- package/dist/email/templates.js.map +1 -0
- package/dist/entitlements/check.d.ts +140 -0
- package/dist/entitlements/check.d.ts.map +1 -0
- package/dist/entitlements/check.js +276 -0
- package/dist/entitlements/check.js.map +1 -0
- package/dist/entitlements/clock-skew.d.ts +27 -0
- package/dist/entitlements/clock-skew.d.ts.map +1 -0
- package/dist/entitlements/clock-skew.js +58 -0
- package/dist/entitlements/clock-skew.js.map +1 -0
- package/dist/entitlements/gate.d.ts +113 -0
- package/dist/entitlements/gate.d.ts.map +1 -0
- package/dist/entitlements/gate.js +430 -0
- package/dist/entitlements/gate.js.map +1 -0
- package/dist/entitlements/hub-machine-token.d.ts +83 -0
- package/dist/entitlements/hub-machine-token.d.ts.map +1 -0
- package/dist/entitlements/hub-machine-token.js +175 -0
- package/dist/entitlements/hub-machine-token.js.map +1 -0
- package/dist/entitlements/hub-subject.d.ts +32 -0
- package/dist/entitlements/hub-subject.d.ts.map +1 -0
- package/dist/entitlements/hub-subject.js +68 -0
- package/dist/entitlements/hub-subject.js.map +1 -0
- package/dist/entitlements/index.d.ts +9 -0
- package/dist/entitlements/index.d.ts.map +1 -0
- package/dist/entitlements/index.js +9 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/entitlements/poster.d.ts +81 -0
- package/dist/entitlements/poster.d.ts.map +1 -0
- package/dist/entitlements/poster.js +169 -0
- package/dist/entitlements/poster.js.map +1 -0
- package/dist/entitlements/profiles.d.ts +121 -0
- package/dist/entitlements/profiles.d.ts.map +1 -0
- package/dist/entitlements/profiles.js +212 -0
- package/dist/entitlements/profiles.js.map +1 -0
- package/dist/entitlements/slot.d.ts +33 -0
- package/dist/entitlements/slot.d.ts.map +1 -0
- package/dist/entitlements/slot.js +75 -0
- package/dist/entitlements/slot.js.map +1 -0
- package/dist/env.d.ts +293 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +383 -0
- package/dist/env.js.map +1 -0
- package/dist/files/http.d.ts +23 -0
- package/dist/files/http.d.ts.map +1 -0
- package/dist/files/http.js +66 -0
- package/dist/files/http.js.map +1 -0
- package/dist/files/index.d.ts +5 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +5 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/serve.d.ts +43 -0
- package/dist/files/serve.d.ts.map +1 -0
- package/dist/files/serve.js +71 -0
- package/dist/files/serve.js.map +1 -0
- package/dist/files/service.d.ts +69 -0
- package/dist/files/service.d.ts.map +1 -0
- package/dist/files/service.js +188 -0
- package/dist/files/service.js.map +1 -0
- package/dist/files/spool.d.ts +21 -0
- package/dist/files/spool.d.ts.map +1 -0
- package/dist/files/spool.js +47 -0
- package/dist/files/spool.js.map +1 -0
- package/dist/identity/better-auth.d.ts +3927 -0
- package/dist/identity/better-auth.d.ts.map +1 -0
- package/dist/identity/better-auth.js +896 -0
- package/dist/identity/better-auth.js.map +1 -0
- package/dist/identity/demo-pass-plugin.d.ts +29 -0
- package/dist/identity/demo-pass-plugin.d.ts.map +1 -0
- package/dist/identity/demo-pass-plugin.js +88 -0
- package/dist/identity/demo-pass-plugin.js.map +1 -0
- package/dist/identity/demo-pass-rules.d.ts +84 -0
- package/dist/identity/demo-pass-rules.d.ts.map +1 -0
- package/dist/identity/demo-pass-rules.js +223 -0
- package/dist/identity/demo-pass-rules.js.map +1 -0
- package/dist/identity/demo-pass.d.ts +159 -0
- package/dist/identity/demo-pass.d.ts.map +1 -0
- package/dist/identity/demo-pass.js +319 -0
- package/dist/identity/demo-pass.js.map +1 -0
- package/dist/identity/hub-grant.d.ts +182 -0
- package/dist/identity/hub-grant.d.ts.map +1 -0
- package/dist/identity/hub-grant.js +594 -0
- package/dist/identity/hub-grant.js.map +1 -0
- package/dist/identity/hub-jwt.d.ts +22 -0
- package/dist/identity/hub-jwt.d.ts.map +1 -0
- package/dist/identity/hub-jwt.js +77 -0
- package/dist/identity/hub-jwt.js.map +1 -0
- package/dist/identity/hub-live-gate.d.ts +17 -0
- package/dist/identity/hub-live-gate.d.ts.map +1 -0
- package/dist/identity/hub-live-gate.js +146 -0
- package/dist/identity/hub-live-gate.js.map +1 -0
- package/dist/identity/hub-logout.d.ts +88 -0
- package/dist/identity/hub-logout.d.ts.map +1 -0
- package/dist/identity/hub-logout.js +158 -0
- package/dist/identity/hub-logout.js.map +1 -0
- package/dist/identity/hub-projection.d.ts +103 -0
- package/dist/identity/hub-projection.d.ts.map +1 -0
- package/dist/identity/hub-projection.js +130 -0
- package/dist/identity/hub-projection.js.map +1 -0
- package/dist/identity/hub-reconcile.d.ts +151 -0
- package/dist/identity/hub-reconcile.d.ts.map +1 -0
- package/dist/identity/hub-reconcile.js +400 -0
- package/dist/identity/hub-reconcile.js.map +1 -0
- package/dist/identity/hub-sso.d.ts +363 -0
- package/dist/identity/hub-sso.d.ts.map +1 -0
- package/dist/identity/hub-sso.js +536 -0
- package/dist/identity/hub-sso.js.map +1 -0
- package/dist/identity/hub-user-client.d.ts +222 -0
- package/dist/identity/hub-user-client.d.ts.map +1 -0
- package/dist/identity/hub-user-client.js +406 -0
- package/dist/identity/hub-user-client.js.map +1 -0
- package/dist/identity/index.d.ts +13 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +13 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/oauth-jwt.d.ts +32 -0
- package/dist/identity/oauth-jwt.d.ts.map +1 -0
- package/dist/identity/oauth-jwt.js +122 -0
- package/dist/identity/oauth-jwt.js.map +1 -0
- package/dist/identity/resolve-membership.d.ts +28 -0
- package/dist/identity/resolve-membership.d.ts.map +1 -0
- package/dist/identity/resolve-membership.js +69 -0
- package/dist/identity/resolve-membership.js.map +1 -0
- package/dist/identity/signing-key.d.ts +49 -0
- package/dist/identity/signing-key.d.ts.map +1 -0
- package/dist/identity/signing-key.js +144 -0
- package/dist/identity/signing-key.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/invitations/index.d.ts +2 -0
- package/dist/invitations/index.d.ts.map +1 -0
- package/dist/invitations/index.js +2 -0
- package/dist/invitations/index.js.map +1 -0
- package/dist/invitations/service.d.ts +36 -0
- package/dist/invitations/service.d.ts.map +1 -0
- package/dist/invitations/service.js +122 -0
- package/dist/invitations/service.js.map +1 -0
- package/dist/jobs/index.d.ts +3 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +3 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/pgboss.d.ts +119 -0
- package/dist/jobs/pgboss.d.ts.map +1 -0
- package/dist/jobs/pgboss.js +521 -0
- package/dist/jobs/pgboss.js.map +1 -0
- package/dist/jobs/timers.d.ts +127 -0
- package/dist/jobs/timers.d.ts.map +1 -0
- package/dist/jobs/timers.js +228 -0
- package/dist/jobs/timers.js.map +1 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +64 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp/errors.d.ts +37 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +77 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/http.d.ts +35 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/http.js +80 -0
- package/dist/mcp/http.js.map +1 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/inputs.d.ts +19 -0
- package/dist/mcp/inputs.d.ts.map +1 -0
- package/dist/mcp/inputs.js +28 -0
- package/dist/mcp/inputs.js.map +1 -0
- package/dist/mcp/projects.d.ts +31 -0
- package/dist/mcp/projects.d.ts.map +1 -0
- package/dist/mcp/projects.js +315 -0
- package/dist/mcp/projects.js.map +1 -0
- package/dist/mcp/server.d.ts +52 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +93 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/teams.d.ts +7 -0
- package/dist/mcp/teams.d.ts.map +1 -0
- package/dist/mcp/teams.js +62 -0
- package/dist/mcp/teams.js.map +1 -0
- package/dist/mcp/tool-kit.d.ts +67 -0
- package/dist/mcp/tool-kit.d.ts.map +1 -0
- package/dist/mcp/tool-kit.js +74 -0
- package/dist/mcp/tool-kit.js.map +1 -0
- package/dist/members/removal.d.ts +95 -0
- package/dist/members/removal.d.ts.map +1 -0
- package/dist/members/removal.js +143 -0
- package/dist/members/removal.js.map +1 -0
- package/dist/middleware/auth-body.d.ts +25 -0
- package/dist/middleware/auth-body.d.ts.map +1 -0
- package/dist/middleware/auth-body.js +55 -0
- package/dist/middleware/auth-body.js.map +1 -0
- package/dist/middleware/auth-context.d.ts +170 -0
- package/dist/middleware/auth-context.d.ts.map +1 -0
- package/dist/middleware/auth-context.js +251 -0
- package/dist/middleware/auth-context.js.map +1 -0
- package/dist/middleware/body-cap.d.ts +17 -0
- package/dist/middleware/body-cap.d.ts.map +1 -0
- package/dist/middleware/body-cap.js +31 -0
- package/dist/middleware/body-cap.js.map +1 -0
- package/dist/middleware/body-refusal.d.ts +20 -0
- package/dist/middleware/body-refusal.d.ts.map +1 -0
- package/dist/middleware/body-refusal.js +5 -0
- package/dist/middleware/body-refusal.js.map +1 -0
- package/dist/middleware/cross-site.d.ts +61 -0
- package/dist/middleware/cross-site.d.ts.map +1 -0
- package/dist/middleware/cross-site.js +59 -0
- package/dist/middleware/cross-site.js.map +1 -0
- package/dist/middleware/hub-managed.d.ts +45 -0
- package/dist/middleware/hub-managed.d.ts.map +1 -0
- package/dist/middleware/hub-managed.js +56 -0
- package/dist/middleware/hub-managed.js.map +1 -0
- package/dist/middleware/idempotency.d.ts +14 -0
- package/dist/middleware/idempotency.d.ts.map +1 -0
- package/dist/middleware/idempotency.js +222 -0
- package/dist/middleware/idempotency.js.map +1 -0
- package/dist/middleware/index.d.ts +12 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +12 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/json-depth.d.ts +32 -0
- package/dist/middleware/json-depth.d.ts.map +1 -0
- package/dist/middleware/json-depth.js +80 -0
- package/dist/middleware/json-depth.js.map +1 -0
- package/dist/middleware/no-store.d.ts +21 -0
- package/dist/middleware/no-store.d.ts.map +1 -0
- package/dist/middleware/no-store.js +32 -0
- package/dist/middleware/no-store.js.map +1 -0
- package/dist/middleware/oauth-public.d.ts +16 -0
- package/dist/middleware/oauth-public.d.ts.map +1 -0
- package/dist/middleware/oauth-public.js +50 -0
- package/dist/middleware/oauth-public.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +182 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +277 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-id.d.ts +10 -0
- package/dist/middleware/request-id.d.ts.map +1 -0
- package/dist/middleware/request-id.js +42 -0
- package/dist/middleware/request-id.js.map +1 -0
- package/dist/middleware/scopes.d.ts +33 -0
- package/dist/middleware/scopes.d.ts.map +1 -0
- package/dist/middleware/scopes.js +139 -0
- package/dist/middleware/scopes.js.map +1 -0
- package/dist/middleware/security-headers.d.ts +53 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/security-headers.js +96 -0
- package/dist/middleware/security-headers.js.map +1 -0
- package/dist/observability/index.d.ts +3 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +3 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/metrics.d.ts +17 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +82 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/otel.d.ts +20 -0
- package/dist/observability/otel.d.ts.map +1 -0
- package/dist/observability/otel.js +54 -0
- package/dist/observability/otel.js.map +1 -0
- package/dist/pagination.d.ts +57 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +90 -0
- package/dist/pagination.js.map +1 -0
- package/dist/platform/edition.d.ts +57 -0
- package/dist/platform/edition.d.ts.map +1 -0
- package/dist/platform/edition.js +90 -0
- package/dist/platform/edition.js.map +1 -0
- package/dist/platform/entitlements.d.ts +26 -0
- package/dist/platform/entitlements.d.ts.map +1 -0
- package/dist/platform/entitlements.js +28 -0
- package/dist/platform/entitlements.js.map +1 -0
- package/dist/platform/events.d.ts +87 -0
- package/dist/platform/events.d.ts.map +1 -0
- package/dist/platform/events.js +32 -0
- package/dist/platform/events.js.map +1 -0
- package/dist/platform/index.d.ts +8 -0
- package/dist/platform/index.d.ts.map +1 -0
- package/dist/platform/index.js +8 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/local-identity.d.ts +39 -0
- package/dist/platform/local-identity.d.ts.map +1 -0
- package/dist/platform/local-identity.js +87 -0
- package/dist/platform/local-identity.js.map +1 -0
- package/dist/platform/registry.d.ts +23 -0
- package/dist/platform/registry.d.ts.map +1 -0
- package/dist/platform/registry.js +5 -0
- package/dist/platform/registry.js.map +1 -0
- package/dist/platform/usage.d.ts +11 -0
- package/dist/platform/usage.d.ts.map +1 -0
- package/dist/platform/usage.js +12 -0
- package/dist/platform/usage.js.map +1 -0
- package/dist/platform/workspaces.d.ts +25 -0
- package/dist/platform/workspaces.d.ts.map +1 -0
- package/dist/platform/workspaces.js +44 -0
- package/dist/platform/workspaces.js.map +1 -0
- package/dist/projects/access.d.ts +40 -0
- package/dist/projects/access.d.ts.map +1 -0
- package/dist/projects/access.js +133 -0
- package/dist/projects/access.js.map +1 -0
- package/dist/projects/index.d.ts +2 -0
- package/dist/projects/index.d.ts.map +1 -0
- package/dist/projects/index.js +2 -0
- package/dist/projects/index.js.map +1 -0
- package/dist/route-label.d.ts +23 -0
- package/dist/route-label.d.ts.map +1 -0
- package/dist/route-label.js +29 -0
- package/dist/route-label.js.map +1 -0
- package/dist/routes/health.d.ts +30 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +54 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +3 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/wellknown.d.ts +11 -0
- package/dist/routes/wellknown.d.ts.map +1 -0
- package/dist/routes/wellknown.js +45 -0
- package/dist/routes/wellknown.js.map +1 -0
- package/dist/secret.d.ts +30 -0
- package/dist/secret.d.ts.map +1 -0
- package/dist/secret.js +97 -0
- package/dist/secret.js.map +1 -0
- package/dist/setup-transport.d.ts +23 -0
- package/dist/setup-transport.d.ts.map +1 -0
- package/dist/setup-transport.js +52 -0
- package/dist/setup-transport.js.map +1 -0
- package/dist/state.d.ts +21 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +4 -0
- package/dist/state.js.map +1 -0
- package/dist/storage/driver.d.ts +38 -0
- package/dist/storage/driver.d.ts.map +1 -0
- package/dist/storage/driver.js +14 -0
- package/dist/storage/driver.js.map +1 -0
- package/dist/storage/factory.d.ts +4 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +20 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +4 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +21 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +88 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/s3.d.ts +29 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +82 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/testing/fake-hub.d.ts +425 -0
- package/dist/testing/fake-hub.d.ts.map +1 -0
- package/dist/testing/fake-hub.js +1074 -0
- package/dist/testing/fake-hub.js.map +1 -0
- package/dist/testing/helpers.d.ts +124 -0
- package/dist/testing/helpers.d.ts.map +1 -0
- package/dist/testing/helpers.js +174 -0
- package/dist/testing/helpers.js.map +1 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/setup-origins.d.ts +21 -0
- package/dist/testing/setup-origins.d.ts.map +1 -0
- package/dist/testing/setup-origins.js +57 -0
- package/dist/testing/setup-origins.js.map +1 -0
- package/dist/testing/sso-helpers.d.ts +43 -0
- package/dist/testing/sso-helpers.d.ts.map +1 -0
- package/dist/testing/sso-helpers.js +159 -0
- package/dist/testing/sso-helpers.js.map +1 -0
- package/dist/tool-definition.d.ts +382 -0
- package/dist/tool-definition.d.ts.map +1 -0
- package/dist/tool-definition.js +46 -0
- package/dist/tool-definition.js.map +1 -0
- package/dist/util/index.d.ts +8 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +8 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +161 -0
- package/src/accounts/deletion.ts +389 -0
- package/src/accounts/erasure-log.ts +140 -0
- package/src/accounts/index.ts +5 -0
- package/src/accounts/mint-refusal.ts +67 -0
- package/src/accounts/signup-duplicate.ts +21 -0
- package/src/accounts/superadmin.ts +46 -0
- package/src/api/apikeys.ts +147 -0
- package/src/api/audit.ts +83 -0
- package/src/api/break-glass.ts +294 -0
- package/src/api/cli-auth.ts +308 -0
- package/src/api/create-api.ts +1403 -0
- package/src/api/demo-passes.ts +207 -0
- package/src/api/errors.ts +13 -0
- package/src/api/export.ts +482 -0
- package/src/api/files.ts +200 -0
- package/src/api/index.ts +29 -0
- package/src/api/invitations.ts +396 -0
- package/src/api/members.ts +617 -0
- package/src/api/onboarding.ts +50 -0
- package/src/api/openapi-doc.ts +34 -0
- package/src/api/projects.ts +788 -0
- package/src/api/scope-routes.ts +12 -0
- package/src/api/sso-connect.ts +246 -0
- package/src/api/sso-logout.ts +76 -0
- package/src/api/teams.ts +575 -0
- package/src/api/workspaces.ts +649 -0
- package/src/apikeys/index.ts +2 -0
- package/src/apikeys/peppers.ts +118 -0
- package/src/apikeys/service.ts +223 -0
- package/src/app.ts +228 -0
- package/src/audit/filters.ts +101 -0
- package/src/audit/index.ts +9 -0
- package/src/audit/service.ts +154 -0
- package/src/boot.ts +953 -0
- package/src/constant-time.ts +9 -0
- package/src/email/driver.ts +139 -0
- package/src/email/index.ts +26 -0
- package/src/email/shell.ts +201 -0
- package/src/email/templates.ts +184 -0
- package/src/entitlements/check.ts +435 -0
- package/src/entitlements/clock-skew.ts +58 -0
- package/src/entitlements/gate.ts +636 -0
- package/src/entitlements/hub-machine-token.ts +205 -0
- package/src/entitlements/hub-subject.ts +77 -0
- package/src/entitlements/index.ts +37 -0
- package/src/entitlements/poster.ts +265 -0
- package/src/entitlements/profiles.ts +303 -0
- package/src/entitlements/slot.ts +105 -0
- package/src/env.ts +456 -0
- package/src/files/http.ts +77 -0
- package/src/files/index.ts +4 -0
- package/src/files/serve.ts +115 -0
- package/src/files/service.ts +209 -0
- package/src/files/spool.ts +58 -0
- package/src/identity/better-auth.ts +1103 -0
- package/src/identity/demo-pass-plugin.ts +121 -0
- package/src/identity/demo-pass-rules.ts +229 -0
- package/src/identity/demo-pass.ts +390 -0
- package/src/identity/hub-grant.ts +704 -0
- package/src/identity/hub-jwt.ts +78 -0
- package/src/identity/hub-live-gate.ts +167 -0
- package/src/identity/hub-logout.ts +174 -0
- package/src/identity/hub-projection.ts +251 -0
- package/src/identity/hub-reconcile.ts +572 -0
- package/src/identity/hub-sso.ts +696 -0
- package/src/identity/hub-user-client.ts +554 -0
- package/src/identity/index.ts +56 -0
- package/src/identity/oauth-jwt.ts +128 -0
- package/src/identity/resolve-membership.ts +104 -0
- package/src/identity/signing-key.ts +164 -0
- package/src/index.ts +65 -0
- package/src/invitations/index.ts +1 -0
- package/src/invitations/service.ts +171 -0
- package/src/jobs/index.ts +25 -0
- package/src/jobs/pgboss.ts +594 -0
- package/src/jobs/timers.ts +287 -0
- package/src/logger.ts +67 -0
- package/src/mcp/errors.ts +111 -0
- package/src/mcp/http.ts +129 -0
- package/src/mcp/index.ts +28 -0
- package/src/mcp/inputs.ts +33 -0
- package/src/mcp/projects.ts +480 -0
- package/src/mcp/server.ts +174 -0
- package/src/mcp/teams.ts +97 -0
- package/src/mcp/tool-kit.ts +148 -0
- package/src/members/removal.ts +216 -0
- package/src/middleware/auth-body.ts +58 -0
- package/src/middleware/auth-context.ts +384 -0
- package/src/middleware/body-cap.ts +33 -0
- package/src/middleware/body-refusal.ts +23 -0
- package/src/middleware/cross-site.ts +122 -0
- package/src/middleware/hub-managed.ts +61 -0
- package/src/middleware/idempotency.ts +264 -0
- package/src/middleware/index.ts +33 -0
- package/src/middleware/json-depth.ts +74 -0
- package/src/middleware/no-store.ts +30 -0
- package/src/middleware/oauth-public.ts +54 -0
- package/src/middleware/rate-limit.ts +432 -0
- package/src/middleware/request-id.ts +54 -0
- package/src/middleware/scopes.ts +150 -0
- package/src/middleware/security-headers.ts +129 -0
- package/src/observability/index.ts +2 -0
- package/src/observability/metrics.ts +102 -0
- package/src/observability/otel.ts +79 -0
- package/src/pagination.ts +118 -0
- package/src/platform/edition.ts +140 -0
- package/src/platform/entitlements.ts +47 -0
- package/src/platform/events.ts +71 -0
- package/src/platform/index.ts +7 -0
- package/src/platform/local-identity.ts +90 -0
- package/src/platform/registry.ts +28 -0
- package/src/platform/usage.ts +13 -0
- package/src/platform/workspaces.ts +41 -0
- package/src/projects/access.ts +160 -0
- package/src/projects/index.ts +7 -0
- package/src/route-label.ts +29 -0
- package/src/routes/health.ts +79 -0
- package/src/routes/index.ts +2 -0
- package/src/routes/wellknown.ts +67 -0
- package/src/secret.ts +115 -0
- package/src/setup-transport.ts +46 -0
- package/src/state.ts +23 -0
- package/src/storage/driver.ts +48 -0
- package/src/storage/factory.ts +34 -0
- package/src/storage/index.ts +3 -0
- package/src/storage/local.ts +93 -0
- package/src/storage/s3.ts +109 -0
- package/src/testing/fake-hub.ts +1316 -0
- package/src/testing/helpers.ts +254 -0
- package/src/testing/index.ts +11 -0
- package/src/testing/setup-origins.ts +58 -0
- package/src/testing/sso-helpers.ts +178 -0
- package/src/tool-definition.ts +463 -0
- package/src/util/index.ts +17 -0
- package/test/fixtures/onboarding-backfill.sql +13 -0
- package/test/fixtures/operator-user-id-backfill.sql +18 -0
- package/test/host/dr-lib.sh +253 -0
- package/test/host/drizzle/0000_chassis_baseline.sql +438 -0
- package/test/host/drizzle/0001_last_owner_guard.sql +42 -0
- package/test/host/drizzle/meta/0000_snapshot.json +3566 -0
- package/test/host/drizzle/meta/_journal.json +20 -0
- package/test/host/drizzle.config.ts +17 -0
- package/test/host/identity.ts +41 -0
- package/test/host/minimal-host.ts +35 -0
- package/test/host/minimal-tool.ts +123 -0
- package/test/integration/account-oracle-uniformity.test.ts +284 -0
- package/test/integration/account-recovery.test.ts +236 -0
- package/test/integration/account.test.ts +120 -0
- package/test/integration/api-quota.test.ts +208 -0
- package/test/integration/apikey-expiry.test.ts +178 -0
- package/test/integration/apikey-pepper-rotation.test.ts +190 -0
- package/test/integration/boot-posture.test.ts +169 -0
- package/test/integration/break-glass.test.ts +484 -0
- package/test/integration/cloud-lifecycle.test.ts +233 -0
- package/test/integration/credential-boundaries.test.ts +195 -0
- package/test/integration/default-workspace.test.ts +414 -0
- package/test/integration/demo-pass-rules.test.ts +332 -0
- package/test/integration/demo-pass.test.ts +1222 -0
- package/test/integration/dr-verify-real-dump.test.ts +154 -0
- package/test/integration/edition.test.ts +868 -0
- package/test/integration/email-change.test.ts +394 -0
- package/test/integration/empty-tool.test.ts +267 -0
- package/test/integration/entitlements-null-hook.test.ts +187 -0
- package/test/integration/entitlements.test.ts +693 -0
- package/test/integration/export-entries.test.ts +321 -0
- package/test/integration/files.test.ts +320 -0
- package/test/integration/first-boot-claim.test.ts +131 -0
- package/test/integration/gdpr.test.ts +532 -0
- package/test/integration/helpers.ts +29 -0
- package/test/integration/hub-grant.test.ts +505 -0
- package/test/integration/hub-live-gate.test.ts +612 -0
- package/test/integration/hub-managed-membership.test.ts +559 -0
- package/test/integration/hub-org-selector.test.ts +281 -0
- package/test/integration/hub-reconcile.test.ts +646 -0
- package/test/integration/hub-sso.test.ts +704 -0
- package/test/integration/hub-teams.test.ts +771 -0
- package/test/integration/idempotency.test.ts +261 -0
- package/test/integration/invite-verification.test.ts +210 -0
- package/test/integration/last-owner-race.test.ts +263 -0
- package/test/integration/m9-fixes.test.ts +290 -0
- package/test/integration/mcp-projects.test.ts +463 -0
- package/test/integration/member-removal-race.test.ts +466 -0
- package/test/integration/member-removal.test.ts +794 -0
- package/test/integration/oauth-mcp.test.ts +477 -0
- package/test/integration/oauth-workspace.test.ts +371 -0
- package/test/integration/observability.test.ts +183 -0
- package/test/integration/onboarding.test.ts +268 -0
- package/test/integration/orphan-purge.test.ts +451 -0
- package/test/integration/pagination.test.ts +344 -0
- package/test/integration/platform-core.test.ts +305 -0
- package/test/integration/platform.test.ts +246 -0
- package/test/integration/pool-survives-restart.test.ts +80 -0
- package/test/integration/projects-cloud.test.ts +193 -0
- package/test/integration/projects.test.ts +1310 -0
- package/test/integration/security-fixes.test.ts +190 -0
- package/test/integration/setup-transport.test.ts +118 -0
- package/test/integration/signin-wall.test.ts +103 -0
- package/test/integration/signing-key-rotation.test.ts +349 -0
- package/test/integration/sso-helpers.ts +15 -0
- package/test/integration/sso-logout.test.ts +283 -0
- package/test/integration/teams-cloud.test.ts +194 -0
- package/test/integration/teams.test.ts +551 -0
- package/test/integration/timers.test.ts +377 -0
- package/test/integration/two-factor.test.ts +301 -0
- package/test/integration/usage-window.test.ts +161 -0
- package/test/integration/workspace-create-cloud.test.ts +523 -0
- package/test/integration/workspace-create.test.ts +510 -0
- package/test/integration/workspace-scoping.test.ts +530 -0
- package/test/integration/zz-adv-replay-owner.test.ts +205 -0
- package/test/unit/apikey-peppers.test.ts +110 -0
- package/test/unit/apikey-prefix.test.ts +34 -0
- package/test/unit/audit-filters.test.ts +135 -0
- package/test/unit/auth-origins.test.ts +92 -0
- package/test/unit/body-cap.test.ts +84 -0
- package/test/unit/clock-skew.test.ts +68 -0
- package/test/unit/cross-site.test.ts +166 -0
- package/test/unit/declared-credits.test.ts +13 -0
- package/test/unit/demo-pass-rules.test.ts +192 -0
- package/test/unit/email-driver.test.ts +127 -0
- package/test/unit/end-pool.test.ts +181 -0
- package/test/unit/entitlement-check.test.ts +492 -0
- package/test/unit/entitlement-gate.test.ts +1333 -0
- package/test/unit/entitlement-profiles.test.ts +289 -0
- package/test/unit/erasure-log.test.ts +72 -0
- package/test/unit/error-mapping.test.ts +89 -0
- package/test/unit/export-entries.test.ts +85 -0
- package/test/unit/export-sanitize.test.ts +27 -0
- package/test/unit/files-head-route.test.ts +155 -0
- package/test/unit/files-http.test.ts +97 -0
- package/test/unit/head-rewrite.test.ts +72 -0
- package/test/unit/hub-create-org.test.ts +181 -0
- package/test/unit/hub-jwt.test.ts +93 -0
- package/test/unit/hub-sso-params.test.ts +39 -0
- package/test/unit/hub-subject.test.ts +83 -0
- package/test/unit/idempotency-crypto.test.ts +45 -0
- package/test/unit/json-depth.test.ts +88 -0
- package/test/unit/logger-redaction.test.ts +66 -0
- package/test/unit/mcp-errors.test.ts +110 -0
- package/test/unit/mcp-inputs.test.ts +106 -0
- package/test/unit/mcp-project-hints.test.ts +26 -0
- package/test/unit/no-store.test.ts +49 -0
- package/test/unit/no-tool-migration-filename.test.ts +70 -0
- package/test/unit/observability.test.ts +64 -0
- package/test/unit/openapi-doc.test.ts +93 -0
- package/test/unit/rate-limit-consume-on.test.ts +76 -0
- package/test/unit/rate-limit.test.ts +39 -0
- package/test/unit/readyz-storage.test.ts +157 -0
- package/test/unit/request-quota.test.ts +146 -0
- package/test/unit/scopes.test.ts +66 -0
- package/test/unit/security-headers.test.ts +131 -0
- package/test/unit/serve-content-type.test.ts +74 -0
- package/test/unit/setup-transport.test.ts +29 -0
- package/test/unit/storage-local.test.ts +53 -0
- package/test/unit/superadmin.test.ts +32 -0
- package/test/unit/timers.test.ts +287 -0
- package/test/unit/tool-definition-guards.test.ts +166 -0
- package/test/unit/tool-entitlements-guard.test.ts +168 -0
- package/test/unit/usage-poster.test.ts +542 -0
- package/test/unit/usage-window.test.ts +48 -0
- package/test/unit/workspace-create-audit.test.ts +48 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { RateLimiterMemory, RateLimiterRedis, RateLimiterRes } from 'rate-limiter-flexible';
|
|
2
|
+
import { getConnInfo } from '@hono/node-server/conninfo';
|
|
3
|
+
/**
|
|
4
|
+
* Auth-surface rate limits (ours, not Better Auth's built-in): login, OTP
|
|
5
|
+
* sends, invitation acceptance, setup attempts, API-key verification
|
|
6
|
+
* failures. Memory backend per replica by default; Redis when REDIS_URL is
|
|
7
|
+
* set so limits hold across replicas (Profile B).
|
|
8
|
+
*/
|
|
9
|
+
/** Workspace-creation attempts one person gets per hour; an address gets ten times that. */
|
|
10
|
+
export const WORKSPACE_CREATE_PER_PERSON = 60;
|
|
11
|
+
/**
|
|
12
|
+
* The chassis buckets plus the TOOL's own (`toolBuckets`, name → declaration),
|
|
13
|
+
* all built by the same `make`: one store (Redis when REDIS_URL is set, memory
|
|
14
|
+
* otherwise), one key-prefix scheme. A tool bucket can never take a chassis
|
|
15
|
+
* bucket's name — the type refuses it.
|
|
16
|
+
*/
|
|
17
|
+
export async function createRateLimiters(env, logger, toolBuckets) {
|
|
18
|
+
let make;
|
|
19
|
+
if (env.REDIS_URL) {
|
|
20
|
+
const { Redis } = await import('ioredis');
|
|
21
|
+
const client = new Redis(env.REDIS_URL, { enableOfflineQueue: false });
|
|
22
|
+
client.on('error', (err) => logger.error({ err }, 'redis rate-limit store error'));
|
|
23
|
+
make = (prefix, points, duration) => new RateLimiterRedis({
|
|
24
|
+
storeClient: client,
|
|
25
|
+
keyPrefix: `rl:${prefix}`,
|
|
26
|
+
points,
|
|
27
|
+
duration,
|
|
28
|
+
// If Redis blips, fail open to memory rather than dropping requests.
|
|
29
|
+
insuranceLimiter: new RateLimiterMemory({ points, duration })
|
|
30
|
+
});
|
|
31
|
+
logger.info('rate limiting backed by redis');
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
make = (prefix, points, duration) => new RateLimiterMemory({ keyPrefix: `rl:${prefix}`, points, duration });
|
|
35
|
+
}
|
|
36
|
+
const chassis = {
|
|
37
|
+
login: make('login', 10, 15 * 60),
|
|
38
|
+
otp: make('otp', 5, 10 * 60),
|
|
39
|
+
invitationAccept: make('inv-accept', 10, 60 * 60),
|
|
40
|
+
setup: make('setup', 5, 60 * 60),
|
|
41
|
+
apiKeyFailures: make('key-fail', 20, 15 * 60),
|
|
42
|
+
cliConnect: make('cli-connect', Math.max(1, env.CLI_CONNECT_IP_LIMIT_PER_15_MIN ?? 300), 15 * 60),
|
|
43
|
+
oauthRegister: make('oauth-dcr', 10, 60 * 60),
|
|
44
|
+
oauthToken: make('oauth-token', 60, 60),
|
|
45
|
+
// Per ADDRESS: an event's teams' agents share one venue address (2026-10-01: ten times 120).
|
|
46
|
+
mcp: make('mcp', 1200, 60),
|
|
47
|
+
passwordReset: make('pw-reset', 5, 10 * 60),
|
|
48
|
+
workspaceExport: make('ws-export', 5, 600),
|
|
49
|
+
breakGlass: make('break-glass', 10, 60 * 60),
|
|
50
|
+
workspaceCreate: make('ws-create', WORKSPACE_CREATE_PER_PERSON, 60 * 60),
|
|
51
|
+
workspaceCreateAddress: make('ws-create-ip', WORKSPACE_CREATE_PER_PERSON * 10, 60 * 60),
|
|
52
|
+
openapiDoc: make('openapi', 60, 60),
|
|
53
|
+
demoRedeem: make('demo-redeem', 20, 15 * 60)
|
|
54
|
+
};
|
|
55
|
+
// The tool's buckets are built after the chassis ones, in declaration order.
|
|
56
|
+
const tool = {};
|
|
57
|
+
for (const [name, d] of Object.entries(toolBuckets ?? {})) {
|
|
58
|
+
tool[name] = make(d.prefix, d.points, d.durationSec);
|
|
59
|
+
}
|
|
60
|
+
return { ...chassis, ...tool, make };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Client identity for buckets and audit. x-forwarded-for is only believed
|
|
64
|
+
* when TRUST_PROXY says a proxy we control sets it; otherwise the socket
|
|
65
|
+
* address wins and spoofed headers are ignored. We read the RIGHTMOST hop —
|
|
66
|
+
* the one appended (or set) by the trusted proxy itself; the leftmost is
|
|
67
|
+
* client-claimed and spoofable whenever the proxy appends rather than
|
|
68
|
+
* overwrites. Note the compose caveat: without a proxy, Docker NAT can
|
|
69
|
+
* collapse external clients onto one address — per-IP limits are only as
|
|
70
|
+
* granular as what the socket sees.
|
|
71
|
+
*/
|
|
72
|
+
export function makeClientIp(trustProxy) {
|
|
73
|
+
return (c) => {
|
|
74
|
+
if (trustProxy) {
|
|
75
|
+
const hops = c.req.header('x-forwarded-for')?.split(',');
|
|
76
|
+
const xff = hops?.[hops.length - 1]?.trim();
|
|
77
|
+
if (xff)
|
|
78
|
+
return xff;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return getConnInfo(c).remote.address ?? 'unknown';
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// No socket (tests via app.request()) — one shared bucket.
|
|
85
|
+
return 'unknown';
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const RATE_LIMITED_BODY = {
|
|
90
|
+
error: { code: 'rate_limited', message: 'Too many requests, slow down' }
|
|
91
|
+
};
|
|
92
|
+
export function rateLimit(limiter, clientIp, extraKeys, options = {}) {
|
|
93
|
+
const consumeOn = options.consumeOn ?? 'arrival';
|
|
94
|
+
return async (c, next) => {
|
|
95
|
+
const keys = [clientIp(c), ...(extraKeys ? await extraKeys(c) : [])];
|
|
96
|
+
if (consumeOn === 'arrival') {
|
|
97
|
+
try {
|
|
98
|
+
for (const key of keys) {
|
|
99
|
+
await limiter.consume(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return c.json(RATE_LIMITED_BODY, 429);
|
|
104
|
+
}
|
|
105
|
+
return next();
|
|
106
|
+
}
|
|
107
|
+
// Failure-only: read the buckets (no consume) and refuse an exhausted one
|
|
108
|
+
// BEFORE the handler, so a drained wall still costs an attacker nothing to
|
|
109
|
+
// hit and no credential check runs. Backend errors fail open, matching
|
|
110
|
+
// every other limiter here.
|
|
111
|
+
for (const key of keys) {
|
|
112
|
+
const state = await limiter.get(key).catch(() => null);
|
|
113
|
+
if (state && state.remainingPoints <= 0 && state.msBeforeNext > 0) {
|
|
114
|
+
return c.json(RATE_LIMITED_BODY, 429);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
await next();
|
|
118
|
+
// 4xx/5xx = the credential was not accepted. Better Auth answers 401 for a
|
|
119
|
+
// bad password, 400 for a malformed attempt, 403 for a refused origin —
|
|
120
|
+
// all of them are attempts that must cost. A 429 is not: it is a wall
|
|
121
|
+
// behind this one (the library's own 3-per-10-seconds on `/sign-in*`)
|
|
122
|
+
// saying "slow down", no credential was judged. Counting it let a burst of
|
|
123
|
+
// ordinary sign-ins from one address (a room behind one public IP) spend
|
|
124
|
+
// this 15-minute wall in seconds (PRDCT-2830, PRDCT-2848).
|
|
125
|
+
if (c.res.status >= 400 && c.res.status !== 429) {
|
|
126
|
+
for (const key of keys) {
|
|
127
|
+
await limiter.consume(key).catch(() => { });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Per-email key for OTP sends and password login (per IP AND per email): the
|
|
134
|
+
* email key keeps per-account protection intact when NAT or a rotating
|
|
135
|
+
* attacker collapses/expands the IP dimension.
|
|
136
|
+
*/
|
|
137
|
+
export async function emailKeyOf(c) {
|
|
138
|
+
try {
|
|
139
|
+
const body = (await c.req.raw.clone().json());
|
|
140
|
+
if (body?.email && typeof body.email === 'string') {
|
|
141
|
+
return [`email:${body.email.toLowerCase().trim()}`];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// non-JSON body — the IP key alone applies
|
|
146
|
+
}
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
/** RateLimit draft headers + the X- legacy mirror; reset values are delta seconds. */
|
|
150
|
+
export function quotaHeaderEntries(d) {
|
|
151
|
+
const remaining = String(Math.max(0, d.remaining));
|
|
152
|
+
const reset = String(Math.max(0, d.resetSeconds));
|
|
153
|
+
const limit = String(d.limit);
|
|
154
|
+
return [
|
|
155
|
+
['RateLimit-Limit', limit],
|
|
156
|
+
['RateLimit-Remaining', remaining],
|
|
157
|
+
['RateLimit-Reset', reset],
|
|
158
|
+
['X-RateLimit-Limit', limit],
|
|
159
|
+
['X-RateLimit-Remaining', remaining],
|
|
160
|
+
['X-RateLimit-Reset', reset]
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Bucket key for the general API quota, derived ONLY from the authenticated
|
|
165
|
+
* principal — never from a client-supplied header, so a caller can neither
|
|
166
|
+
* rotate its own bucket nor fill another principal's. Two API keys of the
|
|
167
|
+
* same user are separate buckets (a leaked key can be throttled/revoked
|
|
168
|
+
* without starving the owner's other integrations); the same user's session
|
|
169
|
+
* is a third.
|
|
170
|
+
*/
|
|
171
|
+
export function principalBucketKey(p) {
|
|
172
|
+
if (p.via === 'api_key')
|
|
173
|
+
return `key:${p.apiKeyId ?? p.userId}`;
|
|
174
|
+
if (p.via === 'oauth')
|
|
175
|
+
return `oauth:${p.userId}`;
|
|
176
|
+
return `session:${p.userId}`;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Per-principal request quota over the shared rate-limit backend. Two buckets
|
|
180
|
+
* per principal: a sustained per-minute window (the quota agents budget
|
|
181
|
+
* against — headers describe this one) and a 1-second spike cap that smooths
|
|
182
|
+
* thundering herds without burning sustained points on rejected spikes.
|
|
183
|
+
*
|
|
184
|
+
* Limits come from EntitlementService.getRequestQuota so a plan-aware edition
|
|
185
|
+
* varies them per principal; limiter instances are created lazily per
|
|
186
|
+
* distinct quota size (a product has a handful of plans, not thousands) and
|
|
187
|
+
* the size is part of the store prefix, so a plan change moves the principal
|
|
188
|
+
* to a fresh bucket instead of misreading the old counter.
|
|
189
|
+
*
|
|
190
|
+
* Failure posture matches the auth-surface limiters: Redis outages fall back
|
|
191
|
+
* to the per-replica insurance limiter inside `make`; any other backend or
|
|
192
|
+
* entitlement failure logs and FAILS OPEN (availability over throttling — the
|
|
193
|
+
* quota is a cost control, not an authz boundary; authz stays fail-closed).
|
|
194
|
+
*/
|
|
195
|
+
export function createRequestQuota({ entitlements, make, logger }) {
|
|
196
|
+
const sustained = new Map();
|
|
197
|
+
const burst = new Map();
|
|
198
|
+
const limiterFor = (cache, prefix, points, durationSec) => {
|
|
199
|
+
let limiter = cache.get(points);
|
|
200
|
+
if (!limiter) {
|
|
201
|
+
limiter = make(`${prefix}:${points}`, points, durationSec);
|
|
202
|
+
cache.set(points, limiter);
|
|
203
|
+
}
|
|
204
|
+
return limiter;
|
|
205
|
+
};
|
|
206
|
+
return {
|
|
207
|
+
async consume(principal) {
|
|
208
|
+
let perMinute;
|
|
209
|
+
let burstPerSecond;
|
|
210
|
+
try {
|
|
211
|
+
({ perMinute, burstPerSecond } = await entitlements.getRequestQuota(principal));
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
logger.error({ err }, 'request quota lookup failed — allowing request');
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
// Guard a product's custom (non-TS-checked) EntitlementService returning
|
|
218
|
+
// a non-numeric quota: a bare `undefined` would slip past `<= 0` and hand
|
|
219
|
+
// rate-limiter-flexible `points: undefined` (a surprise 4/min). Fail open.
|
|
220
|
+
if (!Number.isFinite(perMinute) || !Number.isFinite(burstPerSecond)) {
|
|
221
|
+
logger.error({ perMinute, burstPerSecond }, 'request quota returned a non-numeric value — allowing request');
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
if (perMinute <= 0)
|
|
225
|
+
return null; // 0 = unlimited, a conscious operator opt-out
|
|
226
|
+
const key = principalBucketKey(principal);
|
|
227
|
+
const sustainedLimiter = limiterFor(sustained, 'apiq', perMinute, 60);
|
|
228
|
+
// Spike cap first: a rejected burst must not burn sustained points —
|
|
229
|
+
// the 1 s bucket self-heals and the minute budget stays intact.
|
|
230
|
+
if (burstPerSecond > 0) {
|
|
231
|
+
try {
|
|
232
|
+
await limiterFor(burst, 'apiq-burst', burstPerSecond, 1).consume(key);
|
|
233
|
+
}
|
|
234
|
+
catch (rejection) {
|
|
235
|
+
if (!(rejection instanceof RateLimiterRes)) {
|
|
236
|
+
logger.error({ err: rejection }, 'rate-limit backend failure — allowing request');
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
// Report the sustained bucket's state (read-only) so headers stay
|
|
240
|
+
// meaningful; the retry hint comes from the 1 s burst window.
|
|
241
|
+
const state = await sustainedLimiter.get(key).catch(() => null);
|
|
242
|
+
return {
|
|
243
|
+
ok: false,
|
|
244
|
+
limit: perMinute,
|
|
245
|
+
remaining: state ? Math.max(0, state.remainingPoints) : perMinute,
|
|
246
|
+
resetSeconds: Math.ceil((state?.msBeforeNext ?? 0) / 1000),
|
|
247
|
+
retryAfterSeconds: Math.max(1, Math.ceil(rejection.msBeforeNext / 1000))
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const res = await sustainedLimiter.consume(key);
|
|
253
|
+
return {
|
|
254
|
+
ok: true,
|
|
255
|
+
limit: perMinute,
|
|
256
|
+
remaining: res.remainingPoints,
|
|
257
|
+
resetSeconds: Math.ceil(res.msBeforeNext / 1000)
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
catch (rejection) {
|
|
261
|
+
if (!(rejection instanceof RateLimiterRes)) {
|
|
262
|
+
logger.error({ err: rejection }, 'rate-limit backend failure — allowing request');
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const retry = Math.max(1, Math.ceil(rejection.msBeforeNext / 1000));
|
|
266
|
+
return {
|
|
267
|
+
ok: false,
|
|
268
|
+
limit: perMinute,
|
|
269
|
+
remaining: 0,
|
|
270
|
+
resetSeconds: retry,
|
|
271
|
+
retryAfterSeconds: retry
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EAEf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAKzD;;;;;GAKG;AACH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAyE9C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAmF,EACnF,MAAc,EACd,WAA0F;IAE1F,IAAI,IAAkF,CAAC;IAEvF,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAClB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,8BAA8B,CAAC,CAAC,CAAC;QAC1F,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAClC,IAAI,gBAAgB,CAAC;YACnB,WAAW,EAAE,MAAM;YACnB,SAAS,EAAE,MAAM,MAAM,EAAE;YACzB,MAAM;YACN,QAAQ;YACR,qEAAqE;YACrE,gBAAgB,EAAE,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;SAC9D,CAAC,CAAC;QACL,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAClC,IAAI,iBAAiB,CAAC,EAAE,SAAS,EAAE,MAAM,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,OAAO,GAAG;QACd,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QACjC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;QAC5B,gBAAgB,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QACjD,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;QAChC,cAAc,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QAC7C,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,+BAA+B,IAAI,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;QACjG,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QAC7C,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,CAAC;QACvC,6FAA6F;QAC7F,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QAC1B,aAAa,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;QAC3C,eAAe,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,GAAG,CAAC;QAC1C,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QAC5C,eAAe,EAAE,IAAI,CAAC,WAAW,EAAE,2BAA2B,EAAE,EAAE,GAAG,EAAE,CAAC;QACxE,sBAAsB,EAAE,IAAI,CAAC,cAAc,EAAE,2BAA2B,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QACvF,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC;QACnC,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;KACR,CAAC;IACvC,6EAA6E;IAC7E,MAAM,IAAI,GAAG,EAA0C,CAAC;IACxD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAwC,EAAE,CAAC;QACjG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAKD;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,UAAmB;IAC9C,OAAO,CAAC,CAAC,EAAE,EAAE;QACX,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YAC5C,IAAI,GAAG;gBAAE,OAAO,GAAG,CAAC;QACtB,CAAC;QACD,IAAI,CAAC;YACH,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,SAAS,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;YAC3D,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAuBD,MAAM,iBAAiB,GAAG;IACxB,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,8BAA8B,EAAE;CAChE,CAAC;AAEX,MAAM,UAAU,SAAS,CACvB,OAA4B,EAC5B,QAAoB,EACpB,SAA6C,EAC7C,UAA4B,EAAE;IAE9B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC;IACjD,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAErE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;YACxC,CAAC;YACD,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,0EAA0E;QAC1E,2EAA2E;QAC3E,uEAAuE;QACvE,4BAA4B;QAC5B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACvD,IAAI,KAAK,IAAI,KAAK,CAAC,eAAe,IAAI,CAAC,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;gBAClE,OAAO,CAAC,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;QACb,2EAA2E;QAC3E,wEAAwE;QACxE,sEAAsE;QACtE,sEAAsE;QACtE,2EAA2E;QAC3E,yEAAyE;QACzE,2DAA2D;QAC3D,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAChD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAU;IACzC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAuB,CAAC;QACpE,IAAI,IAAI,EAAE,KAAK,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAClD,OAAO,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAqBD,sFAAsF;AACtF,MAAM,UAAU,kBAAkB,CAAC,CAAgB;IACjD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO;QACL,CAAC,iBAAiB,EAAE,KAAK,CAAC;QAC1B,CAAC,qBAAqB,EAAE,SAAS,CAAC;QAClC,CAAC,iBAAiB,EAAE,KAAK,CAAC;QAC1B,CAAC,mBAAmB,EAAE,KAAK,CAAC;QAC5B,CAAC,uBAAuB,EAAE,SAAS,CAAC;QACpC,CAAC,mBAAmB,EAAE,KAAK,CAAC;KAC7B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,CAAY;IAC7C,IAAI,CAAC,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;IAChE,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IAClD,OAAO,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC;AAC/B,CAAC;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAoB;IACjF,MAAM,SAAS,GAAG,IAAI,GAAG,EAA+B,CAAC;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IACrD,MAAM,UAAU,GAAG,CACjB,KAAuC,EACvC,MAAc,EACd,MAAc,EACd,WAAmB,EACE,EAAE;QACvB,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;YAC3D,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,SAAS;YACrB,IAAI,SAAiB,CAAC;YACtB,IAAI,cAAsB,CAAC;YAC3B,IAAI,CAAC;gBACH,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,MAAM,YAAY,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC;YAClF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,gDAAgD,CAAC,CAAC;gBACxE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,yEAAyE;YACzE,0EAA0E;YAC1E,2EAA2E;YAC3E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBACpE,MAAM,CAAC,KAAK,CACV,EAAE,SAAS,EAAE,cAAc,EAAE,EAC7B,+DAA+D,CAChE,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,SAAS,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,CAAC,8CAA8C;YAE/E,MAAM,GAAG,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAC1C,MAAM,gBAAgB,GAAG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YAEtE,qEAAqE;YACrE,gEAAgE;YAChE,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC;oBACH,MAAM,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACxE,CAAC;gBAAC,OAAO,SAAS,EAAE,CAAC;oBACnB,IAAI,CAAC,CAAC,SAAS,YAAY,cAAc,CAAC,EAAE,CAAC;wBAC3C,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,+CAA+C,CAAC,CAAC;wBAClF,OAAO,IAAI,CAAC;oBACd,CAAC;oBACD,kEAAkE;oBAClE,8DAA8D;oBAC9D,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;oBAChE,OAAO;wBACL,EAAE,EAAE,KAAK;wBACT,KAAK,EAAE,SAAS;wBAChB,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS;wBACjE,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;wBAC1D,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;qBACzE,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAChD,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,KAAK,EAAE,SAAS;oBAChB,SAAS,EAAE,GAAG,CAAC,eAAe;oBAC9B,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC;iBACjD,CAAC;YACJ,CAAC;YAAC,OAAO,SAAS,EAAE,CAAC;gBACnB,IAAI,CAAC,CAAC,SAAS,YAAY,cAAc,CAAC,EAAE,CAAC;oBAC3C,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,+CAA+C,CAAC,CAAC;oBAClF,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC;gBACpE,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,SAAS;oBAChB,SAAS,EAAE,CAAC;oBACZ,YAAY,EAAE,KAAK;oBACnB,iBAAiB,EAAE,KAAK;iBACzB,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { Logger } from '../logger.js';
|
|
3
|
+
declare module 'hono' {
|
|
4
|
+
interface ContextVariableMap {
|
|
5
|
+
requestId: string;
|
|
6
|
+
logger: Logger;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export declare function requestId(base: Logger): MiddlewareHandler;
|
|
10
|
+
//# sourceMappingURL=request-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-id.d.ts","sourceRoot":"","sources":["../../src/middleware/request-id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB;CACF;AAYD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CA+BzD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { routeLabel } from '../route-label.js';
|
|
3
|
+
/**
|
|
4
|
+
* First middleware in the chain: binds a request id (propagated from
|
|
5
|
+
* x-request-id when a proxy already assigned one) and a child logger carrying
|
|
6
|
+
* it, and writes the completion log line with latency. Everything downstream
|
|
7
|
+
* (traces, audit entries) reuses this id, which is what makes one request
|
|
8
|
+
* followable across log, trace, and audit.
|
|
9
|
+
*/
|
|
10
|
+
/** Propagated ids must be sane: bounded length, log-safe charset. */
|
|
11
|
+
const REQUEST_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
12
|
+
export function requestId(base) {
|
|
13
|
+
return async (c, next) => {
|
|
14
|
+
const incoming = c.req.header('x-request-id');
|
|
15
|
+
const id = incoming && REQUEST_ID_RE.test(incoming) ? incoming : randomUUID();
|
|
16
|
+
const log = base.child({ requestId: id });
|
|
17
|
+
c.set('requestId', id);
|
|
18
|
+
c.set('logger', log);
|
|
19
|
+
c.header('x-request-id', id);
|
|
20
|
+
const start = performance.now();
|
|
21
|
+
try {
|
|
22
|
+
await next();
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
const latencyMs = Math.round((performance.now() - start) * 100) / 100;
|
|
26
|
+
log.info({
|
|
27
|
+
method: c.req.method,
|
|
28
|
+
// The MATCHED ROUTE PATTERN, never the raw URL path — see
|
|
29
|
+
// route-label.ts. On THIS repo that is not hypothetical: the public
|
|
30
|
+
// viewer is `/v/:secret`, so logging c.req.path wrote every live
|
|
31
|
+
// share-link capability into the log stream, where redaction (which
|
|
32
|
+
// keys off object properties) can do nothing about one opaque
|
|
33
|
+
// string. Read after next() so routing has resolved.
|
|
34
|
+
path: routeLabel(c),
|
|
35
|
+
status: c.error ? 500 : c.res.status,
|
|
36
|
+
latencyMs,
|
|
37
|
+
...(c.error ? { failed: true } : {})
|
|
38
|
+
}, 'request');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=request-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-id.js","sourceRoot":"","sources":["../../src/middleware/request-id.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAS/C;;;;;;GAMG;AACH,qEAAqE;AACrE,MAAM,aAAa,GAAG,uBAAuB,CAAC;AAE9C,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC9C,MAAM,EAAE,GAAG,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;QAC9E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACvB,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;YACtE,GAAG,CAAC,IAAI,CACN;gBACE,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;gBACpB,0DAA0D;gBAC1D,oEAAoE;gBACpE,iEAAiE;gBACjE,oEAAoE;gBACpE,8DAA8D;gBAC9D,qDAAqD;gBACrD,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;gBACnB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM;gBACpC,SAAS;gBACT,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrC,EACD,SAAS,CACV,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed endpoint allowlist for machine principals (API keys now, OAuth
|
|
3
|
+
* bearer tokens in M6). Ported from a predecessor template's oauth-scopes.ts.
|
|
4
|
+
*
|
|
5
|
+
* Machine principals are deliberately MORE restricted than the user they
|
|
6
|
+
* belong to: they can only reach endpoints listed here, each behind its
|
|
7
|
+
* scope. Anything not listed returns null and the gate 403s — new endpoints
|
|
8
|
+
* are unreachable to keys/tokens until someone consciously opens them.
|
|
9
|
+
* Session principals are role-gated instead and never pass through this.
|
|
10
|
+
*
|
|
11
|
+
* Pure logic (no I/O) so it stays unit-testable. The chassis owns the
|
|
12
|
+
* MECHANISM and the rules of its own endpoints; the scope NAMES and the rules
|
|
13
|
+
* of the tool's endpoints are the tool's (`createScopeAllowlist`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function looksLikeJwt(token: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* One tool rule: the scope a machine principal needs for this request, or
|
|
18
|
+
* null when the rule does not speak about it (the next rule is asked; no rule
|
|
19
|
+
* speaking = denied).
|
|
20
|
+
*/
|
|
21
|
+
export type ScopeRule<S extends string> = (path: string, method: string, isRead: boolean) => S | null;
|
|
22
|
+
export interface ScopeAllowlistOptions<S extends string> {
|
|
23
|
+
/** The tool's scope behind the chassis READ surfaces (`/me`, the files reads). */
|
|
24
|
+
read: S;
|
|
25
|
+
/** The tool's scope behind the chassis MUTATIONS (files writes, the CLI key self-revoke). */
|
|
26
|
+
write: S;
|
|
27
|
+
/** The tool's dedicated opt-in scope for the full-workspace export. */
|
|
28
|
+
dataExport: S;
|
|
29
|
+
/** The tool's own rules, asked in order AFTER the chassis rules. */
|
|
30
|
+
rules: ReadonlyArray<ScopeRule<S>>;
|
|
31
|
+
}
|
|
32
|
+
export declare function createScopeAllowlist<S extends string>({ read, write, dataExport, rules }: ScopeAllowlistOptions<S>): (path: string, method: string) => S | null;
|
|
33
|
+
//# sourceMappingURL=scopes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/middleware/scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC;AAEtG,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,MAAM;IACrD,kFAAkF;IAClF,IAAI,EAAE,CAAC,CAAC;IACR,6FAA6F;IAC7F,KAAK,EAAE,CAAC,CAAC;IACT,uEAAuE;IACvE,UAAU,EAAE,CAAC,CAAC;IACd,oEAAoE;IACpE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CACpC;AAqDD,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,EAAE,EACrD,IAAI,EACJ,KAAK,EACL,UAAU,EACV,KAAK,EACN,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,GAAG,IAAI,CAyDvE"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed endpoint allowlist for machine principals (API keys now, OAuth
|
|
3
|
+
* bearer tokens in M6). Ported from a predecessor template's oauth-scopes.ts.
|
|
4
|
+
*
|
|
5
|
+
* Machine principals are deliberately MORE restricted than the user they
|
|
6
|
+
* belong to: they can only reach endpoints listed here, each behind its
|
|
7
|
+
* scope. Anything not listed returns null and the gate 403s — new endpoints
|
|
8
|
+
* are unreachable to keys/tokens until someone consciously opens them.
|
|
9
|
+
* Session principals are role-gated instead and never pass through this.
|
|
10
|
+
*
|
|
11
|
+
* Pure logic (no I/O) so it stays unit-testable. The chassis owns the
|
|
12
|
+
* MECHANISM and the rules of its own endpoints; the scope NAMES and the rules
|
|
13
|
+
* of the tool's endpoints are the tool's (`createScopeAllowlist`).
|
|
14
|
+
*/
|
|
15
|
+
export function looksLikeJwt(token) {
|
|
16
|
+
return token.split('.').length === 3;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Builds the allowlist function: the chassis rules FIRST, then the tool's
|
|
20
|
+
* rules in order, and `null` (the gate's 403 `endpoint_not_allowed`) when
|
|
21
|
+
* nothing matched — fail-closed by construction.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The chassis project routes a machine principal may reach, EXACT shapes only:
|
|
25
|
+
* reads under the read scope, writes under the write scope. Anything else
|
|
26
|
+
* under `/projects` answers null HERE, so a tool's own route on a project (its
|
|
27
|
+
* link to the project, say) is the tool's rule to open, and an unknown shape
|
|
28
|
+
* stays closed.
|
|
29
|
+
*/
|
|
30
|
+
const PROJECT_ID = '[0-9a-fA-F-]{36}';
|
|
31
|
+
const PROJECT_RE = new RegExp(`^/api/v1/projects/${PROJECT_ID}$`);
|
|
32
|
+
const PROJECT_ARCHIVE_RE = new RegExp(`^/api/v1/projects/${PROJECT_ID}/(?:archive|unarchive)$`);
|
|
33
|
+
const PROJECT_MEMBERS_RE = new RegExp(`^/api/v1/projects/${PROJECT_ID}/members$`);
|
|
34
|
+
const PROJECT_MEMBER_RE = new RegExp(`^/api/v1/projects/${PROJECT_ID}/members/[^/]+$`);
|
|
35
|
+
const PROJECT_TEAM_RE = new RegExp(`^/api/v1/projects/${PROJECT_ID}/teams/${PROJECT_ID}$`);
|
|
36
|
+
function projectAccess(path, method) {
|
|
37
|
+
if (path === '/api/v1/projects')
|
|
38
|
+
return method === 'GET' ? 'read' : method === 'POST' ? 'write' : null;
|
|
39
|
+
if (PROJECT_RE.test(path))
|
|
40
|
+
return method === 'GET' ? 'read' : method === 'PATCH' ? 'write' : null;
|
|
41
|
+
if (PROJECT_ARCHIVE_RE.test(path))
|
|
42
|
+
return method === 'POST' ? 'write' : null;
|
|
43
|
+
if (PROJECT_MEMBERS_RE.test(path))
|
|
44
|
+
return method === 'GET' ? 'read' : method === 'POST' ? 'write' : null;
|
|
45
|
+
if (PROJECT_MEMBER_RE.test(path))
|
|
46
|
+
return method === 'PATCH' || method === 'DELETE' ? 'write' : null;
|
|
47
|
+
if (PROJECT_TEAM_RE.test(path))
|
|
48
|
+
return method === 'PATCH' || method === 'DELETE' ? 'write' : null;
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The chassis team routes a machine principal may reach (PRDCT-2813), EXACT
|
|
53
|
+
* shapes only, the projects' pattern: the reads under the read scope, the
|
|
54
|
+
* writes under the write scope, the handler's owner-or-admin check staying
|
|
55
|
+
* the authority (and the hub-managed gate the refusal in a hub-origin
|
|
56
|
+
* workspace). A person is named by their user id in the path, never by a
|
|
57
|
+
* membership id.
|
|
58
|
+
*/
|
|
59
|
+
const TEAM_RE = new RegExp(`^/api/v1/teams/${PROJECT_ID}$`);
|
|
60
|
+
const TEAM_MEMBERS_RE = new RegExp(`^/api/v1/teams/${PROJECT_ID}/members$`);
|
|
61
|
+
const TEAM_MEMBER_RE = new RegExp(`^/api/v1/teams/${PROJECT_ID}/members/[^/]+$`);
|
|
62
|
+
function teamAccess(path, method) {
|
|
63
|
+
if (path === '/api/v1/teams')
|
|
64
|
+
return method === 'GET' ? 'read' : method === 'POST' ? 'write' : null;
|
|
65
|
+
if (TEAM_RE.test(path)) {
|
|
66
|
+
return method === 'GET' ? 'read' : method === 'PATCH' || method === 'DELETE' ? 'write' : null;
|
|
67
|
+
}
|
|
68
|
+
if (TEAM_MEMBERS_RE.test(path))
|
|
69
|
+
return method === 'GET' ? 'read' : method === 'POST' ? 'write' : null;
|
|
70
|
+
if (TEAM_MEMBER_RE.test(path))
|
|
71
|
+
return method === 'DELETE' ? 'write' : null;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
export function createScopeAllowlist({ read, write, dataExport, rules }) {
|
|
75
|
+
return function requiredScopeFor(path, method) {
|
|
76
|
+
const isRead = method === 'GET' || method === 'HEAD';
|
|
77
|
+
if (path === '/api/v1/me' && isRead)
|
|
78
|
+
return read;
|
|
79
|
+
// Files: the substrate machine callers actually need (agents pushing and
|
|
80
|
+
// pulling artifacts). Reads → the read scope, mutations → the write scope.
|
|
81
|
+
if (path === '/api/v1/files' || path.startsWith('/api/v1/files/')) {
|
|
82
|
+
return isRead ? read : write;
|
|
83
|
+
}
|
|
84
|
+
// CLI logout self-revoke (api/cli-auth.ts): the ONE /cli/auth route open
|
|
85
|
+
// to machines — DELETE revokes exactly the PRESENTING key. Safe to open
|
|
86
|
+
// because the route names no key (no id parameter): the handler acts only
|
|
87
|
+
// on principal.apiKeyId, resolved from the presented secret, and refuses
|
|
88
|
+
// every non-key principal — so the widest possible effect is a credential
|
|
89
|
+
// revoking ITSELF. The write scope because revocation is a mutation
|
|
90
|
+
// and every CLI-minted key carries it (mirrors the hub's account:write
|
|
91
|
+
// entry). Exact path + method match: any OTHER method on the path, and
|
|
92
|
+
// the PUBLIC mint routes (/cli/auth/request, /cli/auth/complete — which
|
|
93
|
+
// never reach this gate anyway), stay fail-closed.
|
|
94
|
+
if (path === '/api/v1/cli/auth/key' && method === 'DELETE')
|
|
95
|
+
return write;
|
|
96
|
+
// The person's default workspace (api/workspaces.ts, PRDCT-2815): a
|
|
97
|
+
// setting of the PERSON a credential stands for, which the CLI sets.
|
|
98
|
+
// The route names no workspace and acts on the caller's own membership
|
|
99
|
+
// rows only; a key pinned to one workspace is refused by the handler.
|
|
100
|
+
// Exact path + method: nothing else under /me opens with it.
|
|
101
|
+
if (path === '/api/v1/me/default-workspace' && method === 'PUT')
|
|
102
|
+
return write;
|
|
103
|
+
// Full-workspace export: a dedicated opt-in scope, NEVER the read scope — any
|
|
104
|
+
// admin read key would otherwise be a whole-tenant exfiltration tool.
|
|
105
|
+
// (Account deletion, DELETE /members/{id}, stays deliberately UNLISTED:
|
|
106
|
+
// machines 403 fail-closed; deleting people is a session-only act.
|
|
107
|
+
// Break-glass, /admin/break-glass/*, is likewise deliberately UNLISTED:
|
|
108
|
+
// superadmin recovery is a human session act — a key or token whose owner
|
|
109
|
+
// is on SUPERADMIN_EMAILS still 403s here, fail-closed. Never list it.)
|
|
110
|
+
if (path === '/api/v1/workspace/export' && isRead)
|
|
111
|
+
return dataExport;
|
|
112
|
+
// Projects: each route listed by its exact shape and method (above).
|
|
113
|
+
const project = projectAccess(path, method);
|
|
114
|
+
if (project !== null)
|
|
115
|
+
return project === 'read' ? read : write;
|
|
116
|
+
// Teams: the same discipline (above).
|
|
117
|
+
const team = teamAccess(path, method);
|
|
118
|
+
if (team !== null)
|
|
119
|
+
return team === 'read' ? read : write;
|
|
120
|
+
for (const rule of rules) {
|
|
121
|
+
const needed = rule(path, method, isRead);
|
|
122
|
+
if (needed !== null)
|
|
123
|
+
return needed;
|
|
124
|
+
}
|
|
125
|
+
// Deliberately UNLISTED chassis surfaces (fail-closed 403 for
|
|
126
|
+
// keys/tokens), like break-glass:
|
|
127
|
+
// - /sso/logout (cloud) — single logout is a BROWSER act: a machine
|
|
128
|
+
// credential must never be able to end its user's sessions. Never
|
|
129
|
+
// list it.
|
|
130
|
+
// - POST /workspaces — creating a workspace (on cloud: an organization at
|
|
131
|
+
// the hub, as the user) is a HUMAN act from the dashboard. A key or a
|
|
132
|
+
// token must never be able to mint tenants for its holder; the handler
|
|
133
|
+
// re-checks `via === 'session'` on top (api/workspaces.ts). Never list it.
|
|
134
|
+
// - /me/onboarding/dismiss (cloud) — the first-run welcome is a browser
|
|
135
|
+
// concern; machines carry no banner to dismiss.
|
|
136
|
+
return null;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=scopes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../src/middleware/scopes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { Env } from '../env.js';
|
|
3
|
+
import type { RuntimeState } from '../state.js';
|
|
4
|
+
/**
|
|
5
|
+
* Baseline headers on every response (brief §2.6). The CSP applies to HTML
|
|
6
|
+
* we serve (the dashboard SPA) and locks it to self — with hashes for the
|
|
7
|
+
* inline bootstrap script SvelteKit bakes into index.html, computed once at
|
|
8
|
+
* boot. User-uploaded content never renders on this origin (the files
|
|
9
|
+
* module forces attachment for active types), so this CSP is the only HTML
|
|
10
|
+
* policy the origin needs. frame-ancestors 'none' replaces X-Frame-Options.
|
|
11
|
+
*/
|
|
12
|
+
export declare function inlineScriptHashes(html: string): string[];
|
|
13
|
+
export interface CspOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Extra `frame-src` sources beyond 'self'. A dashboard may preview a resource in
|
|
16
|
+
* a sandboxed iframe whose URL honours VIEWER_BASE_URL (PRDCT-1352, DASH-4):
|
|
17
|
+
* with `default-src 'self'` and no `frame-src`, a split viewer origin made
|
|
18
|
+
* that preview a blank frame with no error anywhere but the console.
|
|
19
|
+
*/
|
|
20
|
+
frameSrc?: readonly string[];
|
|
21
|
+
/**
|
|
22
|
+
* Extra `img-src` sources beyond 'self' and `data:`. A dashboard that
|
|
23
|
+
* fetches an image through its API (a request that must carry a header an
|
|
24
|
+
* `<img>` cannot send) shows it from an object URL, and `blob:` must be
|
|
25
|
+
* allowed for that or the browser refuses the picture with no error
|
|
26
|
+
* anywhere but the console. A scheme source here is a tool's declaration,
|
|
27
|
+
* never a chassis default.
|
|
28
|
+
*/
|
|
29
|
+
imgSrc?: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function buildCsp(scriptHashes: string[], { frameSrc, imgSrc }?: CspOptions): string;
|
|
32
|
+
export interface SecurityHeaderOptions {
|
|
33
|
+
csp: string;
|
|
34
|
+
state: RuntimeState;
|
|
35
|
+
/**
|
|
36
|
+
* `Strict-Transport-Security` value, or null to send none. Computed once at
|
|
37
|
+
* boot from PUBLIC_BASE_URL + HSTS_MAX_AGE (see {@link hstsValue}) rather
|
|
38
|
+
* than sniffed per request: `X-Forwarded-Proto` is client-controlled unless
|
|
39
|
+
* TRUST_PROXY vouches for it, and this origin issues the session cookie —
|
|
40
|
+
* the operator's declared public scheme is the honest signal.
|
|
41
|
+
*/
|
|
42
|
+
hsts?: string | null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The HSTS header for this instance, or null when it must not be sent.
|
|
46
|
+
* Only an https PUBLIC_BASE_URL arms it: a browser MUST ignore HSTS received
|
|
47
|
+
* over plain http (RFC 6797 §7.2), and pinning an http-only self-host into
|
|
48
|
+
* https for 180 days would brick it. `HSTS_MAX_AGE=0` is the operator opt-out
|
|
49
|
+
* (e.g. a subdomain that cannot yet serve TLS).
|
|
50
|
+
*/
|
|
51
|
+
export declare function hstsValue(env: Pick<Env, 'PUBLIC_BASE_URL' | 'HSTS_MAX_AGE'>): string | null;
|
|
52
|
+
export declare function securityHeaders({ csp, state, hsts }: SecurityHeaderOptions): MiddlewareHandler;
|
|
53
|
+
//# sourceMappingURL=security-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/middleware/security-headers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUzD;AAED,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,QAAa,EAAE,MAAW,EAAE,GAAE,UAAe,GAAG,MAAM,CAwBxG;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,YAAY,CAAC;IACpB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,iBAAiB,GAAG,cAAc,CAAC,GAAG,MAAM,GAAG,IAAI,CAU3F;AAED,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,qBAAqB,GAAG,iBAAiB,CA0B9F"}
|