@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,129 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type { MiddlewareHandler } from 'hono';
|
|
3
|
+
import type { Env } from '../env.js';
|
|
4
|
+
import type { RuntimeState } from '../state.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Baseline headers on every response (brief §2.6). The CSP applies to HTML
|
|
8
|
+
* we serve (the dashboard SPA) and locks it to self — with hashes for the
|
|
9
|
+
* inline bootstrap script SvelteKit bakes into index.html, computed once at
|
|
10
|
+
* boot. User-uploaded content never renders on this origin (the files
|
|
11
|
+
* module forces attachment for active types), so this CSP is the only HTML
|
|
12
|
+
* policy the origin needs. frame-ancestors 'none' replaces X-Frame-Options.
|
|
13
|
+
*/
|
|
14
|
+
export function inlineScriptHashes(html: string): string[] {
|
|
15
|
+
const hashes: string[] = [];
|
|
16
|
+
const re = /<script(?![^>]*\bsrc=)[^>]*>([\s\S]*?)<\/script>/gi;
|
|
17
|
+
let match: RegExpExecArray | null;
|
|
18
|
+
while ((match = re.exec(html)) !== null) {
|
|
19
|
+
const body = match[1] ?? '';
|
|
20
|
+
if (body.trim().length === 0) continue;
|
|
21
|
+
hashes.push(`'sha256-${createHash('sha256').update(body).digest('base64')}'`);
|
|
22
|
+
}
|
|
23
|
+
return hashes;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CspOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Extra `frame-src` sources beyond 'self'. A dashboard may preview a resource in
|
|
29
|
+
* a sandboxed iframe whose URL honours VIEWER_BASE_URL (PRDCT-1352, DASH-4):
|
|
30
|
+
* with `default-src 'self'` and no `frame-src`, a split viewer origin made
|
|
31
|
+
* that preview a blank frame with no error anywhere but the console.
|
|
32
|
+
*/
|
|
33
|
+
frameSrc?: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Extra `img-src` sources beyond 'self' and `data:`. A dashboard that
|
|
36
|
+
* fetches an image through its API (a request that must carry a header an
|
|
37
|
+
* `<img>` cannot send) shows it from an object URL, and `blob:` must be
|
|
38
|
+
* allowed for that or the browser refuses the picture with no error
|
|
39
|
+
* anywhere but the console. A scheme source here is a tool's declaration,
|
|
40
|
+
* never a chassis default.
|
|
41
|
+
*/
|
|
42
|
+
imgSrc?: readonly string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function buildCsp(scriptHashes: string[], { frameSrc = [], imgSrc = [] }: CspOptions = {}): string {
|
|
46
|
+
const script = ["'self'", ...scriptHashes].join(' ');
|
|
47
|
+
const frame = ["'self'", ...frameSrc].join(' ');
|
|
48
|
+
const img = ["'self'", 'data:', ...imgSrc].join(' ');
|
|
49
|
+
return [
|
|
50
|
+
"default-src 'self'",
|
|
51
|
+
`script-src ${script}`,
|
|
52
|
+
// Svelte injects component styles at runtime; hashes are impractical there.
|
|
53
|
+
// The two font CDNs the dashboard's app.html links (PRDCT-2308): the
|
|
54
|
+
// Observatory pairing rides Fontshare (Sentient, Synonym) and Google
|
|
55
|
+
// Fonts (Onest) because this repo is slated for the OSS cut and the
|
|
56
|
+
// faces may not be committed. Stylesheets from the two API hosts, the
|
|
57
|
+
// font files from their two file hosts, and nothing else — the
|
|
58
|
+
// dashboard's own origin stays the only script and connect source.
|
|
59
|
+
"style-src 'self' 'unsafe-inline' https://api.fontshare.com https://fonts.googleapis.com",
|
|
60
|
+
`img-src ${img}`,
|
|
61
|
+
"connect-src 'self'",
|
|
62
|
+
"font-src 'self' https://cdn.fontshare.com https://fonts.gstatic.com",
|
|
63
|
+
`frame-src ${frame}`,
|
|
64
|
+
"object-src 'none'",
|
|
65
|
+
"frame-ancestors 'none'",
|
|
66
|
+
"base-uri 'self'",
|
|
67
|
+
"form-action 'self'"
|
|
68
|
+
].join('; ');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SecurityHeaderOptions {
|
|
72
|
+
csp: string;
|
|
73
|
+
state: RuntimeState;
|
|
74
|
+
/**
|
|
75
|
+
* `Strict-Transport-Security` value, or null to send none. Computed once at
|
|
76
|
+
* boot from PUBLIC_BASE_URL + HSTS_MAX_AGE (see {@link hstsValue}) rather
|
|
77
|
+
* than sniffed per request: `X-Forwarded-Proto` is client-controlled unless
|
|
78
|
+
* TRUST_PROXY vouches for it, and this origin issues the session cookie —
|
|
79
|
+
* the operator's declared public scheme is the honest signal.
|
|
80
|
+
*/
|
|
81
|
+
hsts?: string | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The HSTS header for this instance, or null when it must not be sent.
|
|
86
|
+
* Only an https PUBLIC_BASE_URL arms it: a browser MUST ignore HSTS received
|
|
87
|
+
* over plain http (RFC 6797 §7.2), and pinning an http-only self-host into
|
|
88
|
+
* https for 180 days would brick it. `HSTS_MAX_AGE=0` is the operator opt-out
|
|
89
|
+
* (e.g. a subdomain that cannot yet serve TLS).
|
|
90
|
+
*/
|
|
91
|
+
export function hstsValue(env: Pick<Env, 'PUBLIC_BASE_URL' | 'HSTS_MAX_AGE'>): string | null {
|
|
92
|
+
if (env.HSTS_MAX_AGE <= 0) return null;
|
|
93
|
+
let isHttps = false;
|
|
94
|
+
try {
|
|
95
|
+
isHttps = new URL(env.PUBLIC_BASE_URL).protocol === 'https:';
|
|
96
|
+
} catch {
|
|
97
|
+
isHttps = false;
|
|
98
|
+
}
|
|
99
|
+
if (!isHttps) return null;
|
|
100
|
+
return `max-age=${env.HSTS_MAX_AGE}; includeSubDomains`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function securityHeaders({ csp, state, hsts }: SecurityHeaderOptions): MiddlewareHandler {
|
|
104
|
+
return async (c, next) => {
|
|
105
|
+
await next();
|
|
106
|
+
c.header('x-content-type-options', 'nosniff');
|
|
107
|
+
// HSTS on EVERY response, not just HTML: the cookie this origin issues is
|
|
108
|
+
// sent with XHR too, so an http downgrade on /api/v1 is the same theft.
|
|
109
|
+
if (hsts && !c.res.headers.has('strict-transport-security')) {
|
|
110
|
+
c.header('strict-transport-security', hsts);
|
|
111
|
+
}
|
|
112
|
+
// Baseline defaults, but NEVER clobber a value a route set for itself:
|
|
113
|
+
// the public viewer (ADR 012) serves user-authored HTML under its own
|
|
114
|
+
// `Content-Security-Policy: sandbox …` + `Referrer-Policy: no-referrer`,
|
|
115
|
+
// and an unconditional overwrite here would silently replace the sandbox
|
|
116
|
+
// CSP with the dashboard CSP — re-opening the session-theft surface the
|
|
117
|
+
// sandbox exists to close (found by the ADR 012 spike; fail-dangerous
|
|
118
|
+
// class, see the ADR's residual-risk #1 and the viewer regression tests).
|
|
119
|
+
if (!c.res.headers.has('referrer-policy')) {
|
|
120
|
+
c.header('referrer-policy', 'strict-origin-when-cross-origin');
|
|
121
|
+
}
|
|
122
|
+
// Draining: ask keep-alive clients to reconnect elsewhere (rolling deploy).
|
|
123
|
+
if (state.draining) c.header('connection', 'close');
|
|
124
|
+
const contentType = c.res.headers.get('content-type') ?? '';
|
|
125
|
+
if (contentType.includes('text/html') && !c.res.headers.has('content-security-policy')) {
|
|
126
|
+
c.header('content-security-policy', csp);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { collectDefaultMetrics, Gauge, Histogram, Registry } from 'prom-client';
|
|
2
|
+
import type { MiddlewareHandler } from 'hono';
|
|
3
|
+
import { Hono } from 'hono';
|
|
4
|
+
import { sql } from 'drizzle-orm';
|
|
5
|
+
import { files, type Db } from '@antasphere/chassis-db';
|
|
6
|
+
import type PgBoss from 'pg-boss';
|
|
7
|
+
import { constantTimeEquals } from '../constant-time.js';
|
|
8
|
+
import { USAGE_QUEUE } from '../jobs/pgboss.js';
|
|
9
|
+
import { routeLabel } from '../route-label.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Prometheus surface. Route labels use the MATCHED pattern (not the raw
|
|
13
|
+
* path) so label cardinality stays bounded. Queue depth and storage bytes
|
|
14
|
+
* are collected lazily at scrape time.
|
|
15
|
+
*/
|
|
16
|
+
export interface Metrics {
|
|
17
|
+
registry: Registry;
|
|
18
|
+
middleware: MiddlewareHandler;
|
|
19
|
+
routes: (token: string | undefined) => Hono;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createMetrics(db: Db, boss: PgBoss): Metrics {
|
|
23
|
+
const registry = new Registry();
|
|
24
|
+
collectDefaultMetrics({ register: registry });
|
|
25
|
+
|
|
26
|
+
const httpDuration = new Histogram({
|
|
27
|
+
name: 'http_request_duration_seconds',
|
|
28
|
+
help: 'HTTP request latency by route',
|
|
29
|
+
labelNames: ['method', 'route', 'status'] as const,
|
|
30
|
+
buckets: [0.005, 0.02, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
|
|
31
|
+
registers: [registry]
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
new Gauge({
|
|
35
|
+
name: 'job_queue_depth',
|
|
36
|
+
help: 'Pending jobs in the usage queue',
|
|
37
|
+
registers: [registry],
|
|
38
|
+
async collect() {
|
|
39
|
+
try {
|
|
40
|
+
this.set((await boss.getQueueSize(USAGE_QUEUE)) ?? 0);
|
|
41
|
+
} catch {
|
|
42
|
+
// queue may not exist yet (api-only role before first worker boot)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
new Gauge({
|
|
48
|
+
name: 'storage_bytes_total',
|
|
49
|
+
help: 'Bytes referenced by live file rows',
|
|
50
|
+
registers: [registry],
|
|
51
|
+
async collect() {
|
|
52
|
+
const [row] = await db
|
|
53
|
+
.select({ total: sql<string>`coalesce(sum(${files.sizeBytes}), 0)::text` })
|
|
54
|
+
.from(files)
|
|
55
|
+
.where(sql`${files.deletedAt} IS NULL`);
|
|
56
|
+
this.set(Number(row?.total ?? '0'));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const middleware: MiddlewareHandler = async (c, next) => {
|
|
61
|
+
const start = performance.now();
|
|
62
|
+
try {
|
|
63
|
+
await next();
|
|
64
|
+
} finally {
|
|
65
|
+
const seconds = (performance.now() - start) / 1000;
|
|
66
|
+
httpDuration.observe(
|
|
67
|
+
{
|
|
68
|
+
method: c.req.method,
|
|
69
|
+
// routeLabel, same as the log line and the span: on the pinned
|
|
70
|
+
// Hono the previous `?? c.req.path` fallback was unreachable
|
|
71
|
+
// (routePath always answers a string here), but a raw-path
|
|
72
|
+
// expression in a label position is exactly what must not come
|
|
73
|
+
// back to life on a version bump — one shared, secret-free
|
|
74
|
+
// label instead (PRIV-1).
|
|
75
|
+
route: routeLabel(c),
|
|
76
|
+
status: String(c.error ? 500 : c.res.status)
|
|
77
|
+
},
|
|
78
|
+
seconds
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const routes = (token: string | undefined): Hono => {
|
|
84
|
+
const app = new Hono();
|
|
85
|
+
app.get('/metrics', async (c) => {
|
|
86
|
+
// Default-closed: route names, queue depth, and storage totals are not
|
|
87
|
+
// for the open internet. setup.sh generates a METRICS_TOKEN.
|
|
88
|
+
if (!token) {
|
|
89
|
+
return c.text('metrics disabled: set METRICS_TOKEN', 401);
|
|
90
|
+
}
|
|
91
|
+
const auth = c.req.header('authorization') ?? '';
|
|
92
|
+
if (!constantTimeEquals(auth, `Bearer ${token}`)) {
|
|
93
|
+
return c.text('unauthorized', 401);
|
|
94
|
+
}
|
|
95
|
+
c.header('content-type', registry.contentType);
|
|
96
|
+
return c.body(await registry.metrics());
|
|
97
|
+
});
|
|
98
|
+
return app;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return { registry, middleware, routes };
|
|
102
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { trace, type Tracer } from '@opentelemetry/api';
|
|
2
|
+
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
3
|
+
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
4
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
5
|
+
import type { MiddlewareHandler } from 'hono';
|
|
6
|
+
import type { Env } from '../env.js';
|
|
7
|
+
import type { Logger } from '../logger.js';
|
|
8
|
+
import { routeLabel } from '../route-label.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Tracing with the zero-phone-home default: spans are recorded ONLY when an
|
|
12
|
+
* operator configures OTEL_EXPORTER_OTLP_ENDPOINT — without it no exporter
|
|
13
|
+
* is registered and nothing leaves the instance. The request id rides every
|
|
14
|
+
* span as `request.id`, which is the correlation key across log line, span,
|
|
15
|
+
* and audit row.
|
|
16
|
+
*/
|
|
17
|
+
export interface Otel {
|
|
18
|
+
tracer: Tracer;
|
|
19
|
+
middleware: MiddlewareHandler;
|
|
20
|
+
shutdown: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function createOtel(
|
|
24
|
+
env: Pick<Env, 'OTEL_EXPORTER_OTLP_ENDPOINT' | 'APP_VERSION' | 'EDITION'>,
|
|
25
|
+
logger: Logger,
|
|
26
|
+
options: { serviceName: string }
|
|
27
|
+
): Promise<Otel> {
|
|
28
|
+
const resource = resourceFromAttributes({
|
|
29
|
+
'service.name': options.serviceName,
|
|
30
|
+
'service.version': env.APP_VERSION,
|
|
31
|
+
'deployment.environment.name': env.EDITION
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const spanProcessors = [];
|
|
35
|
+
if (env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
36
|
+
const { OTLPTraceExporter } = await import('@opentelemetry/exporter-trace-otlp-http');
|
|
37
|
+
spanProcessors.push(
|
|
38
|
+
new BatchSpanProcessor(new OTLPTraceExporter({ url: `${env.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces` }))
|
|
39
|
+
);
|
|
40
|
+
logger.info({ endpoint: env.OTEL_EXPORTER_OTLP_ENDPOINT }, 'otel export enabled');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const provider = new NodeTracerProvider({ resource, spanProcessors });
|
|
44
|
+
provider.register();
|
|
45
|
+
const tracer = trace.getTracer(options.serviceName);
|
|
46
|
+
|
|
47
|
+
const middleware: MiddlewareHandler = async (c, next) => {
|
|
48
|
+
// Start with a low-cardinality name (the matched route pattern isn't known
|
|
49
|
+
// until after routing); rename to the pattern in `finally` so span names
|
|
50
|
+
// stay bounded — a raw path with ids would explode span cardinality.
|
|
51
|
+
await tracer.startActiveSpan(`HTTP ${c.req.method}`, async (span) => {
|
|
52
|
+
span.setAttributes({
|
|
53
|
+
'http.request.method': c.req.method,
|
|
54
|
+
'request.id': c.get('requestId')
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
await next();
|
|
58
|
+
} finally {
|
|
59
|
+
// The MATCHED ROUTE PATTERN, never the raw path (route-label.ts): a
|
|
60
|
+
// span attribute is exported OFF-INSTANCE, so a `/v/<share secret>`
|
|
61
|
+
// there hands a working capability to the trace backend.
|
|
62
|
+
const route = routeLabel(c);
|
|
63
|
+
span.updateName(`${c.req.method} ${route}`);
|
|
64
|
+
span.setAttributes({
|
|
65
|
+
'http.route': route,
|
|
66
|
+
'url.path': route,
|
|
67
|
+
'http.response.status_code': c.error ? 500 : c.res.status
|
|
68
|
+
});
|
|
69
|
+
span.end();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
tracer,
|
|
76
|
+
middleware,
|
|
77
|
+
shutdown: () => provider.shutdown()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { sql, type SQL } from 'drizzle-orm';
|
|
2
|
+
import type { PgColumn, PgTable } from 'drizzle-orm/pg-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Keyset (cursor) pagination over (created_at, id), newest first — the audit
|
|
6
|
+
* route's convention extended to the uuid-keyed tables. Two cursor dialects,
|
|
7
|
+
* one rule: the timestamp's precision never leaves Postgres, because a JS
|
|
8
|
+
* Date round-trip truncates its microseconds and a millisecond-equal
|
|
9
|
+
* boundary row would then be skipped or repeated.
|
|
10
|
+
*
|
|
11
|
+
* - `keysetBefore`: the cursor is the last returned row's plain id, and the
|
|
12
|
+
* timestamp is resolved from that row by subquery at page time. For the
|
|
13
|
+
* tables that never hard-delete a row (members are deactivated, keys and
|
|
14
|
+
* invitations revoked, projects archived): the cursor row is always there.
|
|
15
|
+
* - `keysetBeforeValue`: the cursor CARRIES the timestamp, as the text
|
|
16
|
+
* Postgres itself rendered (`created_at::text`, selected beside the row,
|
|
17
|
+
* never a JS Date), with the id. For a table that hard-deletes rows
|
|
18
|
+
* (project_members): a cursor naming a removed row would compare against
|
|
19
|
+
* NULL under the subquery dialect and end the listing early.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shared UUID shape check — cursors here, and the id params of the
|
|
26
|
+
* plain-Hono routes that have no contract schema (files content/HEAD): a
|
|
27
|
+
* non-UUID string must be rejected before it reaches Postgres' uuid cast.
|
|
28
|
+
*/
|
|
29
|
+
export function isUuid(value: string): boolean {
|
|
30
|
+
return UUID_RE.test(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The cursor row id, or null when absent/malformed. A malformed cursor is
|
|
35
|
+
* silently ignored — the caller serves page 1 (the audit route's
|
|
36
|
+
* `Number('banana')` tolerance).
|
|
37
|
+
*/
|
|
38
|
+
export function cursorRowId(cursor: string | undefined): string | null {
|
|
39
|
+
if (!cursor || !isUuid(cursor)) return null;
|
|
40
|
+
return cursor;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Row-value keyset predicate: everything strictly older than the cursor row,
|
|
45
|
+
* with the cursor row's (created_at, id) resolved by a SQL-side subquery.
|
|
46
|
+
* The subquery is workspace-scoped so a foreign workspace's cursor can never
|
|
47
|
+
* position inside this workspace's ordering. A well-formed-but-unknown id
|
|
48
|
+
* makes the subquery empty → NULL predicate → empty page + nextCursor null;
|
|
49
|
+
* harmless, since cursors are server-issued and these tables never
|
|
50
|
+
* hard-delete rows.
|
|
51
|
+
*/
|
|
52
|
+
export function keysetBefore(opts: {
|
|
53
|
+
table: PgTable;
|
|
54
|
+
id: PgColumn;
|
|
55
|
+
createdAt: PgColumn;
|
|
56
|
+
workspaceId: PgColumn;
|
|
57
|
+
cursorId: string;
|
|
58
|
+
workspace: string;
|
|
59
|
+
}): SQL {
|
|
60
|
+
return sql`(${opts.createdAt}, ${opts.id}) < (SELECT ${opts.createdAt}, ${opts.id} FROM ${opts.table} WHERE ${opts.id} = ${opts.cursorId} AND ${opts.workspaceId} = ${opts.workspace})`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Slice a limit+1 fetch into the page + the audit-style nextCursor (last id of the page). */
|
|
64
|
+
export function pageOf<T extends { id: string }>(
|
|
65
|
+
rows: T[],
|
|
66
|
+
limit: number
|
|
67
|
+
): { page: T[]; nextCursor: string | null } {
|
|
68
|
+
const page = rows.slice(0, limit);
|
|
69
|
+
const nextCursor = rows.length > limit ? (page[page.length - 1]?.id ?? null) : null;
|
|
70
|
+
return { page, nextCursor };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The value-carrying cursor (`keysetBeforeValue`): the row's `created_at` as
|
|
75
|
+
* Postgres rendered it and its id, base64url of `<text>|<id>`. Opaque on the
|
|
76
|
+
* wire like the other dialect; a malformed one is ignored (page 1).
|
|
77
|
+
*/
|
|
78
|
+
const TIMESTAMPTZ_TEXT_RE =
|
|
79
|
+
/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,6})?[+-]\d{2}(?::\d{2}(?::\d{2})?)?$/;
|
|
80
|
+
|
|
81
|
+
export interface KeysetValueCursor {
|
|
82
|
+
/** `created_at::text`, exactly as Postgres rendered it. */
|
|
83
|
+
createdAt: string;
|
|
84
|
+
id: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function encodeKeysetCursor(row: { id: string; createdAtText: string }): string {
|
|
88
|
+
return Buffer.from(`${row.createdAtText}|${row.id}`, 'utf8').toString('base64url');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function decodeKeysetCursor(cursor: string | undefined): KeysetValueCursor | null {
|
|
92
|
+
if (!cursor || cursor.length > 200) return null;
|
|
93
|
+
// Node's base64url decoder never throws (it drops what it cannot read):
|
|
94
|
+
// the guard is the two shape checks below, not the decode.
|
|
95
|
+
const decoded = Buffer.from(cursor, 'base64url').toString('utf8');
|
|
96
|
+
const at = decoded.lastIndexOf('|');
|
|
97
|
+
if (at < 0) return null;
|
|
98
|
+
const createdAt = decoded.slice(0, at);
|
|
99
|
+
const id = decoded.slice(at + 1);
|
|
100
|
+
// Both halves are validated before they reach a Postgres cast: a shape the
|
|
101
|
+
// casts would refuse must answer page 1, never a 500.
|
|
102
|
+
if (!TIMESTAMPTZ_TEXT_RE.test(createdAt) || !isUuid(id)) return null;
|
|
103
|
+
return { createdAt, id };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The `created_at::text` column to select beside a row that will mint a value cursor. */
|
|
107
|
+
export function createdAtText(createdAt: PgColumn): SQL<string> {
|
|
108
|
+
return sql<string>`${createdAt}::text`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** `(created_at, id) < (cursor)` with the cursor's own values; `keysetBefore`'s twin for hard-deleting tables. */
|
|
112
|
+
export function keysetBeforeValue(opts: {
|
|
113
|
+
id: PgColumn;
|
|
114
|
+
createdAt: PgColumn;
|
|
115
|
+
cursor: KeysetValueCursor;
|
|
116
|
+
}): SQL {
|
|
117
|
+
return sql`(${opts.createdAt}, ${opts.id}) < (${opts.cursor.createdAt}::timestamptz, ${opts.cursor.id}::uuid)`;
|
|
118
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { Db } from '@antasphere/chassis-db';
|
|
2
|
+
import type {
|
|
3
|
+
EntitlementService,
|
|
4
|
+
IdentityProvider,
|
|
5
|
+
InstanceAuthDescriptor,
|
|
6
|
+
Principal,
|
|
7
|
+
RequestContext,
|
|
8
|
+
UsageSink
|
|
9
|
+
} from '@antasphere/chassis-contract';
|
|
10
|
+
import { HUB_SSO_PROVIDER_ID } from '../identity/hub-sso.js';
|
|
11
|
+
import { HubLiveGate } from '../identity/hub-live-gate.js';
|
|
12
|
+
import type { HubOrgReconciler } from '../identity/hub-reconcile.js';
|
|
13
|
+
import type { PrincipalGate } from '../middleware/auth-context.js';
|
|
14
|
+
import type { HubConfig } from '../env.js';
|
|
15
|
+
import type { Logger } from '../logger.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The edition split (internal/federation.md): one codebase, one Docker image,
|
|
19
|
+
* two seam bindings selected purely by instance config. This module is the
|
|
20
|
+
* ONLY place edition decides which implementations the boot-time registry
|
|
21
|
+
* gets — `oss` binds the local defaults byte-identically; `cloud` rebinds
|
|
22
|
+
* the hub-federating variants here.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The hub SSO method name the cloud edition advertises in discovery — the
|
|
27
|
+
* better-auth `genericOAuth` providerId registered by identity/hub-sso.ts
|
|
28
|
+
* (its callback lands on /api/v1/auth/oauth2/callback/antasphere, the
|
|
29
|
+
* redirect URI in the hub's TOOL_REGISTRY entry).
|
|
30
|
+
*/
|
|
31
|
+
export const HUB_SSO_METHOD = HUB_SSO_PROVIDER_ID;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The cloud identity binding. Request RESOLUTION stays exactly the local
|
|
35
|
+
* provider's — after the SSO callback mints an ordinary session, every
|
|
36
|
+
* request is sessions + live membership re-check (the live hub gate runs
|
|
37
|
+
* post-resolution in authContext, not here). What changes is the ADVERTISED
|
|
38
|
+
* entrance (D1, hub-only human login):
|
|
39
|
+
*
|
|
40
|
+
* - `antasphere` replaces password/email-otp/google — the login page
|
|
41
|
+
* renders ONLY "Sign in with Antasphere". The local password machinery
|
|
42
|
+
* stays WIRED (hidden, not blocked): the break-glass CLI remains the
|
|
43
|
+
* operator door, and blocking /sign-in/email would dead-end it.
|
|
44
|
+
* - `passwordReset`/`emailChange` are off: credentials and email are the
|
|
45
|
+
* HUB's to manage (D10 re-syncs email at every login); a local reset
|
|
46
|
+
* surface would fight the sync. Since the P8 close (ADR 017) the reset
|
|
47
|
+
* routes don't just report false — they REFUSE on cloud (403; the
|
|
48
|
+
* before-hook in identity/better-auth.ts + the members reset-link mint),
|
|
49
|
+
* so a hub-JIT user can never SET a local password and sidestep SSO.
|
|
50
|
+
* - `twoFactor` is off: the second factor guards local password/OTP
|
|
51
|
+
* sign-ins, which the cloud page no longer offers — MFA is the hub's
|
|
52
|
+
* concern at its own login.
|
|
53
|
+
*/
|
|
54
|
+
class HubSsoIdentityProvider implements IdentityProvider {
|
|
55
|
+
constructor(private readonly local: IdentityProvider) {}
|
|
56
|
+
|
|
57
|
+
resolve(ctx: RequestContext): Promise<Principal | null> {
|
|
58
|
+
return this.local.resolve(ctx);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe(): InstanceAuthDescriptor {
|
|
62
|
+
return {
|
|
63
|
+
// Machine entrances are edition-independent; the human entrance is
|
|
64
|
+
// hub-only. 'oauth' stays: every instance is its own OAuth 2.1 AS for
|
|
65
|
+
// /mcp — the hub never appears in the MCP dance.
|
|
66
|
+
methods: [HUB_SSO_METHOD, 'api-key', 'oauth'],
|
|
67
|
+
passwordReset: false,
|
|
68
|
+
emailChange: false,
|
|
69
|
+
twoFactor: false
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** The three registry seams the edition owns (events/workspaces are edition-independent). */
|
|
75
|
+
export interface EditionSeams {
|
|
76
|
+
identity: IdentityProvider;
|
|
77
|
+
entitlements: EntitlementService;
|
|
78
|
+
usage: UsageSink;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** What the cloud bindings need beyond the seams themselves. */
|
|
82
|
+
export interface EditionBindingDeps {
|
|
83
|
+
db: Db;
|
|
84
|
+
/**
|
|
85
|
+
* The live org reconciler (identity/hub-reconcile.ts), constructed by
|
|
86
|
+
* boot on cloud — the ONE hub data path, shared by the login pass, the
|
|
87
|
+
* live gate, and the unknown-workspace miss hook.
|
|
88
|
+
*/
|
|
89
|
+
reconciler?: HubOrgReconciler | undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The bound seams plus the cloud-only extra boot wires alongside them: the
|
|
94
|
+
* post-resolution principal gate (authContext). Absent on oss by
|
|
95
|
+
* construction.
|
|
96
|
+
*/
|
|
97
|
+
export interface BoundEditionSeams extends EditionSeams {
|
|
98
|
+
principalGate?: PrincipalGate;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Bind the registry seams for this instance's edition. `hub` is the single
|
|
103
|
+
* switch: null (EDITION=oss) returns the local defaults UNTOUCHED — the
|
|
104
|
+
* self-host edition carries zero hub surface at runtime; a HubConfig
|
|
105
|
+
* (EDITION=cloud, validated at env parse) selects the cloud bindings.
|
|
106
|
+
*
|
|
107
|
+
* Entitlements bind LOCAL on BOTH editions: the live gate refuses a
|
|
108
|
+
* suspended/revoked/unavailable principal at the door, before any handler
|
|
109
|
+
* or entitlement check runs — a hub-wrapping entitlement layer would be a
|
|
110
|
+
* second, redundant enforcement path.
|
|
111
|
+
*/
|
|
112
|
+
export function bindEditionSeams(
|
|
113
|
+
hub: HubConfig | null,
|
|
114
|
+
local: EditionSeams,
|
|
115
|
+
logger: Logger,
|
|
116
|
+
deps: EditionBindingDeps
|
|
117
|
+
): BoundEditionSeams {
|
|
118
|
+
if (!hub) return local;
|
|
119
|
+
|
|
120
|
+
logger.info(
|
|
121
|
+
{ hubIssuer: hub.issuerUrl },
|
|
122
|
+
'EDITION=cloud: hub SSO is the human entrance (identity resolution stays local sessions); ' +
|
|
123
|
+
'org/membership truth is reconciled live AS THE USER via each user’s own hub grant ' +
|
|
124
|
+
'(internal/federation.md).'
|
|
125
|
+
);
|
|
126
|
+
const gate = deps.reconciler
|
|
127
|
+
? new HubLiveGate({ db: deps.db, reconciler: deps.reconciler, logger })
|
|
128
|
+
: undefined;
|
|
129
|
+
return {
|
|
130
|
+
// Hub-only entrance advertised; the SSO machinery itself (relying
|
|
131
|
+
// party, JIT, login reconcile) lives in identity/hub-sso.ts and is
|
|
132
|
+
// wired through createAuth — resolution stays local by design.
|
|
133
|
+
identity: new HubSsoIdentityProvider(local.identity),
|
|
134
|
+
entitlements: local.entitlements,
|
|
135
|
+
// D6: usage stays the local sink with a no-op downstream for v1; hub
|
|
136
|
+
// ingest is a one-class downstream swap when it ships (plan §4.3).
|
|
137
|
+
usage: local.usage,
|
|
138
|
+
...(gate ? { principalGate: gate.assert } : {})
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EntitlementDecision,
|
|
3
|
+
EntitlementService,
|
|
4
|
+
MeteredAction,
|
|
5
|
+
Principal,
|
|
6
|
+
RequestQuota
|
|
7
|
+
} from '@antasphere/chassis-contract';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default EntitlementService: everything is allowed, bounded only by
|
|
11
|
+
* instance-level env caps. The cloud/central edition swaps this via the
|
|
12
|
+
* registry for a credits-backed implementation; handlers only ever call
|
|
13
|
+
* `entitlements.check(...)` and never know the difference.
|
|
14
|
+
*/
|
|
15
|
+
export interface InstanceCaps {
|
|
16
|
+
maxFileSizeMb: number;
|
|
17
|
+
/** General-API quota defaults: sustained per-minute + 1 s spike cap (env-driven). */
|
|
18
|
+
apiRequestsPerMinute: number;
|
|
19
|
+
apiRequestsBurstPerSecond: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class AllowAllEntitlements implements EntitlementService {
|
|
23
|
+
constructor(private readonly caps: InstanceCaps) {}
|
|
24
|
+
|
|
25
|
+
async check(_principal: Principal, action: MeteredAction): Promise<EntitlementDecision> {
|
|
26
|
+
if (action.key === 'files.upload' && action.unit === 'bytes') {
|
|
27
|
+
const max = this.caps.maxFileSizeMb * 1024 * 1024;
|
|
28
|
+
if (action.quantity > max) {
|
|
29
|
+
return { allowed: false, reason: `file exceeds MAX_FILE_SIZE_MB (${this.caps.maxFileSizeMb}MB)` };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { allowed: true };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* One flat instance-level quota for every principal. A plan-aware edition
|
|
37
|
+
* returns different values per plan here — the enforcing middleware keys
|
|
38
|
+
* buckets by (quota size, principal), so per-plan variation needs no other
|
|
39
|
+
* change.
|
|
40
|
+
*/
|
|
41
|
+
async getRequestQuota(_principal: Principal): Promise<RequestQuota> {
|
|
42
|
+
return {
|
|
43
|
+
perMinute: this.caps.apiRequestsPerMinute,
|
|
44
|
+
burstPerSecond: this.caps.apiRequestsBurstPerSecond
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|