@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,71 @@
|
|
|
1
|
+
import type { Logger } from '../logger.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Internal event bus: domain modules publish, platform modules (and later
|
|
5
|
+
* rail/ee modules registered at boot) subscribe. Deliberately synchronous
|
|
6
|
+
* fan-out with error isolation — a failing subscriber never breaks the
|
|
7
|
+
* publisher's request path.
|
|
8
|
+
*/
|
|
9
|
+
export type PlatformEvents = {
|
|
10
|
+
/**
|
|
11
|
+
* Fired by the identity layer (Better Auth databaseHooks.user.create.after,
|
|
12
|
+
* wired in boot) for EVERY account entrance — setup, invitation accept,
|
|
13
|
+
* collaborator claim, future SSO JIT. Trustworthy by construction; call
|
|
14
|
+
* sites never emit it.
|
|
15
|
+
*/
|
|
16
|
+
'user.created': { userId: string; email: string };
|
|
17
|
+
'member.joined': { workspaceId: string; userId: string; role: string };
|
|
18
|
+
'member.updated': { workspaceId: string; userId: string };
|
|
19
|
+
'member.removed': { workspaceId: string; userId: string };
|
|
20
|
+
'apikey.created': { workspaceId: string; apiKeyId: string };
|
|
21
|
+
'apikey.revoked': { workspaceId: string; apiKeyId: string };
|
|
22
|
+
'invitation.created': { workspaceId: string; invitationId: string };
|
|
23
|
+
'invitation.accepted': { workspaceId: string; invitationId: string; userId: string };
|
|
24
|
+
'file.uploaded': { workspaceId: string; fileId: string; sizeBytes: number };
|
|
25
|
+
/** workspaceId is null on the cloud edition — setup creates no workspace there. */
|
|
26
|
+
/** A person created ANOTHER workspace from inside the product (POST /workspaces). */
|
|
27
|
+
'workspace.created': { workspaceId: string; ownerUserId: string };
|
|
28
|
+
'setup.completed': { workspaceId: string | null; instanceId: string };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The events a bus carries: the chassis ones plus the tool's own (`TExtra`, a
|
|
33
|
+
* map of event name to payload, declared by the tool that instantiates the bus).
|
|
34
|
+
*/
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
36
|
+
export type EventMap<TExtra = {}> = PlatformEvents & TExtra;
|
|
37
|
+
|
|
38
|
+
type Handler<M, E extends keyof M> = (payload: M[E]) => void | Promise<void>;
|
|
39
|
+
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
41
|
+
export class EventBus<TExtra = {}> {
|
|
42
|
+
private handlers = new Map<keyof EventMap<TExtra>, Set<Handler<EventMap<TExtra>, never>>>();
|
|
43
|
+
|
|
44
|
+
constructor(private readonly logger: Logger) {}
|
|
45
|
+
|
|
46
|
+
on<E extends keyof EventMap<TExtra>>(event: E, handler: Handler<EventMap<TExtra>, E>): () => void {
|
|
47
|
+
const set = this.handlers.get(event) ?? new Set();
|
|
48
|
+
set.add(handler as Handler<EventMap<TExtra>, never>);
|
|
49
|
+
this.handlers.set(event, set);
|
|
50
|
+
return () => set.delete(handler as Handler<EventMap<TExtra>, never>);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The SAME bus as the chassis sees it: the generic events only. The
|
|
55
|
+
* composition code is generic over the tool's event map, and a bus is
|
|
56
|
+
* invariant in it (it both takes and hands out payloads), so the chassis
|
|
57
|
+
* modules, written against the generic map, read the bus through this view.
|
|
58
|
+
* Sound as long as a tool never re-declares a chassis event name.
|
|
59
|
+
*/
|
|
60
|
+
chassisView(): EventBus {
|
|
61
|
+
return this as unknown as EventBus;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
emit<E extends keyof EventMap<TExtra>>(event: E, payload: EventMap<TExtra>[E]): void {
|
|
65
|
+
for (const handler of this.handlers.get(event) ?? []) {
|
|
66
|
+
Promise.resolve((handler as Handler<EventMap<TExtra>, E>)(payload)).catch((err) => {
|
|
67
|
+
this.logger.error({ err, event }, 'event handler failed');
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AllowAllEntitlements } from './entitlements.js';
|
|
2
|
+
export { EventBus } from './events.js';
|
|
3
|
+
export { type PlatformRegistry, createRegistry } from './registry.js';
|
|
4
|
+
export { NoopUsageSink } from './usage.js';
|
|
5
|
+
export { WorkspaceService } from './workspaces.js';
|
|
6
|
+
export { bindEditionSeams } from './edition.js';
|
|
7
|
+
export { LocalIdentityProvider } from './local-identity.js';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type Db } from '@antasphere/chassis-db';
|
|
2
|
+
import {
|
|
3
|
+
ACTIVE_WORKSPACE_HEADER,
|
|
4
|
+
type IdentityProvider,
|
|
5
|
+
type InstanceAuthDescriptor,
|
|
6
|
+
type Principal,
|
|
7
|
+
type RequestContext
|
|
8
|
+
} from '@antasphere/chassis-contract';
|
|
9
|
+
import type { Auth } from '../identity/better-auth.js';
|
|
10
|
+
import {
|
|
11
|
+
isWorkspaceSelector,
|
|
12
|
+
resolveMembership,
|
|
13
|
+
type OnWorkspaceMiss
|
|
14
|
+
} from '../identity/resolve-membership.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The template's default IdentityProvider: Better Auth sessions + a LIVE
|
|
18
|
+
* workspace_members re-check on every request. The membership row — not the
|
|
19
|
+
* cookie — is the authorization decision, so deactivating a member locks
|
|
20
|
+
* them out instantly regardless of cookie age.
|
|
21
|
+
*
|
|
22
|
+
* Workspace scoping (ADR 014, user-scoped credential model): a session
|
|
23
|
+
* resolves to exactly ONE workspace per request via the shared
|
|
24
|
+
* resolveMembership rule — the X-Workspace-Id header names it (ACTIVE
|
|
25
|
+
* membership required, else null: fail closed, an unknown workspace and a
|
|
26
|
+
* workspace the user does not belong to are indistinguishable); without the
|
|
27
|
+
* header the user's default membership wins, else the deterministic oldest
|
|
28
|
+
* active membership.
|
|
29
|
+
*
|
|
30
|
+
* API-key and OAuth-bearer resolution live in the auth-context middleware
|
|
31
|
+
* (they are credential formats, not identity sources) and share the SAME
|
|
32
|
+
* selection rule; this provider is the seam the central account service
|
|
33
|
+
* replaces later.
|
|
34
|
+
*/
|
|
35
|
+
export class LocalIdentityProvider implements IdentityProvider {
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly auth: Auth,
|
|
38
|
+
private readonly db: Db,
|
|
39
|
+
private readonly hasGoogle: boolean,
|
|
40
|
+
private readonly hasOtp: boolean = false,
|
|
41
|
+
private readonly hasPasswordReset: boolean = false,
|
|
42
|
+
private readonly hasEmailChange: boolean = false,
|
|
43
|
+
/** Cloud only: one cached reconcile on a well-formed selector miss. */
|
|
44
|
+
private readonly onWorkspaceMiss?: OnWorkspaceMiss | undefined
|
|
45
|
+
) {}
|
|
46
|
+
|
|
47
|
+
async resolve(ctx: RequestContext): Promise<Principal | null> {
|
|
48
|
+
const session = await this.auth.api.getSession({
|
|
49
|
+
headers: ctx.headers as Headers
|
|
50
|
+
});
|
|
51
|
+
if (!session?.user) return null;
|
|
52
|
+
|
|
53
|
+
const requested = ctx.headers.get(ACTIVE_WORKSPACE_HEADER)?.trim() || null;
|
|
54
|
+
let row = await resolveMembership(this.db, session.user.id, requested);
|
|
55
|
+
if (!row && requested && this.onWorkspaceMiss && isWorkspaceSelector(requested)) {
|
|
56
|
+
// Unknown-workspace deep-link retry (cloud): the named workspace may
|
|
57
|
+
// be a hub org granted since the last pass. One cached reconcile, one
|
|
58
|
+
// re-run of the SAME lookup — no recursion.
|
|
59
|
+
await this.onWorkspaceMiss(session.user.id, requested);
|
|
60
|
+
row = await resolveMembership(this.db, session.user.id, requested);
|
|
61
|
+
}
|
|
62
|
+
if (!row) return null;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
userId: session.user.id,
|
|
66
|
+
email: session.user.email,
|
|
67
|
+
name: session.user.name,
|
|
68
|
+
workspaceId: row.workspaceId,
|
|
69
|
+
role: row.role,
|
|
70
|
+
origin: row.origin,
|
|
71
|
+
via: 'session',
|
|
72
|
+
scopes: null,
|
|
73
|
+
...(row.accountRef ? { accountRef: row.accountRef } : {})
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe(): InstanceAuthDescriptor {
|
|
78
|
+
// 'oauth' is always on: every instance is an OAuth 2.1 AS for its /mcp (M6).
|
|
79
|
+
const methods: InstanceAuthDescriptor['methods'] = ['password', 'api-key', 'oauth'];
|
|
80
|
+
if (this.hasOtp) methods.push('email-otp');
|
|
81
|
+
if (this.hasGoogle) methods.push('google');
|
|
82
|
+
return {
|
|
83
|
+
methods,
|
|
84
|
+
passwordReset: this.hasPasswordReset,
|
|
85
|
+
emailChange: this.hasEmailChange,
|
|
86
|
+
// Opt-in TOTP needs no email driver — always available on the template.
|
|
87
|
+
twoFactor: true
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { EntitlementService, IdentityProvider, UsageSink } from '@antasphere/chassis-contract';
|
|
2
|
+
import type { EventBus } from './events.js';
|
|
3
|
+
import type { WorkspaceService } from './workspaces.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Boot-time module registry: the single place where seam implementations are
|
|
7
|
+
* bound. The template binds local defaults; a rail-connected or ee edition
|
|
8
|
+
* re-binds its own implementations here at boot (Cal.com/n8n pattern) and
|
|
9
|
+
* nothing else in the codebase changes.
|
|
10
|
+
*/
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
12
|
+
export interface PlatformRegistry<TExtra = {}> {
|
|
13
|
+
identity: IdentityProvider;
|
|
14
|
+
entitlements: EntitlementService;
|
|
15
|
+
usage: UsageSink;
|
|
16
|
+
events: EventBus<TExtra>;
|
|
17
|
+
/**
|
|
18
|
+
* Workspace lifecycle (ADR 014). Setup creates the FIRST workspace through
|
|
19
|
+
* it; product flows that open workspace creation (the cloud edition's lazy
|
|
20
|
+
* org projection) call it here instead of inserting rows themselves.
|
|
21
|
+
*/
|
|
22
|
+
workspaces: WorkspaceService;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
26
|
+
export function createRegistry<TExtra = {}>(defaults: PlatformRegistry<TExtra>): PlatformRegistry<TExtra> {
|
|
27
|
+
return { ...defaults };
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { UsageEvent, UsageSink } from '@antasphere/chassis-contract';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default UsageSink: drop everything. Domain code always emits usage events
|
|
5
|
+
* (they double as local analytics hooks); only a rail-connected sink turns
|
|
6
|
+
* them into billing. Zero phone-home is the default posture — nothing leaves
|
|
7
|
+
* the instance unless an operator explicitly configures a sink.
|
|
8
|
+
*/
|
|
9
|
+
export class NoopUsageSink implements UsageSink {
|
|
10
|
+
async emit(_event: UsageEvent): Promise<void> {
|
|
11
|
+
// intentionally empty
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { workspaceMembers, workspaces, type DbConn } from '@antasphere/chassis-db';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Workspace lifecycle — the template's one creation path (ADR 014). Setup
|
|
5
|
+
* creates the FIRST workspace through it; products that open their own
|
|
6
|
+
* workspace-creation flow (a hub's sign-up, a cloud edition's lazy org
|
|
7
|
+
* projection) call it via the platform registry instead of hand-rolling
|
|
8
|
+
* the two inserts. The template exposed no HTTP endpoint for this (workspace
|
|
9
|
+
* creation is a product decision); the chassis took that decision with
|
|
10
|
+
* `POST /workspaces` (api/workspaces.ts, PRDCT-2444): sessions only, capped
|
|
11
|
+
* per person, and still through THIS service — never a second insert path.
|
|
12
|
+
*/
|
|
13
|
+
export class WorkspaceService {
|
|
14
|
+
constructor(private readonly db: DbConn) {}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a workspace with `ownerUserId` as its ACTIVE OWNER — one unit:
|
|
18
|
+
* pass a transaction handle (`conn`) to join an enclosing transaction
|
|
19
|
+
* (setup does), or omit it to run the two inserts on this service's
|
|
20
|
+
* connection inside their own transaction when it supports one.
|
|
21
|
+
*/
|
|
22
|
+
async create(name: string, ownerUserId: string, conn: DbConn = this.db): Promise<{ workspaceId: string }> {
|
|
23
|
+
const run = async (tx: DbConn) => {
|
|
24
|
+
const [workspace] = await tx.insert(workspaces).values({ name }).returning({ id: workspaces.id });
|
|
25
|
+
if (!workspace) throw new Error('workspace insert returned no row');
|
|
26
|
+
await tx.insert(workspaceMembers).values({
|
|
27
|
+
workspaceId: workspace.id,
|
|
28
|
+
userId: ownerUserId,
|
|
29
|
+
role: 'owner'
|
|
30
|
+
});
|
|
31
|
+
return { workspaceId: workspace.id };
|
|
32
|
+
};
|
|
33
|
+
// A DbConn may already BE a transaction (no .transaction on the type);
|
|
34
|
+
// when the caller hands us a plain Db, wrap the pair so a workspace can
|
|
35
|
+
// never exist without its owner membership.
|
|
36
|
+
if ('transaction' in conn && typeof conn.transaction === 'function') {
|
|
37
|
+
return conn.transaction(async (tx) => run(tx));
|
|
38
|
+
}
|
|
39
|
+
return run(conn);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { sql, type SQL, type SQLWrapper } from 'drizzle-orm';
|
|
2
|
+
import type { Principal, ProjectRole } from '@antasphere/chassis-contract';
|
|
3
|
+
import { projects, type DbConn } from '@antasphere/chassis-db';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* THE project access rule, stated once. A tool links its own resources to
|
|
7
|
+
* projects through a table of its own, and asks ONE question of the chassis:
|
|
8
|
+
* "does this principal hold a grant of at least `<role>` on the project in
|
|
9
|
+
* this column?". The chassis routes (`api/projects.ts`) ask the same question
|
|
10
|
+
* through the same builder, so a tool cannot drift from them.
|
|
11
|
+
*
|
|
12
|
+
* What the predicate carries, and every clause is load-bearing:
|
|
13
|
+
*
|
|
14
|
+
* - **The guest refusal.** An `origin='guest'` membership exists for
|
|
15
|
+
* principal resolution only (D2): it is never a project member. Refused on
|
|
16
|
+
* the PRINCIPAL (`false`, no query at all) AND on the membership row the
|
|
17
|
+
* grant rides on (`origin <> 'guest'`), so a stray row is inert.
|
|
18
|
+
* - **The live membership.** The grant is read THROUGH its
|
|
19
|
+
* `workspace_members` row: same user, same workspace as the request,
|
|
20
|
+
* `is_active`. A deleted membership took the grant with it (the foreign
|
|
21
|
+
* key's cascade); a deactivated one resolves no principal at all.
|
|
22
|
+
* - **One workspace.** The project belongs to the request's workspace and to
|
|
23
|
+
* the membership's. A request never spans workspaces (ADR 014).
|
|
24
|
+
* - **The role ladder.** `manager` contains `editor` contains `viewer`.
|
|
25
|
+
* - **A team's grant (PRDCT-2794).** A person seated in a team that holds a
|
|
26
|
+
* place on the project holds the team's role, on the same live membership
|
|
27
|
+
* row; their effective role is the highest of their own grant and their
|
|
28
|
+
* teams'. A seat or a grant removed is felt on the next request.
|
|
29
|
+
* - **The operator view.** A workspace owner or admin acts as a manager on
|
|
30
|
+
* every project of the workspace (ADR 006), which is why a project with no
|
|
31
|
+
* manager stays manageable and there is no last-manager guard.
|
|
32
|
+
* - **The archived-write rule.** `access: 'write'` is off while the project
|
|
33
|
+
* is archived, for everyone, the operator included. A READ stays.
|
|
34
|
+
*
|
|
35
|
+
* Callers answer 404, never 403, when a READ predicate is false: a project a
|
|
36
|
+
* principal cannot read must not reveal its existence.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
const LADDER: Record<ProjectRole, readonly ProjectRole[]> = {
|
|
40
|
+
viewer: ['viewer', 'editor', 'manager'],
|
|
41
|
+
editor: ['editor', 'manager'],
|
|
42
|
+
manager: ['manager']
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export interface ProjectGrantOptions {
|
|
46
|
+
/** The least role that satisfies the predicate. */
|
|
47
|
+
atLeast: ProjectRole;
|
|
48
|
+
/** `write` adds the archived-write rule; `read` does not look at the archive. */
|
|
49
|
+
access: 'read' | 'write';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A boolean SQL expression over `projectId`, a column (or expression) of the
|
|
54
|
+
* CALLER's query that holds a project id: the tool's link table column, or
|
|
55
|
+
* `projects.id` itself. The aliases are prefixed so they cannot collide with
|
|
56
|
+
* the ones of the query the predicate is embedded in.
|
|
57
|
+
*
|
|
58
|
+
* **Pass a QUALIFIED reference when the predicate lands in a SELECT LIST.**
|
|
59
|
+
* drizzle renders a column object of a single-table select WITHOUT its table
|
|
60
|
+
* name there (`"id"`), and inside this subquery a bare `"id"` binds to the
|
|
61
|
+
* subquery's own tables: ambiguous at best, always-true at worst. A raw
|
|
62
|
+
* `sql\`"link_table"."project_id"\`` (or `PROJECTS_ID` below) is immune; a
|
|
63
|
+
* column object is safe in a WHERE, where drizzle always qualifies it.
|
|
64
|
+
*/
|
|
65
|
+
export function projectGrantPredicate(
|
|
66
|
+
principal: Pick<Principal, 'userId' | 'workspaceId' | 'role' | 'origin'>,
|
|
67
|
+
projectId: SQLWrapper,
|
|
68
|
+
opts: ProjectGrantOptions
|
|
69
|
+
): SQL {
|
|
70
|
+
if (principal.origin === 'guest') return sql`false`;
|
|
71
|
+
const live = opts.access === 'write' ? sql`AND prj_pr.archived_at IS NULL` : sql``;
|
|
72
|
+
if (principal.role === 'owner' || principal.role === 'admin') {
|
|
73
|
+
return sql`EXISTS (
|
|
74
|
+
SELECT 1 FROM projects prj_pr
|
|
75
|
+
WHERE prj_pr.id = ${projectId}
|
|
76
|
+
AND prj_pr.workspace_id = ${principal.workspaceId}
|
|
77
|
+
${live}
|
|
78
|
+
)`;
|
|
79
|
+
}
|
|
80
|
+
const roles = sql.join(
|
|
81
|
+
LADDER[opts.atLeast].map((role) => sql`${role}`),
|
|
82
|
+
sql`, `
|
|
83
|
+
);
|
|
84
|
+
// Two ways to hold a grant, judged on the SAME live membership row: the
|
|
85
|
+
// person's own entry (`project_members`), or the entry of a team the person
|
|
86
|
+
// is seated in (`project_teams` through `workspace_team_members`, PRDCT-2794).
|
|
87
|
+
// The team's workspace is checked against the membership's like the
|
|
88
|
+
// project's, so a seat can never reach across workspaces; the effective
|
|
89
|
+
// role is the highest of the two, which the ladder's IN list yields.
|
|
90
|
+
return sql`EXISTS (
|
|
91
|
+
SELECT 1
|
|
92
|
+
FROM workspace_members prj_wm
|
|
93
|
+
JOIN projects prj_pr ON prj_pr.workspace_id = prj_wm.workspace_id
|
|
94
|
+
WHERE prj_pr.id = ${projectId}
|
|
95
|
+
AND prj_wm.user_id = ${principal.userId}
|
|
96
|
+
AND prj_wm.workspace_id = ${principal.workspaceId}
|
|
97
|
+
AND prj_wm.is_active
|
|
98
|
+
AND prj_wm.origin <> 'guest'
|
|
99
|
+
${live}
|
|
100
|
+
AND (
|
|
101
|
+
EXISTS (
|
|
102
|
+
SELECT 1 FROM project_members prj_pm
|
|
103
|
+
WHERE prj_pm.project_id = prj_pr.id
|
|
104
|
+
AND prj_pm.member_id = prj_wm.id
|
|
105
|
+
AND prj_pm.role IN (${roles})
|
|
106
|
+
)
|
|
107
|
+
OR EXISTS (
|
|
108
|
+
SELECT 1
|
|
109
|
+
FROM project_teams prj_pt
|
|
110
|
+
JOIN workspace_team_members prj_tm ON prj_tm.team_id = prj_pt.team_id
|
|
111
|
+
JOIN workspace_teams prj_t ON prj_t.id = prj_pt.team_id
|
|
112
|
+
WHERE prj_pt.project_id = prj_pr.id
|
|
113
|
+
AND prj_tm.member_id = prj_wm.id
|
|
114
|
+
AND prj_t.workspace_id = prj_wm.workspace_id
|
|
115
|
+
AND prj_pt.role IN (${roles})
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
)`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** `projects.id`, always qualified: safe in a select list as in a WHERE (see above). */
|
|
122
|
+
export const PROJECTS_ID: SQL = sql.raw('"projects"."id"');
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The caller's effective role on a project AS A SQL EXPRESSION over a project
|
|
126
|
+
* id column: `manager`, `editor`, `viewer`, or NULL (no read). Built from the
|
|
127
|
+
* predicate above, never beside it. Archive-blind on purpose: a role is a
|
|
128
|
+
* READ fact, and whether an act is allowed on an archived project is the
|
|
129
|
+
* act's own question (`access: 'write'`, or the route's 409).
|
|
130
|
+
*/
|
|
131
|
+
export function projectRoleExpression(
|
|
132
|
+
principal: Pick<Principal, 'userId' | 'workspaceId' | 'role' | 'origin'>,
|
|
133
|
+
projectId: SQLWrapper
|
|
134
|
+
): SQL<ProjectRole | null> {
|
|
135
|
+
const has = (atLeast: ProjectRole) =>
|
|
136
|
+
projectGrantPredicate(principal, projectId, { atLeast, access: 'read' });
|
|
137
|
+
return sql<ProjectRole | null>`CASE
|
|
138
|
+
WHEN ${has('manager')} THEN 'manager'
|
|
139
|
+
WHEN ${has('editor')} THEN 'editor'
|
|
140
|
+
WHEN ${has('viewer')} THEN 'viewer'
|
|
141
|
+
END`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The effective role of `principal` on one project of the request's
|
|
146
|
+
* workspace, or null. **null = 404**: no such project here, or a project the
|
|
147
|
+
* principal cannot read; the two must stay indistinguishable to the caller.
|
|
148
|
+
*/
|
|
149
|
+
export async function projectRole(
|
|
150
|
+
conn: DbConn,
|
|
151
|
+
principal: Pick<Principal, 'userId' | 'workspaceId' | 'role' | 'origin'>,
|
|
152
|
+
projectId: string
|
|
153
|
+
): Promise<ProjectRole | null> {
|
|
154
|
+
const rows = await conn
|
|
155
|
+
.select({ role: projectRoleExpression(principal, PROJECTS_ID) })
|
|
156
|
+
.from(projects)
|
|
157
|
+
.where(sql`${projects.id} = ${projectId} AND ${projects.workspaceId} = ${principal.workspaceId}`)
|
|
158
|
+
.limit(1);
|
|
159
|
+
return rows[0]?.role ?? null;
|
|
160
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The low-cardinality, secret-free label for a request, used by BOTH the
|
|
5
|
+
* completion log line and the trace span.
|
|
6
|
+
*
|
|
7
|
+
* The raw `c.req.path` must never reach the log stream or a span attribute.
|
|
8
|
+
* Path segments are request data like any other: the moment a route puts a
|
|
9
|
+
* capability in the URL — a share secret, a one-time token, an invitation
|
|
10
|
+
* code — every log sink, every trace backend, and every operator with
|
|
11
|
+
* `docker logs` holds a working credential, and the redaction list cannot
|
|
12
|
+
* help (it keys off object properties, and `path` is one opaque string).
|
|
13
|
+
* Logging the MATCHED ROUTE PATTERN instead (`/v/:secret`, not
|
|
14
|
+
* `/v/9f3c…`) keeps the line just as useful for debugging and cardinality
|
|
15
|
+
* bounded, and it is secret-free by construction for every route that will
|
|
16
|
+
* ever be added.
|
|
17
|
+
*
|
|
18
|
+
* `routePath` is only populated once routing has run, so callers must read
|
|
19
|
+
* this AFTER `await next()`. Unmatched requests have no pattern to report:
|
|
20
|
+
* they get the literal `(unmatched)` rather than the attacker-controlled URL
|
|
21
|
+
* they probed with.
|
|
22
|
+
*/
|
|
23
|
+
export function routeLabel(c: Context): string {
|
|
24
|
+
const matched = c.req.routePath;
|
|
25
|
+
// Hono answers '/*' for a catch-all match and for no match at all; neither
|
|
26
|
+
// says anything the pattern doesn't, and both are safe to report as-is.
|
|
27
|
+
if (!matched) return '(unmatched)';
|
|
28
|
+
return matched;
|
|
29
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { RuntimeState } from '../state.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* /healthz — liveness. No dependencies: answers 200 whenever the event loop
|
|
6
|
+
* runs, so orchestrators never restart a replica that is merely waiting on
|
|
7
|
+
* the database.
|
|
8
|
+
*
|
|
9
|
+
* /readyz — readiness. 503 until env is parsed, migrations are applied (or
|
|
10
|
+
* confirmed current), and the DB is reachable; 503 again while draining; and
|
|
11
|
+
* 503 whenever the backing store stops being writable.
|
|
12
|
+
*
|
|
13
|
+
* That last clause is the whole point of `probeStorage`. Readiness used to be
|
|
14
|
+
* a boot-time boolean, so the storage probe ran exactly once, at startup: an
|
|
15
|
+
* instance whose /data volume was emptied, unmounted or turned read-only
|
|
16
|
+
* after boot — the state a failed restore leaves behind — kept answering 200
|
|
17
|
+
* and kept taking traffic it could not serve. Readiness has to be a live
|
|
18
|
+
* claim about the dependencies, not a memory of them.
|
|
19
|
+
*/
|
|
20
|
+
export interface HealthDeps {
|
|
21
|
+
/** Re-probe the backing store. Must throw when it is not writable. */
|
|
22
|
+
probeStorage?: () => Promise<void>;
|
|
23
|
+
/** How long a SUCCESSFUL probe is trusted before it re-runs. */
|
|
24
|
+
storageProbeTtlMs?: number;
|
|
25
|
+
/** Called with the probe failure so it lands in the logs. */
|
|
26
|
+
onStorageFailure?: (err: unknown) => void;
|
|
27
|
+
/** Injectable clock (tests). */
|
|
28
|
+
now?: () => number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Successes are cached briefly so a fast readiness poll does not become a
|
|
33
|
+
* PUT+DELETE per second against S3. Failures are NEVER cached: the next
|
|
34
|
+
* request re-probes, so recovery is observed immediately.
|
|
35
|
+
*/
|
|
36
|
+
const DEFAULT_STORAGE_PROBE_TTL_MS = 10_000;
|
|
37
|
+
|
|
38
|
+
export function healthRoutes(state: RuntimeState, deps: HealthDeps = {}): Hono {
|
|
39
|
+
const app = new Hono();
|
|
40
|
+
const { probeStorage, onStorageFailure } = deps;
|
|
41
|
+
const ttlMs = deps.storageProbeTtlMs ?? DEFAULT_STORAGE_PROBE_TTL_MS;
|
|
42
|
+
const now = deps.now ?? (() => Date.now());
|
|
43
|
+
|
|
44
|
+
let trustedUntil = 0;
|
|
45
|
+
let inflight: Promise<unknown> | null = null;
|
|
46
|
+
|
|
47
|
+
/** Resolves to the failure, or null when storage is reachable. */
|
|
48
|
+
function checkStorage(): Promise<unknown> {
|
|
49
|
+
if (!probeStorage) return Promise.resolve(null);
|
|
50
|
+
if (now() < trustedUntil) return Promise.resolve(null);
|
|
51
|
+
// One probe at a time: a burst of readiness polls must not fan out into a
|
|
52
|
+
// burst of writes against the store.
|
|
53
|
+
const pending = (inflight ??= (async () => {
|
|
54
|
+
try {
|
|
55
|
+
await probeStorage();
|
|
56
|
+
trustedUntil = now() + ttlMs;
|
|
57
|
+
return null;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
trustedUntil = 0;
|
|
60
|
+
onStorageFailure?.(err);
|
|
61
|
+
return err ?? new Error('storage probe failed');
|
|
62
|
+
} finally {
|
|
63
|
+
inflight = null;
|
|
64
|
+
}
|
|
65
|
+
})());
|
|
66
|
+
return pending;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
app.get('/healthz', (c) => c.json({ status: 'ok' }));
|
|
70
|
+
app.get('/readyz', async (c) => {
|
|
71
|
+
if (state.draining) return c.json({ status: 'unavailable', reason: 'draining' }, 503);
|
|
72
|
+
if (!state.ready) return c.json({ status: 'unavailable', reason: state.reason }, 503);
|
|
73
|
+
// The reason stays coarse on purpose: /readyz is unauthenticated and a
|
|
74
|
+
// driver's error text carries bucket names and filesystem paths.
|
|
75
|
+
if (await checkStorage()) return c.json({ status: 'unavailable', reason: 'storage' }, 503);
|
|
76
|
+
return c.json({ status: 'ready' });
|
|
77
|
+
});
|
|
78
|
+
return app;
|
|
79
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Hono, type Context } from 'hono';
|
|
2
|
+
import {
|
|
3
|
+
oauthProviderAuthServerMetadata,
|
|
4
|
+
oauthProviderOpenIdConfigMetadata
|
|
5
|
+
} from '@better-auth/oauth-provider';
|
|
6
|
+
import { mcpResourceUrl, type Auth } from '../identity/better-auth.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Root-level OAuth discovery surface.
|
|
10
|
+
*
|
|
11
|
+
* The oauthProvider plugin serves RFC 8414 / OIDC discovery under the
|
|
12
|
+
* /api/v1/auth base path, but clients resolve discovery from the ISSUER (the
|
|
13
|
+
* bare origin — jwt.issuer in identity/better-auth.ts), so the same documents
|
|
14
|
+
* must exist at the origin root. RFC 9728 protected-resource metadata is ours:
|
|
15
|
+
* it points the 401-challenged MCP client at this same origin as its
|
|
16
|
+
* authorization server. All documents are public by design and fetched
|
|
17
|
+
* cross-origin by browser-based MCP hosts — wide-open CORS is correct.
|
|
18
|
+
*/
|
|
19
|
+
const CORS_HEADERS = {
|
|
20
|
+
'Access-Control-Allow-Origin': '*',
|
|
21
|
+
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
|
22
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, mcp-protocol-version',
|
|
23
|
+
'Access-Control-Max-Age': '86400'
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
export interface WellKnownDeps {
|
|
27
|
+
auth: Auth;
|
|
28
|
+
publicBaseUrl: string;
|
|
29
|
+
instanceName?: string;
|
|
30
|
+
/** The tool's OAuth scope list, as registered with the provider: what `scopes_supported` advertises. */
|
|
31
|
+
oauthScopes: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function wellKnownRoutes({ auth, publicBaseUrl, oauthScopes }: WellKnownDeps): Hono {
|
|
35
|
+
const app = new Hono();
|
|
36
|
+
|
|
37
|
+
const asMetadata = oauthProviderAuthServerMetadata(auth, {
|
|
38
|
+
headers: { ...CORS_HEADERS, 'Cache-Control': 'public, max-age=3600' }
|
|
39
|
+
});
|
|
40
|
+
const oidcMetadata = oauthProviderOpenIdConfigMetadata(auth, {
|
|
41
|
+
headers: { ...CORS_HEADERS, 'Cache-Control': 'public, max-age=3600' }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
app.get('/.well-known/oauth-authorization-server', (c) => asMetadata(c.req.raw));
|
|
45
|
+
app.get('/.well-known/openid-configuration', (c) => oidcMetadata(c.req.raw));
|
|
46
|
+
|
|
47
|
+
// RFC 9728: served at the origin level AND at the path-aware derivation
|
|
48
|
+
// (clients insert /.well-known/oauth-protected-resource between origin and
|
|
49
|
+
// the resource path /mcp) — both return the same document.
|
|
50
|
+
const protectedResource = (c: Context) =>
|
|
51
|
+
c.json(
|
|
52
|
+
{
|
|
53
|
+
resource: mcpResourceUrl(publicBaseUrl),
|
|
54
|
+
authorization_servers: [publicBaseUrl],
|
|
55
|
+
scopes_supported: [...oauthScopes],
|
|
56
|
+
bearer_methods_supported: ['header']
|
|
57
|
+
},
|
|
58
|
+
200,
|
|
59
|
+
{ ...CORS_HEADERS, 'Cache-Control': 'public, max-age=3600' }
|
|
60
|
+
);
|
|
61
|
+
app.get('/.well-known/oauth-protected-resource', (c) => protectedResource(c));
|
|
62
|
+
app.get('/.well-known/oauth-protected-resource/mcp', (c) => protectedResource(c));
|
|
63
|
+
|
|
64
|
+
app.options('/.well-known/*', (c) => c.body(null, 204, { ...CORS_HEADERS }));
|
|
65
|
+
|
|
66
|
+
return app;
|
|
67
|
+
}
|