@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,435 @@
|
|
|
1
|
+
import { Counter, Gauge } from 'prom-client';
|
|
2
|
+
import {
|
|
3
|
+
USAGE_ROUTE_ERRORS,
|
|
4
|
+
usageCheckSchema,
|
|
5
|
+
type UsageCheck,
|
|
6
|
+
type UsageRouteError
|
|
7
|
+
} from '@antasphere/chassis-contract';
|
|
8
|
+
import type { Logger } from '../logger.js';
|
|
9
|
+
import type { HubMachineToken } from './hub-machine-token.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The cloud edition's credit check (the billing rail spec, §7 steps 3 and 4;
|
|
13
|
+
* PRDCT-2664): before a metered action runs, the hub's `POST
|
|
14
|
+
* /api/v1/usage/check` prices THIS quantity against the organization's
|
|
15
|
+
* balance, with the machine token. The answer is advisory at the hub (no
|
|
16
|
+
* side effect): the debit happens at ingest, from the usage event the gate
|
|
17
|
+
* emits after a 2xx.
|
|
18
|
+
*
|
|
19
|
+
* The cache, per account per action. The price is non-decreasing in the
|
|
20
|
+
* quantity, which is what makes an answer reusable for a DIFFERENT quantity:
|
|
21
|
+
* a fresh allowed answer serves any request asking for no more than it was
|
|
22
|
+
* asked for (kept `allowTtlMs`), a fresh denial refuses any request asking
|
|
23
|
+
* for at least as much (kept `denyTtlMs`, shorter: a top-up must be felt
|
|
24
|
+
* soon); every other case asks the hub. The cache is consulted BEFORE the
|
|
25
|
+
* hub always, so a fresh answer serves through an outage too.
|
|
26
|
+
*
|
|
27
|
+
* What the hub's answer means:
|
|
28
|
+
* - 200 that parses: the verdict (a success clears the failure clock).
|
|
29
|
+
* `allowed: false` carries one of three reasons, each a REFUSAL:
|
|
30
|
+
* `insufficient_credits`, `account_suspended`, and `unpriceable` (the
|
|
31
|
+
* price of this quantity exceeds `Number.MAX_SAFE_INTEGER`, `credits`
|
|
32
|
+
* clamped to that bound), never an outage (the drift of 23 September
|
|
33
|
+
* 2026, PRDCT-2677: the copy knew two reasons, read the third as an
|
|
34
|
+
* unparseable answer, and failed open);
|
|
35
|
+
* - 401: the token is dead, invalidated and minted again ONCE, then an outage;
|
|
36
|
+
* - 404 `unknown_account`: no organization holds this id at the hub, so
|
|
37
|
+
* nothing can be charged: allowed, logged at warn, not an outage;
|
|
38
|
+
* - 400: our request is malformed, a bug no waiting heals: allowed, logged
|
|
39
|
+
* at ERROR, not an outage;
|
|
40
|
+
* - 403: the client lacks the scope, an outage logged at error (the
|
|
41
|
+
* operator must see it);
|
|
42
|
+
* - anything else (another 404, 408, 429, 5xx, a network error, a timeout,
|
|
43
|
+
* a 200 that is not the hub's answer, no machine token): an OUTAGE.
|
|
44
|
+
*
|
|
45
|
+
* An outage fails OPEN for `failOpenMs` from its first failure (the
|
|
46
|
+
* federation gate's own posture: fifteen minutes), then CLOSED
|
|
47
|
+
* (`hub_unavailable`) until the hub answers again. Every verdict is counted
|
|
48
|
+
* on /metrics and sets the posture gauge.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
export interface CreditCheckDials {
|
|
52
|
+
/** How long an allowed answer is reused (for a quantity ≤ the one asked). */
|
|
53
|
+
allowTtlMs: number;
|
|
54
|
+
/** How long a denial is reused (for a quantity ≥ the one asked). */
|
|
55
|
+
denyTtlMs: number;
|
|
56
|
+
/** How long an outage fails open, from its first failure, before it closes. */
|
|
57
|
+
failOpenMs: number;
|
|
58
|
+
/** Hub request budget per call. */
|
|
59
|
+
timeoutMs: number;
|
|
60
|
+
/**
|
|
61
|
+
* How long the hub is left alone after a failed call: every request in
|
|
62
|
+
* that window takes the outage's verdict at once (open, then closed)
|
|
63
|
+
* instead of paying the hub budget again. Without it, a hub that times out
|
|
64
|
+
* would cost every metered request five seconds for fifteen minutes; with
|
|
65
|
+
* it, one probe per window for the whole instance, the machine token's
|
|
66
|
+
* own posture.
|
|
67
|
+
*/
|
|
68
|
+
outageHoldMs: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const DEFAULT_CREDIT_CHECK_DIALS: CreditCheckDials = {
|
|
72
|
+
allowTtlMs: 30_000,
|
|
73
|
+
denyTtlMs: 5_000,
|
|
74
|
+
failOpenMs: 15 * 60_000,
|
|
75
|
+
timeoutMs: 5_000,
|
|
76
|
+
outageHoldMs: 5_000
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export interface CreditCheckRequest {
|
|
80
|
+
/** The paying account: the hub organization id. */
|
|
81
|
+
accountRef: string;
|
|
82
|
+
actionKey: string;
|
|
83
|
+
/** The quantity of THIS request, in the meter's unit. */
|
|
84
|
+
quantity: number;
|
|
85
|
+
/** The meter's unit, compared against the price row's (a mismatch is logged once per action). */
|
|
86
|
+
unit: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type CreditVerdict =
|
|
90
|
+
| {
|
|
91
|
+
allowed: true;
|
|
92
|
+
source: 'hub' | 'cache' | 'fail_open' | 'unknown_account' | 'malformed';
|
|
93
|
+
check: UsageCheck | null;
|
|
94
|
+
}
|
|
95
|
+
| {
|
|
96
|
+
allowed: false;
|
|
97
|
+
reason: 'insufficient_credits' | 'account_suspended' | 'unpriceable' | 'hub_unavailable';
|
|
98
|
+
source: 'hub' | 'cache' | 'closed';
|
|
99
|
+
check: UsageCheck | null;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** What the entitlement gate depends on: a unit test stubs it. */
|
|
103
|
+
export interface CreditCheck {
|
|
104
|
+
check(req: CreditCheckRequest): Promise<CreditVerdict>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface HubCreditCheckOptions {
|
|
108
|
+
issuerUrl: string;
|
|
109
|
+
token: HubMachineToken;
|
|
110
|
+
logger: Logger;
|
|
111
|
+
dials?: Partial<CreditCheckDials> | undefined;
|
|
112
|
+
fetchImpl?: typeof fetch | undefined;
|
|
113
|
+
now?: (() => number) | undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface CacheEntry {
|
|
117
|
+
answer: UsageCheck;
|
|
118
|
+
/** The quantity the hub was asked for (the larger one, when an allowed answer replaced another). */
|
|
119
|
+
quantity: number;
|
|
120
|
+
until: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type HubOutcome =
|
|
124
|
+
| { kind: 'answer'; answer: UsageCheck }
|
|
125
|
+
| { kind: 'unknown_account' }
|
|
126
|
+
| { kind: 'malformed' }
|
|
127
|
+
| { kind: 'outage' };
|
|
128
|
+
|
|
129
|
+
type Outcome =
|
|
130
|
+
| 'allowed'
|
|
131
|
+
| 'denied'
|
|
132
|
+
| 'suspended'
|
|
133
|
+
| 'unpriceable'
|
|
134
|
+
| 'cached_allowed'
|
|
135
|
+
| 'cached_denied'
|
|
136
|
+
| 'fail_open'
|
|
137
|
+
| 'closed'
|
|
138
|
+
| 'unknown_account'
|
|
139
|
+
| 'malformed';
|
|
140
|
+
|
|
141
|
+
/** How many entries the cache may hold before an insert sweeps the expired ones. */
|
|
142
|
+
const SWEEP_ABOVE = 5_000;
|
|
143
|
+
|
|
144
|
+
export class HubCreditCheck implements CreditCheck {
|
|
145
|
+
private readonly url: string;
|
|
146
|
+
private readonly fetchImpl: typeof fetch;
|
|
147
|
+
private readonly now: () => number;
|
|
148
|
+
private readonly dials: CreditCheckDials;
|
|
149
|
+
private readonly cache = new Map<string, CacheEntry>();
|
|
150
|
+
/** The first failure of the current outage, or null when the hub answers. */
|
|
151
|
+
private failingSinceMs: number | null = null;
|
|
152
|
+
/** When the hub last failed to answer: the hold window starts here. */
|
|
153
|
+
private lastFailureMs = 0;
|
|
154
|
+
/** Whether the flip to closed was logged for the current outage. */
|
|
155
|
+
private closedLogged = false;
|
|
156
|
+
/** The action keys whose unit mismatch was already logged. */
|
|
157
|
+
private readonly unitWarned = new Set<string>();
|
|
158
|
+
/** The action keys whose malformed-request answer was already logged at error (one line per key, not per request). */
|
|
159
|
+
private readonly malformedLogged = new Set<string>();
|
|
160
|
+
/** Whether the hub's 403 was logged at error for the current outage (the hold probes every few seconds). */
|
|
161
|
+
private forbiddenLogged = false;
|
|
162
|
+
/** How many calls reached the hub — a test seam. */
|
|
163
|
+
hubCalls = 0;
|
|
164
|
+
|
|
165
|
+
readonly verdicts: Counter<'outcome'>;
|
|
166
|
+
readonly posture: Gauge;
|
|
167
|
+
readonly failingSince: Gauge;
|
|
168
|
+
readonly cacheEntries: Gauge;
|
|
169
|
+
/** The metrics boot registers on the app's /metrics registry (cloud only). */
|
|
170
|
+
readonly promMetrics: Array<Counter<string> | Gauge<string>>;
|
|
171
|
+
|
|
172
|
+
constructor(private readonly opts: HubCreditCheckOptions) {
|
|
173
|
+
this.url = opts.issuerUrl.replace(/\/+$/, '') + '/api/v1/usage/check';
|
|
174
|
+
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
175
|
+
this.now = opts.now ?? Date.now;
|
|
176
|
+
this.dials = { ...DEFAULT_CREDIT_CHECK_DIALS, ...opts.dials };
|
|
177
|
+
// registers: [] — boot attaches these to the app registry; a test builds
|
|
178
|
+
// a check without one.
|
|
179
|
+
this.verdicts = new Counter({
|
|
180
|
+
name: 'usage_check_total',
|
|
181
|
+
help: 'Credit check verdicts by outcome: allowed, denied, suspended, unpriceable, cached_allowed, cached_denied, fail_open, closed, unknown_account, malformed',
|
|
182
|
+
labelNames: ['outcome'] as const,
|
|
183
|
+
registers: []
|
|
184
|
+
});
|
|
185
|
+
this.posture = new Gauge({
|
|
186
|
+
name: 'usage_check_posture',
|
|
187
|
+
help: 'The credit check posture: 0 healthy, 1 failing open (the hub is unreachable), 2 closed (hub_unavailable)',
|
|
188
|
+
registers: []
|
|
189
|
+
});
|
|
190
|
+
this.failingSince = new Gauge({
|
|
191
|
+
name: 'usage_check_failing_since_seconds',
|
|
192
|
+
help: 'Unix seconds of the first failure of the current hub outage, 0 when healthy',
|
|
193
|
+
registers: []
|
|
194
|
+
});
|
|
195
|
+
this.cacheEntries = new Gauge({
|
|
196
|
+
name: 'usage_check_cache_entries',
|
|
197
|
+
help: 'Credit check answers held in the per-account per-action cache',
|
|
198
|
+
registers: []
|
|
199
|
+
});
|
|
200
|
+
this.promMetrics = [this.verdicts, this.posture, this.failingSince, this.cacheEntries];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** How many answers the cache holds (the gauge's value). */
|
|
204
|
+
get cacheSize(): number {
|
|
205
|
+
return this.cache.size;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Forget every cached answer (a test seam). */
|
|
209
|
+
clear(): void {
|
|
210
|
+
this.cache.clear();
|
|
211
|
+
this.cacheEntries.set(0);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async check(req: CreditCheckRequest): Promise<CreditVerdict> {
|
|
215
|
+
const key = `${req.accountRef}\u0000${req.actionKey}`;
|
|
216
|
+
const cached = this.cache.get(key);
|
|
217
|
+
if (cached && cached.until > this.now()) {
|
|
218
|
+
if (cached.answer.allowed && req.quantity <= cached.quantity) {
|
|
219
|
+
return this.verdict('cached_allowed', { allowed: true, source: 'cache', check: cached.answer });
|
|
220
|
+
}
|
|
221
|
+
if (!cached.answer.allowed && req.quantity >= cached.quantity) {
|
|
222
|
+
return this.verdict('cached_denied', {
|
|
223
|
+
allowed: false,
|
|
224
|
+
reason: cached.answer.reason ?? 'insufficient_credits',
|
|
225
|
+
source: 'cache',
|
|
226
|
+
check: cached.answer
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Inside the hold after a failed call, the hub is left alone: the
|
|
232
|
+
// outage's verdict at once, no second budget paid per request.
|
|
233
|
+
if (this.failingSinceMs !== null && this.now() - this.lastFailureMs < this.dials.outageHoldMs) {
|
|
234
|
+
return this.outage();
|
|
235
|
+
}
|
|
236
|
+
const outcome = await this.ask(req, true);
|
|
237
|
+
switch (outcome.kind) {
|
|
238
|
+
case 'answer': {
|
|
239
|
+
this.answered();
|
|
240
|
+
const { answer } = outcome;
|
|
241
|
+
if (
|
|
242
|
+
typeof answer.unit === 'string' &&
|
|
243
|
+
answer.unit !== req.unit &&
|
|
244
|
+
!this.unitWarned.has(req.actionKey)
|
|
245
|
+
) {
|
|
246
|
+
this.unitWarned.add(req.actionKey);
|
|
247
|
+
this.opts.logger.warn(
|
|
248
|
+
{ actionKey: req.actionKey, meterUnit: req.unit, priceUnit: answer.unit },
|
|
249
|
+
'usage check: the hub prices this action in another unit than the route meters it — check the price book'
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
this.store(key, req.quantity, answer);
|
|
253
|
+
if (answer.allowed) return this.verdict('allowed', { allowed: true, source: 'hub', check: answer });
|
|
254
|
+
const reason = answer.reason ?? 'insufficient_credits';
|
|
255
|
+
return this.verdict(
|
|
256
|
+
reason === 'account_suspended' ? 'suspended' : reason === 'unpriceable' ? 'unpriceable' : 'denied',
|
|
257
|
+
{
|
|
258
|
+
allowed: false,
|
|
259
|
+
reason,
|
|
260
|
+
source: 'hub',
|
|
261
|
+
check: answer
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
// The hub ANSWERED, whatever it said: the outage, if one was running,
|
|
266
|
+
// is over (verifier round 1: a clock cleared only by a 200 let a hub
|
|
267
|
+
// answering 400 or unknown_account after one blip keep an outage
|
|
268
|
+
// "running", and the next blip closed the gate without its window).
|
|
269
|
+
case 'unknown_account':
|
|
270
|
+
this.answered();
|
|
271
|
+
return this.verdict('unknown_account', { allowed: true, source: 'unknown_account', check: null });
|
|
272
|
+
case 'malformed':
|
|
273
|
+
this.answered();
|
|
274
|
+
return this.verdict('malformed', { allowed: true, source: 'malformed', check: null });
|
|
275
|
+
case 'outage':
|
|
276
|
+
this.lastFailureMs = this.now();
|
|
277
|
+
return this.outage();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The hub answered: the outage, if any, is over, and the next one warns again. */
|
|
282
|
+
private answered(): void {
|
|
283
|
+
this.failingSinceMs = null;
|
|
284
|
+
this.closedLogged = false;
|
|
285
|
+
this.forbiddenLogged = false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Keep an answer; an allowed answer over a fresh allowed entry keeps the larger quantity. */
|
|
289
|
+
private store(key: string, quantity: number, answer: UsageCheck): void {
|
|
290
|
+
const now = this.now();
|
|
291
|
+
const previous = this.cache.get(key);
|
|
292
|
+
const kept =
|
|
293
|
+
answer.allowed && previous && previous.answer.allowed && previous.until > now
|
|
294
|
+
? Math.max(previous.quantity, quantity)
|
|
295
|
+
: quantity;
|
|
296
|
+
this.cache.set(key, {
|
|
297
|
+
answer,
|
|
298
|
+
quantity: kept,
|
|
299
|
+
until: now + (answer.allowed ? this.dials.allowTtlMs : this.dials.denyTtlMs)
|
|
300
|
+
});
|
|
301
|
+
if (this.cache.size > SWEEP_ABOVE) this.sweep(now);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Keep the cache bounded on a long-lived replica: drop every entry past its `until`. */
|
|
305
|
+
private sweep(now: number): void {
|
|
306
|
+
for (const [key, entry] of this.cache) {
|
|
307
|
+
if (entry.until <= now) this.cache.delete(key);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private outage(): CreditVerdict {
|
|
312
|
+
const now = this.now();
|
|
313
|
+
const first = this.failingSinceMs === null;
|
|
314
|
+
if (first) this.failingSinceMs = now;
|
|
315
|
+
if (now - this.failingSinceMs! <= this.dials.failOpenMs) {
|
|
316
|
+
if (first) {
|
|
317
|
+
this.opts.logger.warn(
|
|
318
|
+
'usage check: the hub does not answer — metered actions are allowed (failing open) for fifteen minutes'
|
|
319
|
+
);
|
|
320
|
+
} else {
|
|
321
|
+
this.opts.logger.debug('usage check: the hub still does not answer — failing open');
|
|
322
|
+
}
|
|
323
|
+
return this.verdict('fail_open', { allowed: true, source: 'fail_open', check: null });
|
|
324
|
+
}
|
|
325
|
+
if (!this.closedLogged) {
|
|
326
|
+
this.closedLogged = true;
|
|
327
|
+
this.opts.logger.error(
|
|
328
|
+
{ failingSince: new Date(this.failingSinceMs!).toISOString() },
|
|
329
|
+
'usage check: the hub has not answered past the fail-open window — metered actions are refused (hub_unavailable) until it recovers'
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
return this.verdict('closed', {
|
|
333
|
+
allowed: false,
|
|
334
|
+
reason: 'hub_unavailable',
|
|
335
|
+
source: 'closed',
|
|
336
|
+
check: null
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Count the verdict, set the gauges, return it. */
|
|
341
|
+
private verdict(outcome: Outcome, verdict: CreditVerdict): CreditVerdict {
|
|
342
|
+
this.verdicts.inc({ outcome });
|
|
343
|
+
const since = this.failingSinceMs;
|
|
344
|
+
this.posture.set(since === null ? 0 : this.now() - since <= this.dials.failOpenMs ? 1 : 2);
|
|
345
|
+
this.failingSince.set(since === null ? 0 : Math.floor(since / 1000));
|
|
346
|
+
this.cacheEntries.set(this.cache.size);
|
|
347
|
+
return verdict;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** One hub call (and one retry on a dead token); never throws. */
|
|
351
|
+
private async ask(req: CreditCheckRequest, retryOn401: boolean): Promise<HubOutcome> {
|
|
352
|
+
let token: string;
|
|
353
|
+
try {
|
|
354
|
+
token = await this.opts.token.get();
|
|
355
|
+
} catch (err) {
|
|
356
|
+
this.opts.logger.debug({ err }, 'usage check: no machine token');
|
|
357
|
+
return { kind: 'outage' };
|
|
358
|
+
}
|
|
359
|
+
this.hubCalls += 1;
|
|
360
|
+
let res: Response;
|
|
361
|
+
try {
|
|
362
|
+
res = await this.fetchImpl(this.url, {
|
|
363
|
+
method: 'POST',
|
|
364
|
+
headers: {
|
|
365
|
+
'content-type': 'application/json',
|
|
366
|
+
accept: 'application/json',
|
|
367
|
+
authorization: `Bearer ${token}`
|
|
368
|
+
},
|
|
369
|
+
body: JSON.stringify({
|
|
370
|
+
accountRef: req.accountRef,
|
|
371
|
+
actionKey: req.actionKey,
|
|
372
|
+
quantity: req.quantity,
|
|
373
|
+
unit: req.unit
|
|
374
|
+
}),
|
|
375
|
+
signal: AbortSignal.timeout(this.dials.timeoutMs)
|
|
376
|
+
});
|
|
377
|
+
} catch (err) {
|
|
378
|
+
this.opts.logger.debug({ err }, 'usage check: hub unreachable');
|
|
379
|
+
return { kind: 'outage' };
|
|
380
|
+
}
|
|
381
|
+
if (res.status === 401) {
|
|
382
|
+
if (!retryOn401) return { kind: 'outage' };
|
|
383
|
+
this.opts.token.invalidate();
|
|
384
|
+
return this.ask(req, false);
|
|
385
|
+
}
|
|
386
|
+
if (res.ok) {
|
|
387
|
+
const parsed = usageCheckSchema.safeParse(await res.json().catch(() => null));
|
|
388
|
+
if (!parsed.success) {
|
|
389
|
+
this.opts.logger.warn(
|
|
390
|
+
{ status: res.status },
|
|
391
|
+
'usage check: the hub answered 2xx without a readable check — treated as an outage'
|
|
392
|
+
);
|
|
393
|
+
return { kind: 'outage' };
|
|
394
|
+
}
|
|
395
|
+
return { kind: 'answer', answer: parsed.data };
|
|
396
|
+
}
|
|
397
|
+
if (res.status === USAGE_ROUTE_ERRORS.unknown_account) {
|
|
398
|
+
const body = (await res.json().catch(() => null)) as { error?: { code?: unknown } } | null;
|
|
399
|
+
if (body?.error?.code === ('unknown_account' satisfies UsageRouteError)) {
|
|
400
|
+
this.opts.logger.warn(
|
|
401
|
+
{ accountRef: req.accountRef, actionKey: req.actionKey },
|
|
402
|
+
'usage check: no organization holds this account at the hub — nothing can be charged, the action is allowed'
|
|
403
|
+
);
|
|
404
|
+
return { kind: 'unknown_account' };
|
|
405
|
+
}
|
|
406
|
+
return { kind: 'outage' };
|
|
407
|
+
}
|
|
408
|
+
if (res.status === USAGE_ROUTE_ERRORS.validation_error) {
|
|
409
|
+
const body = await res.text().catch(() => '');
|
|
410
|
+
// One error line per action key: a schema drift would otherwise write
|
|
411
|
+
// one per metered request (the code review).
|
|
412
|
+
if (!this.malformedLogged.has(req.actionKey)) {
|
|
413
|
+
this.malformedLogged.add(req.actionKey);
|
|
414
|
+
this.opts.logger.error(
|
|
415
|
+
{ accountRef: req.accountRef, actionKey: req.actionKey, body: body.slice(0, 500) },
|
|
416
|
+
'usage check: the hub refused the check as malformed — a bug, the action is allowed (counted on usage_check_total{outcome="malformed"})'
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return { kind: 'malformed' };
|
|
420
|
+
}
|
|
421
|
+
if (res.status === 403) {
|
|
422
|
+
// One error line per outage: the hold probes every few seconds.
|
|
423
|
+
if (!this.forbiddenLogged) {
|
|
424
|
+
this.forbiddenLogged = true;
|
|
425
|
+
this.opts.logger.error(
|
|
426
|
+
{ status: res.status },
|
|
427
|
+
'usage check: the hub refuses this client (403) — check the client’s usage:write scope; treated as an outage'
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
return { kind: 'outage' };
|
|
431
|
+
}
|
|
432
|
+
this.opts.logger.debug({ status: res.status }, 'usage check: the hub answered non-2xx');
|
|
433
|
+
return { kind: 'outage' };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { USAGE_EVENT_MAX_FUTURE_MS } from '@antasphere/chassis-contract';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How far this instance's clock sits from the hub's (PRDCT-2644). The hub
|
|
5
|
+
* judges every usage event's `occurredAt` against ITS clock, the poster
|
|
6
|
+
* against this instance's: a clock ahead of the hub's by more than the
|
|
7
|
+
* forward window (five minutes) stamps events the hub would refuse as from
|
|
8
|
+
* the future, and the poster, reading its own clock, cannot see it. So boot
|
|
9
|
+
* asks once: one GET of the discovery document the cloud boot already
|
|
10
|
+
* trusts (a HEAD may be refused by a proxy), and the `Date` header of its
|
|
11
|
+
* answer is the hub's clock at that moment.
|
|
12
|
+
*
|
|
13
|
+
* Positive = this instance is AHEAD of the hub. `null` when the hub is
|
|
14
|
+
* unreachable, answers no `Date` header, or one that does not parse: this is
|
|
15
|
+
* a diagnostic, it never throws and never refuses a boot. The header has a
|
|
16
|
+
* one-second resolution and the request's latency sits inside the reading,
|
|
17
|
+
* both far below the five-minute window it is compared with.
|
|
18
|
+
*/
|
|
19
|
+
export async function hubClockSkewMs(
|
|
20
|
+
issuerUrl: string,
|
|
21
|
+
opts: { fetchImpl?: typeof fetch; timeoutMs?: number; now?: () => number } = {}
|
|
22
|
+
): Promise<number | null> {
|
|
23
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
24
|
+
const now = opts.now ?? Date.now;
|
|
25
|
+
const url = issuerUrl.replace(/\/+$/, '') + '/.well-known/openid-configuration';
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetchImpl(url, {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
headers: { accept: 'application/json' },
|
|
30
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 5_000)
|
|
31
|
+
});
|
|
32
|
+
const localNow = now();
|
|
33
|
+
// The body is not read: only the header matters; cancel it so the
|
|
34
|
+
// connection is released.
|
|
35
|
+
await res.body?.cancel().catch(() => {});
|
|
36
|
+
const header = res.headers.get('date');
|
|
37
|
+
if (!header) return null;
|
|
38
|
+
const hubNow = Date.parse(header);
|
|
39
|
+
if (Number.isNaN(hubNow)) return null;
|
|
40
|
+
return localNow - hubNow;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The sentence boot logs when the skew exceeds the hub's forward window, or
|
|
48
|
+
* null when it does not (`|skewMs| <= USAGE_EVENT_MAX_FUTURE_MS`).
|
|
49
|
+
*/
|
|
50
|
+
export function describeHubClockSkew(skewMs: number): string | null {
|
|
51
|
+
if (Math.abs(skewMs) <= USAGE_EVENT_MAX_FUTURE_MS) return null;
|
|
52
|
+
const minutes = Math.round(Math.abs(skewMs) / 60_000);
|
|
53
|
+
const unit = minutes === 1 ? 'minute' : 'minutes';
|
|
54
|
+
if (skewMs > 0) {
|
|
55
|
+
return `this instance's clock is ${minutes} ${unit} ahead of the hub's; usage events it stamps would be refused by the hub's window`;
|
|
56
|
+
}
|
|
57
|
+
return `this instance's clock is ${minutes} ${unit} behind the hub's; usage events it stamps would read older than they are at the hub, and the poster's own window check would not match the hub's`;
|
|
58
|
+
}
|