@k2b/cloud 0.6.0-rc.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/README.md +98 -0
- package/package.json +128 -0
- package/public/logo.svg +1 -0
- package/scripts/app-favicon.test.ts +49 -0
- package/scripts/app-favicon.ts +51 -0
- package/scripts/build.ts +205 -0
- package/scripts/preload.ts +164 -0
- package/scripts/sync-recovery-smoke.ts +200 -0
- package/src/_internal/app-favicon.ts +2 -0
- package/src/_internal/app-presentation.ts +61 -0
- package/src/_internal/app-workspace-state.ts +24 -0
- package/src/_internal/bounded-json.ts +47 -0
- package/src/_internal/build-metadata.ts +9 -0
- package/src/_internal/capabilities.ts +1118 -0
- package/src/_internal/define-app.ts +801 -0
- package/src/_internal/heartbeat.ts +167 -0
- package/src/_internal/help-catalog.ts +241 -0
- package/src/_internal/help.ts +95 -0
- package/src/_internal/nats-connection.ts +34 -0
- package/src/_internal/page-responses.ts +40 -0
- package/src/_internal/process-sync.ts +117 -0
- package/src/_internal/readiness.ts +3 -0
- package/src/_internal/registry-validation.ts +136 -0
- package/src/_internal/registry-watch.ts +52 -0
- package/src/_internal/registry.ts +338 -0
- package/src/_internal/runtime-compatibility.ts +64 -0
- package/src/_internal/runtime-context.ts +84 -0
- package/src/_internal/runtime-watcher.ts +78 -0
- package/src/_internal/static-assets.ts +86 -0
- package/src/_internal/status-preserving-ssr.ts +49 -0
- package/src/access/PermissionEditor.tsx +486 -0
- package/src/access/ResourceApiKeys.tsx +271 -0
- package/src/access/messages.ts +121 -0
- package/src/access/ui.ts +16 -0
- package/src/account/Avatar.tsx +28 -0
- package/src/account/EntitySearch.tsx +363 -0
- package/src/account/avatar-upload-dialog.tsx +197 -0
- package/src/account/avatar-upload.ts +149 -0
- package/src/account/ui.ts +3 -0
- package/src/ai/admin.ts +47 -0
- package/src/ai/approval-routes.ts +130 -0
- package/src/ai/approvals.ts +139 -0
- package/src/ai/assistant-models.ts +61 -0
- package/src/ai/attachments.ts +40 -0
- package/src/ai/browser.ts +92 -0
- package/src/ai/capabilities.ts +714 -0
- package/src/ai/capability-execution.ts +151 -0
- package/src/ai/chat/blocks.render.test.tsx +1108 -0
- package/src/ai/chat/blocks.tsx +730 -0
- package/src/ai/chat/builtin-tools.tsx +428 -0
- package/src/ai/chat/capability-table.tsx +87 -0
- package/src/ai/chat/composer-adapter.ts +169 -0
- package/src/ai/chat/file-tools.tsx +81 -0
- package/src/ai/chat/message-actions.tsx +370 -0
- package/src/ai/chat/message-utils.ts +479 -0
- package/src/ai/chat/messages.ts +43 -0
- package/src/ai/chat/presentation.tsx +395 -0
- package/src/ai/chat/primitives.tsx +3 -0
- package/src/ai/chat/tool-disclosure.tsx +41 -0
- package/src/ai/chat/turn-navigator-utils.ts +28 -0
- package/src/ai/chat/turn-navigator.tsx +164 -0
- package/src/ai/chat/user-message.tsx +149 -0
- package/src/ai/chat/visual-tools.tsx +435 -0
- package/src/ai/chat/web-tools.tsx +202 -0
- package/src/ai/chat-task-contracts.ts +113 -0
- package/src/ai/chat-tasks.ts +952 -0
- package/src/ai/client/controller.ts +1211 -0
- package/src/ai/client/file-source.ts +95 -0
- package/src/ai/client/live-connection.ts +140 -0
- package/src/ai/client/projection.ts +178 -0
- package/src/ai/client/transport.ts +111 -0
- package/src/ai/compaction.ts +182 -0
- package/src/ai/credentials.ts +124 -0
- package/src/ai/default-tool-contracts.ts +108 -0
- package/src/ai/default-tools.ts +127 -0
- package/src/ai/enrich.ts +280 -0
- package/src/ai/executor.ts +1624 -0
- package/src/ai/fetch-file-tool.ts +313 -0
- package/src/ai/file-context.ts +92 -0
- package/src/ai/file-media-type.ts +28 -0
- package/src/ai/file-mount.ts +16 -0
- package/src/ai/file-tools.ts +566 -0
- package/src/ai/files-store.ts +390 -0
- package/src/ai/firecrawl-tools.ts +305 -0
- package/src/ai/grids-skill.ts +126 -0
- package/src/ai/http.ts +265 -0
- package/src/ai/index.ts +337 -0
- package/src/ai/kit-skill.ts +22 -0
- package/src/ai/limits.ts +5 -0
- package/src/ai/live-events.ts +289 -0
- package/src/ai/live-messages.ts +45 -0
- package/src/ai/live-outbox.ts +102 -0
- package/src/ai/live-routes.ts +412 -0
- package/src/ai/live.ts +2 -0
- package/src/ai/maintenance.ts +302 -0
- package/src/ai/markdown-pdf-tool.ts +95 -0
- package/src/ai/memories.ts +605 -0
- package/src/ai/memory-learning-runs.ts +222 -0
- package/src/ai/memory-learning.ts +619 -0
- package/src/ai/memory-tool.ts +120 -0
- package/src/ai/memory-workflow-evidence.ts +158 -0
- package/src/ai/migrate.ts +2082 -0
- package/src/ai/model-access-migrate.ts +30 -0
- package/src/ai/model-access.ts +191 -0
- package/src/ai/personal-agent.ts +6 -0
- package/src/ai/prefs.ts +72 -0
- package/src/ai/project-tool.ts +94 -0
- package/src/ai/projects-routes.ts +277 -0
- package/src/ai/projects.ts +902 -0
- package/src/ai/protocol.ts +279 -0
- package/src/ai/provider.ts +73 -0
- package/src/ai/public-projection.ts +25 -0
- package/src/ai/resource-markers.ts +31 -0
- package/src/ai/resource-refs.ts +70 -0
- package/src/ai/routes.ts +1131 -0
- package/src/ai/runtime-tools.ts +39 -0
- package/src/ai/runtime.ts +544 -0
- package/src/ai/settings.ts +529 -0
- package/src/ai/short-id.ts +45 -0
- package/src/ai/skill-catalog.ts +73 -0
- package/src/ai/skill-format.ts +145 -0
- package/src/ai/skill-seeds.ts +430 -0
- package/src/ai/skill-tool.ts +81 -0
- package/src/ai/skills-routes.ts +143 -0
- package/src/ai/skills.ts +934 -0
- package/src/ai/solid.ts +27 -0
- package/src/ai/store.ts +3113 -0
- package/src/ai/stream.ts +308 -0
- package/src/ai/structured-runs.ts +66 -0
- package/src/ai/structured.ts +186 -0
- package/src/ai/system-prompt.ts +170 -0
- package/src/ai/task-prompt.ts +22 -0
- package/src/ai/timeline.ts +148 -0
- package/src/ai/tool-audit.ts +177 -0
- package/src/ai/tool-result-budget.ts +16 -0
- package/src/ai/tools.ts +155 -0
- package/src/ai/types.ts +967 -0
- package/src/ai/ui.tsx +39 -0
- package/src/ai/usage-migrate.ts +81 -0
- package/src/ai/usage.ts +315 -0
- package/src/ai/validate.ts +54 -0
- package/src/ai/vision-tool.ts +79 -0
- package/src/api/accounts-entities.ts +174 -0
- package/src/api/admin-ai-projects.ts +74 -0
- package/src/api/admin-ai-skills.ts +76 -0
- package/src/api/admin-ai-usage.ts +42 -0
- package/src/api/admin-core-settings.ts +508 -0
- package/src/api/admin-identity.ts +148 -0
- package/src/api/admin-lifecycle.ts +206 -0
- package/src/api/admin-linux-identities.ts +66 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/app-approval.ts +129 -0
- package/src/api/apps.ts +51 -0
- package/src/api/auth/schemas.ts +54 -0
- package/src/api/auth.ts +370 -0
- package/src/api/capabilities.ts +658 -0
- package/src/api/help-messages.ts +19 -0
- package/src/api/help.ts +78 -0
- package/src/api/index.ts +108 -0
- package/src/api/mcp.ts +744 -0
- package/src/api/me-rail.ts +45 -0
- package/src/api/me.ts +753 -0
- package/src/api/search/schemas.ts +79 -0
- package/src/api/search.bench.ts +185 -0
- package/src/api/search.ts +407 -0
- package/src/api/sync-ops.ts +79 -0
- package/src/api/widgets.ts +143 -0
- package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
- package/src/browser/CloudResourceSearch.tsx +791 -0
- package/src/browser/app-approval-vault.ts +305 -0
- package/src/browser/app-approval.ts +234 -0
- package/src/browser/live-websocket.ts +268 -0
- package/src/browser/locale-preference.ts +25 -0
- package/src/browser/notification-target.ts +27 -0
- package/src/browser/notification-timeout.ts +14 -0
- package/src/browser/notifications.ts +181 -0
- package/src/browser/resource-clipboard.ts +70 -0
- package/src/browser/resource-picker.tsx +39 -0
- package/src/browser/resource-search-messages.ts +55 -0
- package/src/browser/resource-search.ts +27 -0
- package/src/capabilities/client.ts +149 -0
- package/src/capabilities/response.ts +39 -0
- package/src/capabilities/server.ts +215 -0
- package/src/capabilities/signals.ts +4 -0
- package/src/capabilities/testing.ts +12 -0
- package/src/capabilities/types.ts +32 -0
- package/src/cli/access.ts +499 -0
- package/src/cli/account.ts +382 -0
- package/src/cli/admin/account-administration.ts +45 -0
- package/src/cli/admin/account-categories.ts +49 -0
- package/src/cli/admin/ai-usage.ts +118 -0
- package/src/cli/admin/app-sign-in.ts +49 -0
- package/src/cli/admin/data.ts +343 -0
- package/src/cli/admin/documentation.ts +34 -0
- package/src/cli/admin/gateway.ts +155 -0
- package/src/cli/admin/index.ts +61 -0
- package/src/cli/admin/instance.ts +250 -0
- package/src/cli/admin/jobs.ts +223 -0
- package/src/cli/admin/legal.ts +122 -0
- package/src/cli/admin/linux.ts +53 -0
- package/src/cli/admin/logs.ts +324 -0
- package/src/cli/admin/metrics.ts +142 -0
- package/src/cli/admin/nats.ts +167 -0
- package/src/cli/admin/notifications.ts +516 -0
- package/src/cli/admin/shared.ts +113 -0
- package/src/cli/admin/sync.ts +188 -0
- package/src/cli/admin/telemetry.ts +314 -0
- package/src/cli/admin/webhooks.ts +222 -0
- package/src/cli/admin/workflows.ts +367 -0
- package/src/cli/apps.ts +40 -0
- package/src/cli/capabilities.ts +239 -0
- package/src/cli/commands.ts +604 -0
- package/src/cli/index.ts +61 -0
- package/src/cli/locale.ts +31 -0
- package/src/cli/output.ts +58 -0
- package/src/clients/core.ts +20 -0
- package/src/config/env.ts +54 -0
- package/src/config/index.ts +6 -0
- package/src/contracts/account-categories.ts +62 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app-approval.ts +197 -0
- package/src/contracts/app.ts +162 -0
- package/src/contracts/capabilities.ts +484 -0
- package/src/contracts/index.ts +14 -0
- package/src/contracts/notification-live.ts +66 -0
- package/src/contracts/notification-types.ts +235 -0
- package/src/contracts/posix.ts +69 -0
- package/src/contracts/profile.ts +85 -0
- package/src/contracts/rail-preferences.ts +53 -0
- package/src/contracts/registry.ts +110 -0
- package/src/contracts/resource-clipboard.ts +33 -0
- package/src/contracts/settings-types.ts +98 -0
- package/src/contracts/shared.ts +408 -0
- package/src/contracts/user-notifications.ts +101 -0
- package/src/contracts/widgets.ts +183 -0
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/index.ts +58 -0
- package/src/server/actor.ts +41 -0
- package/src/server/api/index.ts +2 -0
- package/src/server/api/respond.ts +96 -0
- package/src/server/api-client.ts +54 -0
- package/src/server/app-context.ts +39 -0
- package/src/server/help.ts +187 -0
- package/src/server/index.ts +89 -0
- package/src/server/locale.ts +55 -0
- package/src/server/middleware/auth.ts +386 -0
- package/src/server/middleware/index.ts +28 -0
- package/src/server/middleware/invocation.ts +55 -0
- package/src/server/middleware/middleware.ts +65 -0
- package/src/server/middleware/openapi.ts +126 -0
- package/src/server/middleware/rate-limit.ts +125 -0
- package/src/server/middleware/request-logger.ts +41 -0
- package/src/server/middleware/route-template.ts +46 -0
- package/src/server/middleware/runtime.ts +22 -0
- package/src/server/middleware/settings.ts +55 -0
- package/src/server/middleware/validator.ts +49 -0
- package/src/server/middleware/workload.ts +22 -0
- package/src/server/ratelimit.ts +151 -0
- package/src/server/request-cookies.ts +18 -0
- package/src/server/services/access.ts +644 -0
- package/src/server/services/freeipa/client.ts +137 -0
- package/src/server/services/freeipa/index.ts +9 -0
- package/src/server/services/freeipa/session.ts +123 -0
- package/src/server/services/freeipa/test-certificates.ts +44 -0
- package/src/server/services/freeipa/tls.ts +44 -0
- package/src/server/services/freeipa/transport.ts +116 -0
- package/src/server/services/freeipa/util.ts +59 -0
- package/src/server/services/geo.ts +198 -0
- package/src/server/services/index.ts +48 -0
- package/src/server/services/pagination.ts +22 -0
- package/src/server/services/services.ts +13 -0
- package/src/server/time.ts +33 -0
- package/src/services/account-category-policy.ts +51 -0
- package/src/services/account-lifecycle/audit.ts +41 -0
- package/src/services/account-lifecycle/index.ts +940 -0
- package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
- package/src/services/account-lifecycle/notification-sender.ts +13 -0
- package/src/services/account-lifecycle/scheduler.ts +506 -0
- package/src/services/account-model.ts +22 -0
- package/src/services/accounts/app.ts +1671 -0
- package/src/services/accounts/authz.ts +44 -0
- package/src/services/accounts/avatar.ts +120 -0
- package/src/services/accounts/base-group.ts +11 -0
- package/src/services/accounts/base-user.ts +46 -0
- package/src/services/accounts/duplicate-emails.ts +82 -0
- package/src/services/accounts/entities.ts +622 -0
- package/src/services/accounts/group-sql.ts +106 -0
- package/src/services/accounts/groups.ts +272 -0
- package/src/services/accounts/index.ts +14 -0
- package/src/services/accounts/ipa-data.ts +66 -0
- package/src/services/accounts/lifecycle.ts +2 -0
- package/src/services/accounts/local-groups.ts +499 -0
- package/src/services/accounts/model.ts +129 -0
- package/src/services/accounts/notification-sender.ts +31 -0
- package/src/services/accounts/posix.ts +386 -0
- package/src/services/accounts/request-policy.ts +11 -0
- package/src/services/accounts/switching.ts +126 -0
- package/src/services/accounts/users.ts +745 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/app-approval.ts +498 -0
- package/src/services/audit/index.ts +438 -0
- package/src/services/auth-flows/index.ts +15 -0
- package/src/services/auth-flows/ipa.ts +197 -0
- package/src/services/auth-flows/magic-link.ts +185 -0
- package/src/services/auth-flows/notification-sender.ts +10 -0
- package/src/services/auth-flows/password-reset.ts +268 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/document-extraction.ts +148 -0
- package/src/services/freeipa-config.ts +179 -0
- package/src/services/gateway.ts +186 -0
- package/src/services/identity/constants.ts +18 -0
- package/src/services/identity/index.ts +52 -0
- package/src/services/identity/invocation-actor.ts +120 -0
- package/src/services/identity/invocation-authority.ts +53 -0
- package/src/services/identity/invocation-operations.ts +15 -0
- package/src/services/identity/invocation-token.ts +276 -0
- package/src/services/identity/key-config.ts +41 -0
- package/src/services/identity/key-ring.ts +530 -0
- package/src/services/identity/metrics.ts +29 -0
- package/src/services/identity/routes.ts +18 -0
- package/src/services/identity/runtime-config.ts +88 -0
- package/src/services/identity/session-token.ts +152 -0
- package/src/services/identity/signer-refresh.ts +44 -0
- package/src/services/identity/workload-auth.ts +64 -0
- package/src/services/index.ts +187 -0
- package/src/services/ipa/auth.ts +216 -0
- package/src/services/ipa/connection.ts +57 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/groups.ts +722 -0
- package/src/services/ipa/guard.ts +17 -0
- package/src/services/ipa/index.ts +17 -0
- package/src/services/ipa/posix.ts +19 -0
- package/src/services/ipa/profile.ts +132 -0
- package/src/services/ipa/runtime.ts +28 -0
- package/src/services/ipa/search.ts +154 -0
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.ts +100 -0
- package/src/services/ipa/sync.ts +862 -0
- package/src/services/ipa/users.ts +806 -0
- package/src/services/legal-consent.ts +76 -0
- package/src/services/logging/index.ts +477 -0
- package/src/services/logging/redaction.ts +20 -0
- package/src/services/logging/trace.ts +1231 -0
- package/src/services/mandates/index.ts +855 -0
- package/src/services/mandates/policy.ts +85 -0
- package/src/services/network-security.ts +31 -0
- package/src/services/notifications/batches.ts +722 -0
- package/src/services/notifications/browser.ts +252 -0
- package/src/services/notifications/catalog.ts +113 -0
- package/src/services/notifications/channels.ts +104 -0
- package/src/services/notifications/dispatcher.ts +139 -0
- package/src/services/notifications/email.ts +118 -0
- package/src/services/notifications/index.ts +633 -0
- package/src/services/notifications/live.ts +50 -0
- package/src/services/notifications/observability.ts +407 -0
- package/src/services/notifications/platform.ts +421 -0
- package/src/services/notifications/runtime.ts +120 -0
- package/src/services/notifications/user.ts +248 -0
- package/src/services/notifications/web-push-transport.ts +135 -0
- package/src/services/oauth-tokens.ts +209 -0
- package/src/services/outbox.ts +187 -0
- package/src/services/pdf/gotenberg.ts +290 -0
- package/src/services/pdf/index.ts +43 -0
- package/src/services/pdf/markdown.ts +209 -0
- package/src/services/pdf/template-preview.ts +122 -0
- package/src/services/postgres.ts +76 -0
- package/src/services/providers/index.ts +27 -0
- package/src/services/providers/local/auth.ts +51 -0
- package/src/services/providers/local/index.ts +4 -0
- package/src/services/providers/local/users.ts +263 -0
- package/src/services/rail-preferences.ts +27 -0
- package/src/services/runtime-lifecycle.ts +191 -0
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.ts +891 -0
- package/src/services/service-accounts.ts +184 -0
- package/src/services/session/index.ts +236 -0
- package/src/services/session/test-fixture.ts +18 -0
- package/src/services/session/user.ts +144 -0
- package/src/services/settings/api.ts +61 -0
- package/src/services/settings/app.ts +86 -0
- package/src/services/settings/core-settings.ts +796 -0
- package/src/services/settings/crypto.ts +69 -0
- package/src/services/settings/defaults.ts +374 -0
- package/src/services/settings/index.ts +215 -0
- package/src/services/settings/namespace.ts +9 -0
- package/src/services/settings/snapshot.ts +49 -0
- package/src/services/settings/store.ts +241 -0
- package/src/services/settings/templates.ts +4 -0
- package/src/services/sync-ops.ts +403 -0
- package/src/services/topic-cursor.ts +22 -0
- package/src/services/weather/forecast.ts +323 -0
- package/src/services/weather/geo.ts +119 -0
- package/src/services/weather/index.ts +67 -0
- package/src/services/weather/location.ts +24 -0
- package/src/services/weather/locations.ts +133 -0
- package/src/services/weather/migrate.ts +45 -0
- package/src/services/weather/settings.ts +66 -0
- package/src/services/weather/short-id.ts +57 -0
- package/src/services/weather/types.ts +61 -0
- package/src/services/weather/ui.ts +50 -0
- package/src/services/webauthn.ts +385 -0
- package/src/shared/account-action-notice.ts +70 -0
- package/src/shared/account-display.ts +15 -0
- package/src/shared/account-session.ts +15 -0
- package/src/shared/ai-platform-prompt.ts +134 -0
- package/src/shared/ai-usage.ts +49 -0
- package/src/shared/app-presentation.ts +48 -0
- package/src/shared/app-url.ts +12 -0
- package/src/shared/branding.ts +14 -0
- package/src/shared/capability-messages.ts +70 -0
- package/src/shared/email-html.ts +54 -0
- package/src/shared/format.ts +91 -0
- package/src/shared/help.ts +68 -0
- package/src/shared/icons.ts +400 -0
- package/src/shared/index.ts +54 -0
- package/src/shared/locale.ts +48 -0
- package/src/shared/login-method.ts +44 -0
- package/src/shared/markdown/client.ts +130 -0
- package/src/shared/markdown/extensions/code.ts +105 -0
- package/src/shared/markdown/extensions/guided-help.ts +100 -0
- package/src/shared/markdown/extensions/images.ts +79 -0
- package/src/shared/markdown/extensions/info-blocks.ts +97 -0
- package/src/shared/markdown/extensions/katex.ts +120 -0
- package/src/shared/markdown/extensions/links.ts +43 -0
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +109 -0
- package/src/shared/markdown/extensions/task-list.ts +53 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +193 -0
- package/src/shared/markdown/shared.ts +36 -0
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/network-address.ts +106 -0
- package/src/shared/redirect.ts +56 -0
- package/src/shared/template-rendering.ts +140 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +53 -0
- package/src/ssr/AdminSidebar.tsx +50 -0
- package/src/ssr/AppLaunchpad.island.tsx +236 -0
- package/src/ssr/AppWorkspaceController.island.tsx +21 -0
- package/src/ssr/BrowserNotifications.island.tsx +135 -0
- package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
- package/src/ssr/GlobalSearchDialog.tsx +35 -0
- package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
- package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
- package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
- package/src/ssr/Layout.render.test.tsx +148 -0
- package/src/ssr/Layout.tsx +292 -0
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutFooter.tsx +22 -0
- package/src/ssr/LayoutHeader.tsx +81 -0
- package/src/ssr/LayoutHelp.tsx +869 -0
- package/src/ssr/LayoutPreferences.island.tsx +36 -0
- package/src/ssr/LayoutRail.tsx +58 -0
- package/src/ssr/MinimalLayout.tsx +46 -0
- package/src/ssr/PageError.tsx +29 -0
- package/src/ssr/ProfilePreferences.island.tsx +92 -0
- package/src/ssr/RailApps.island.tsx +61 -0
- package/src/ssr/RailEditor.tsx +293 -0
- package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/admin-active-link.ts +47 -0
- package/src/ssr/admin-navigation.ts +78 -0
- package/src/ssr/app-appearance.ts +23 -0
- package/src/ssr/app-navigation.ts +41 -0
- package/src/ssr/help-messages.ts +104 -0
- package/src/ssr/index.ts +14 -0
- package/src/ssr/islands/SearchBar.island.tsx +80 -0
- package/src/ssr/islands/index.ts +16 -0
- package/src/ssr/layout-context.ts +17 -0
- package/src/ssr/layout-help-markdown.ts +9 -0
- package/src/ssr/layout-help-navigation.ts +24 -0
- package/src/ssr/layout-help-url.ts +5 -0
- package/src/ssr/layout-runtime.ts +22 -0
- package/src/ssr/page-error-messages.ts +25 -0
- package/src/ssr/platform-messages.ts +135 -0
- package/src/ssr/preference-controller.ts +44 -0
- package/src/ssr/profile-preferences-messages.ts +29 -0
- package/src/ssr/rail-context.ts +21 -0
- package/src/ssr/rail-messages.ts +66 -0
- package/src/ssr/rail-navigation.ts +60 -0
- package/src/ssr/runtime.ts +31 -0
- package/src/ssr/url-filter.ts +127 -0
- package/src/styles/base-popover.css +36 -0
- package/src/styles/effects.css +851 -0
- package/src/styles/global.css +268 -0
- package/src/styles/input.css +58 -0
- package/src/styles/tokens.css +230 -0
- package/src/styles/utilities-buttons.css +405 -0
- package/src/styles/utilities-data.css +33 -0
- package/src/styles/utilities-feedback.css +232 -0
- package/src/styles/utilities-layout.css +603 -0
- package/src/styles/utilities-markdown-editor.css +525 -0
- package/src/styles/utilities-markdown-table.css +234 -0
- package/src/styles/utilities-navigation.css +1054 -0
- package/src/types/ambient.d.ts +24 -0
- package/src/workflows/ai/index.ts +17 -0
- package/src/workflows/ai/runtime.ts +286 -0
- package/src/workflows/ai/store.ts +314 -0
- package/src/workflows/ai/types.ts +111 -0
- package/src/workflows/ai-actions.ts +269 -0
- package/src/workflows/authoring.ts +88 -0
- package/src/workflows/builtins.ts +193 -0
- package/src/workflows/contracts.ts +280 -0
- package/src/workflows/definition.ts +334 -0
- package/src/workflows/editor.ts +113 -0
- package/src/workflows/index.ts +71 -0
- package/src/workflows/language/binder.ts +45 -0
- package/src/workflows/language/canonical.ts +29 -0
- package/src/workflows/language/compiler.ts +396 -0
- package/src/workflows/language/expressions.ts +34 -0
- package/src/workflows/language/index.ts +20 -0
- package/src/workflows/language/references.ts +55 -0
- package/src/workflows/language/schema.ts +134 -0
- package/src/workflows/language/strict-yaml.ts +131 -0
- package/src/workflows/manifest.ts +24 -0
- package/src/workflows/module.ts +12 -0
- package/src/workflows/runtime/coordinator.ts +115 -0
- package/src/workflows/runtime/executor.ts +1003 -0
- package/src/workflows/runtime/index.ts +60 -0
- package/src/workflows/runtime/instant.ts +8 -0
- package/src/workflows/runtime/ports.ts +213 -0
- package/src/workflows/runtime/schedule.ts +203 -0
- package/src/workflows/runtime/trigger.ts +37 -0
- package/src/workflows/runtime/wake.ts +13 -0
- package/src/workflows/store/actions.ts +438 -0
- package/src/workflows/store/budget.ts +145 -0
- package/src/workflows/store/definitions.ts +345 -0
- package/src/workflows/store/events.ts +383 -0
- package/src/workflows/store/index.ts +107 -0
- package/src/workflows/store/observability.ts +594 -0
- package/src/workflows/store/runs.ts +975 -0
- package/src/workflows/store/transaction.ts +23 -0
- package/src/workflows/store/worker.ts +439 -0
- package/src/workflows/testing/conformance.ts +72 -0
- package/src/workflows/testing/index.ts +11 -0
- package/src/workflows/testing/process-fixtures.ts +275 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import { createMiddleware } from "hono/factory";
|
|
3
|
+
import type { MessageResponse, Role, RoleOrSpecial, User, UserProfile, UserProvider } from "../../contracts/shared";
|
|
4
|
+
import { isAccountExpired } from "../../services/account-model";
|
|
5
|
+
import { isReservedWorkloadApiCredential } from "../../services/identity/workload-auth";
|
|
6
|
+
import { oauthTokens } from "../../services/oauth-tokens";
|
|
7
|
+
import { serviceAccountCredentials } from "../../services/service-account-credentials";
|
|
8
|
+
import type { ServiceAccount } from "../../services/service-accounts";
|
|
9
|
+
import { session } from "../../services/session";
|
|
10
|
+
import { createLoginRedirectUrl } from "../../shared/redirect";
|
|
11
|
+
import type { AccessSubject } from "../services/access";
|
|
12
|
+
|
|
13
|
+
// ==========================
|
|
14
|
+
// Types
|
|
15
|
+
// ==========================
|
|
16
|
+
|
|
17
|
+
export type InvocationProvenance = {
|
|
18
|
+
kind: "invocation";
|
|
19
|
+
callingAppId: string;
|
|
20
|
+
credentialKind: "session" | "oauth" | "api_key" | "mandate";
|
|
21
|
+
invocationId: string;
|
|
22
|
+
requestId?: string | null;
|
|
23
|
+
mandateId?: string;
|
|
24
|
+
mandateRevision?: number;
|
|
25
|
+
workloadType?: string;
|
|
26
|
+
workloadId?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type UserRequestActor = {
|
|
30
|
+
kind: "user";
|
|
31
|
+
user: User;
|
|
32
|
+
delegation?: InvocationProvenance;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type ServiceAccountRequestActor =
|
|
36
|
+
| {
|
|
37
|
+
kind: "service_account";
|
|
38
|
+
serviceAccount: ServiceAccount;
|
|
39
|
+
delegatedUser: User;
|
|
40
|
+
scopes: string[];
|
|
41
|
+
credentialId?: string | null;
|
|
42
|
+
credentialExpiresAt?: string | null;
|
|
43
|
+
delegation?: InvocationProvenance;
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
kind: "service_account";
|
|
47
|
+
serviceAccount: ServiceAccount;
|
|
48
|
+
delegatedUser: null;
|
|
49
|
+
scopes: string[];
|
|
50
|
+
credentialId?: string | null;
|
|
51
|
+
credentialExpiresAt?: string | null;
|
|
52
|
+
delegation?: InvocationProvenance;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type RequestActor = UserRequestActor | ServiceAccountRequestActor;
|
|
56
|
+
|
|
57
|
+
export type RequestCredentialKind = "session" | "oauth" | "api_key" | "invocation";
|
|
58
|
+
|
|
59
|
+
export type RequestAuthority = {
|
|
60
|
+
actor: RequestActor;
|
|
61
|
+
accessSubject: AccessSubject;
|
|
62
|
+
credentialKind: RequestCredentialKind;
|
|
63
|
+
scopes: string[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Hono context with authenticated user variables. */
|
|
67
|
+
export type AuthContext = {
|
|
68
|
+
Variables: {
|
|
69
|
+
actor: RequestActor;
|
|
70
|
+
accessSubject: AccessSubject;
|
|
71
|
+
user: User;
|
|
72
|
+
sessionToken?: string;
|
|
73
|
+
/** Credential class resolved once for downstream delegation. */
|
|
74
|
+
credentialKind?: RequestCredentialKind;
|
|
75
|
+
/** Constraints carried by OAuth/API credentials; absent sessions are unrestricted by transport scope. */
|
|
76
|
+
credentialScopes?: string[];
|
|
77
|
+
/** OAuth scopes for bearer-token requests. Absent for sessions and API credentials. */
|
|
78
|
+
oauthScopes?: string[];
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// ==========================
|
|
83
|
+
// Role-based Middleware
|
|
84
|
+
// ==========================
|
|
85
|
+
|
|
86
|
+
type RejectResult = string | Response | { message: string; status: number };
|
|
87
|
+
|
|
88
|
+
type RoleOptions = {
|
|
89
|
+
onReject?: (c: Context, reason: "unauthenticated" | "forbidden") => RejectResult | Promise<RejectResult>;
|
|
90
|
+
/** Require any of these audiences for OAuth bearer tokens. Sessions and API keys are unaffected. */
|
|
91
|
+
oauthAudience?: string | string[] | (() => string | string[] | Promise<string | string[]>);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
type AccountOptions = RoleOptions & {
|
|
95
|
+
provider?: UserProvider;
|
|
96
|
+
profile?: UserProfile;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const handleReject = async (c: Context, options: RoleOptions, reason: "unauthenticated" | "forbidden"): Promise<Response> => {
|
|
100
|
+
if (options.onReject) {
|
|
101
|
+
const result = await options.onReject(c, reason);
|
|
102
|
+
if (typeof result === "string") return c.redirect(result);
|
|
103
|
+
if (result instanceof Response) return result;
|
|
104
|
+
return c.json({ message: result.message } as MessageResponse, result.status as 400 | 401 | 403 | 404 | 500);
|
|
105
|
+
}
|
|
106
|
+
// Default: JSON response
|
|
107
|
+
if (reason === "unauthenticated") {
|
|
108
|
+
return c.json({ message: "Authentication required" } as MessageResponse, 401);
|
|
109
|
+
}
|
|
110
|
+
return c.json({ message: "Insufficient permissions" } as MessageResponse, 403);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
type AuthenticatedActorResult = {
|
|
114
|
+
token: string | null;
|
|
115
|
+
user: User | null;
|
|
116
|
+
actor: RequestActor | null;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const actorResolutionByRequest = new WeakMap<Context, Promise<AuthenticatedActorResult>>();
|
|
120
|
+
|
|
121
|
+
const loadAuthenticatedActorUncached = async (
|
|
122
|
+
c: Context<AuthContext>,
|
|
123
|
+
options: Pick<RoleOptions, "oauthAudience"> = {},
|
|
124
|
+
): Promise<AuthenticatedActorResult> => {
|
|
125
|
+
const token = session.getToken(c);
|
|
126
|
+
const authenticatedSession = token ? await session.authenticateRequest(c, token) : null;
|
|
127
|
+
const user = authenticatedSession?.user ?? null;
|
|
128
|
+
|
|
129
|
+
if (user && token) {
|
|
130
|
+
c.set("actor", { kind: "user", user });
|
|
131
|
+
c.set("accessSubject", { type: "user", userId: user.id });
|
|
132
|
+
c.set("user", user);
|
|
133
|
+
c.set("sessionToken", token);
|
|
134
|
+
c.set("credentialKind", "session");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (user) return { token, user, actor: { kind: "user", user } };
|
|
138
|
+
|
|
139
|
+
const bearer = session.getBearerToken(c);
|
|
140
|
+
if (bearer && serviceAccountCredentials.isApiToken(bearer)) {
|
|
141
|
+
const authResult = await serviceAccountCredentials.authenticateApiToken(bearer);
|
|
142
|
+
if (!authResult) return { token: null, user: null, actor: null };
|
|
143
|
+
// App workload keys enter only through Core's dedicated broker/authority
|
|
144
|
+
// handlers, which authenticate their exact app binding directly.
|
|
145
|
+
if (isReservedWorkloadApiCredential(authResult)) return { token: null, user: null, actor: null };
|
|
146
|
+
if (authResult.delegatedUser && isAccountExpired(authResult.delegatedUser.accountExpires)) {
|
|
147
|
+
return { token: null, user: null, actor: null };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const actor: RequestActor = {
|
|
151
|
+
kind: "service_account",
|
|
152
|
+
serviceAccount: authResult.serviceAccount,
|
|
153
|
+
delegatedUser: authResult.delegatedUser,
|
|
154
|
+
scopes: authResult.credential.scopes,
|
|
155
|
+
credentialId: authResult.credential.id,
|
|
156
|
+
credentialExpiresAt: authResult.credential.expiresAt,
|
|
157
|
+
};
|
|
158
|
+
c.set("actor", actor);
|
|
159
|
+
c.set("credentialKind", "api_key");
|
|
160
|
+
c.set("credentialScopes", authResult.credential.scopes);
|
|
161
|
+
if (authResult.delegatedUser) {
|
|
162
|
+
c.set("accessSubject", { type: "user", userId: authResult.delegatedUser.id });
|
|
163
|
+
c.set("user", authResult.delegatedUser);
|
|
164
|
+
} else {
|
|
165
|
+
c.set("accessSubject", { type: "service_account", serviceAccountId: authResult.serviceAccount.id });
|
|
166
|
+
}
|
|
167
|
+
return { token: null, user: authResult.delegatedUser, actor };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (bearer) {
|
|
171
|
+
const expectedAudience = typeof options.oauthAudience === "function" ? await options.oauthAudience() : options.oauthAudience;
|
|
172
|
+
const authResult = await oauthTokens.verifyAccessToken(bearer, expectedAudience);
|
|
173
|
+
if (!authResult) return { token: null, user: null, actor: null };
|
|
174
|
+
|
|
175
|
+
if (authResult.kind === "user") {
|
|
176
|
+
if (isAccountExpired(authResult.user.accountExpires)) return { token: null, user: null, actor: null };
|
|
177
|
+
const actor: RequestActor = { kind: "user", user: authResult.user };
|
|
178
|
+
c.set("actor", actor);
|
|
179
|
+
c.set("accessSubject", { type: "user", userId: authResult.user.id });
|
|
180
|
+
c.set("user", authResult.user);
|
|
181
|
+
c.set("oauthScopes", authResult.scopes);
|
|
182
|
+
c.set("credentialKind", "oauth");
|
|
183
|
+
c.set("credentialScopes", authResult.scopes);
|
|
184
|
+
return { token: null, user: authResult.user, actor };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (authResult.delegatedUser && isAccountExpired(authResult.delegatedUser.accountExpires)) {
|
|
188
|
+
return { token: null, user: null, actor: null };
|
|
189
|
+
}
|
|
190
|
+
const actor: RequestActor = {
|
|
191
|
+
kind: "service_account",
|
|
192
|
+
serviceAccount: authResult.serviceAccount,
|
|
193
|
+
delegatedUser: authResult.delegatedUser,
|
|
194
|
+
scopes: authResult.scopes,
|
|
195
|
+
credentialId: null,
|
|
196
|
+
credentialExpiresAt: typeof authResult.payload.exp === "number" ? new Date(authResult.payload.exp * 1_000).toISOString() : null,
|
|
197
|
+
};
|
|
198
|
+
c.set("actor", actor);
|
|
199
|
+
c.set("oauthScopes", authResult.scopes);
|
|
200
|
+
c.set("credentialKind", "oauth");
|
|
201
|
+
c.set("credentialScopes", authResult.scopes);
|
|
202
|
+
if (authResult.delegatedUser) {
|
|
203
|
+
c.set("accessSubject", { type: "user", userId: authResult.delegatedUser.id });
|
|
204
|
+
c.set("user", authResult.delegatedUser);
|
|
205
|
+
} else {
|
|
206
|
+
c.set("accessSubject", { type: "service_account", serviceAccountId: authResult.serviceAccount.id });
|
|
207
|
+
}
|
|
208
|
+
return { token: null, user: authResult.delegatedUser, actor };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { token: null, user: null, actor: null };
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const loadAuthenticatedActor = (
|
|
215
|
+
c: Context<AuthContext>,
|
|
216
|
+
options: Pick<RoleOptions, "oauthAudience"> = {},
|
|
217
|
+
): Promise<AuthenticatedActorResult> => {
|
|
218
|
+
if (options.oauthAudience) return loadAuthenticatedActorUncached(c, options);
|
|
219
|
+
const existing = actorResolutionByRequest.get(c);
|
|
220
|
+
if (existing) return existing;
|
|
221
|
+
const pending = loadAuthenticatedActorUncached(c, options);
|
|
222
|
+
actorResolutionByRequest.set(c, pending);
|
|
223
|
+
return pending;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Universal auth middleware. Handles authentication AND authorization.
|
|
228
|
+
*
|
|
229
|
+
* @param args - Roles to check (OR logic) + optional RoleOptions at the end. Special roles:
|
|
230
|
+
* - "*": No check, always passes (like optionalAuth)
|
|
231
|
+
* - "authenticated": Any logged-in user
|
|
232
|
+
* - "anonymous": Only non-logged-in users (for login page)
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* // API: Only admins (returns JSON 401/403)
|
|
236
|
+
* .use(requireRole("admin"))
|
|
237
|
+
*
|
|
238
|
+
* // API: Admins OR group managers
|
|
239
|
+
* .use(requireRole("admin", "group-manager"))
|
|
240
|
+
*
|
|
241
|
+
* // SSR: Admin area with redirect
|
|
242
|
+
* .use(requireRole("admin", redirect("/")))
|
|
243
|
+
*
|
|
244
|
+
* // SSR: Protected page with login redirect
|
|
245
|
+
* .use(requireRole("authenticated", redirectToLogin))
|
|
246
|
+
*
|
|
247
|
+
* // SSR: Login page (only for non-logged-in users)
|
|
248
|
+
* .use(requireRole("anonymous", redirect("/")))
|
|
249
|
+
*/
|
|
250
|
+
const requireRole = (...args: (RoleOrSpecial | RoleOptions)[]) => {
|
|
251
|
+
// Parse args: roles + optional options at the end
|
|
252
|
+
const lastArg = args[args.length - 1];
|
|
253
|
+
const hasOptions = typeof lastArg === "object" && lastArg !== null && ("onReject" in lastArg || "oauthAudience" in lastArg);
|
|
254
|
+
const options: RoleOptions = hasOptions ? (args.pop() as RoleOptions) : {};
|
|
255
|
+
const roles = args as RoleOrSpecial[];
|
|
256
|
+
|
|
257
|
+
return createMiddleware<AuthContext>(async (c, next) => {
|
|
258
|
+
// "*" = no check at all, pass through (but try to load user)
|
|
259
|
+
if (roles.includes("*")) {
|
|
260
|
+
await loadAuthenticatedActor(c, options);
|
|
261
|
+
return next();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const { user, actor } = await loadAuthenticatedActor(c, options);
|
|
265
|
+
|
|
266
|
+
// "anonymous" = must NOT be logged in
|
|
267
|
+
if (roles.includes("anonymous")) {
|
|
268
|
+
if (actor) {
|
|
269
|
+
return handleReject(c, options, "forbidden");
|
|
270
|
+
}
|
|
271
|
+
return next();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// All other roles require authentication
|
|
275
|
+
if (!actor) {
|
|
276
|
+
return handleReject(c, options, "unauthenticated");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// "authenticated" = any logged-in user
|
|
280
|
+
if (roles.includes("authenticated")) {
|
|
281
|
+
return next();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!user) {
|
|
285
|
+
return handleReject(c, options, "forbidden");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Check if user has at least one required role
|
|
289
|
+
const hasRequiredRole = roles.some((role) => user.roles.includes(role as Role));
|
|
290
|
+
if (!hasRequiredRole) {
|
|
291
|
+
return handleReject(c, options, "forbidden");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return next();
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Require a request that has a user behind it.
|
|
300
|
+
*
|
|
301
|
+
* `requireRole("authenticated")` deliberately admits any authenticated
|
|
302
|
+
* principal, including a resource-bound service account that has no user at
|
|
303
|
+
* all — that is what lets an API key reach an ordinary app route. It is the one
|
|
304
|
+
* role branch that does not imply a user, so a route gated with it that then
|
|
305
|
+
* reaches for roles, ownership or a display name is reaching for something that
|
|
306
|
+
* may not be there.
|
|
307
|
+
*
|
|
308
|
+
* Use this alongside it wherever the handler needs the user itself, so the
|
|
309
|
+
* caller gets a 403 stating the reason instead of whatever the missing user
|
|
310
|
+
* turns into further down.
|
|
311
|
+
*/
|
|
312
|
+
const requireUser = (options: RoleOptions = {}) =>
|
|
313
|
+
createMiddleware<AuthContext>(async (c, next) => {
|
|
314
|
+
const { user } = await loadAuthenticatedActor(c);
|
|
315
|
+
if (user) return next();
|
|
316
|
+
// A caller that reaches here is authenticated but has no user, so the
|
|
317
|
+
// generic "Insufficient permissions" would send them looking for a missing
|
|
318
|
+
// role. `onReject` still wins, which is what keeps SSR redirects working.
|
|
319
|
+
if (options.onReject) return handleReject(c, options, "forbidden");
|
|
320
|
+
return c.json({ message: "Self-service endpoints require a user-backed actor", code: "FORBIDDEN" }, 403);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
/** Require one OAuth scope when the caller uses OAuth. Sessions and API credentials are unaffected. */
|
|
324
|
+
const requireOAuthScope = (...requiredScopes: string[]) =>
|
|
325
|
+
createMiddleware<AuthContext>(async (c, next) => {
|
|
326
|
+
const scopes = c.get("oauthScopes");
|
|
327
|
+
if (!scopes || requiredScopes.some((scope) => scopes.includes(scope))) return next();
|
|
328
|
+
return c.json({ code: "FORBIDDEN", message: `OAuth scope ${requiredScopes.join(" or ")} is required` }, 403);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const getAuthority = (c: Context<AuthContext>): RequestAuthority => {
|
|
332
|
+
const actor = c.get("actor");
|
|
333
|
+
const accessSubject = c.get("accessSubject");
|
|
334
|
+
const credentialKind = c.get("credentialKind");
|
|
335
|
+
if (!actor || !accessSubject || !credentialKind) throw new Error("Request authority has not been resolved");
|
|
336
|
+
return {
|
|
337
|
+
actor,
|
|
338
|
+
accessSubject,
|
|
339
|
+
credentialKind,
|
|
340
|
+
scopes: [...(c.get("credentialScopes") ?? [])],
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/** Preset: Redirect to a fixed URL on rejection */
|
|
345
|
+
const redirect = (url: string): RoleOptions => ({
|
|
346
|
+
onReject: () => url,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
/** Preset: Redirect to login page with returnTo parameter */
|
|
350
|
+
const redirectToLogin: RoleOptions = {
|
|
351
|
+
onReject: (c) => createLoginRedirectUrl(c.req.url),
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const requireAccount = (options: AccountOptions) =>
|
|
355
|
+
createMiddleware<AuthContext>(async (c, next) => {
|
|
356
|
+
const { user } = await loadAuthenticatedActor(c);
|
|
357
|
+
|
|
358
|
+
if (!user) {
|
|
359
|
+
return handleReject(c, options, "unauthenticated");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (options.provider && user.provider !== options.provider) {
|
|
363
|
+
return handleReject(c, options, "forbidden");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (options.profile && user.profile !== options.profile) {
|
|
367
|
+
return handleReject(c, options, "forbidden");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return next();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// ==========================
|
|
374
|
+
// Export
|
|
375
|
+
// ==========================
|
|
376
|
+
|
|
377
|
+
export const auth = {
|
|
378
|
+
session,
|
|
379
|
+
requireRole,
|
|
380
|
+
requireUser,
|
|
381
|
+
requireOAuthScope,
|
|
382
|
+
getAuthority,
|
|
383
|
+
requireAccount,
|
|
384
|
+
redirect,
|
|
385
|
+
redirectToLogin,
|
|
386
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type { AccessSubject } from "../services/access";
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
type AuthContext,
|
|
5
|
+
auth,
|
|
6
|
+
type InvocationProvenance,
|
|
7
|
+
type RequestActor,
|
|
8
|
+
type RequestAuthority,
|
|
9
|
+
type RequestCredentialKind,
|
|
10
|
+
type ServiceAccountRequestActor,
|
|
11
|
+
type UserRequestActor,
|
|
12
|
+
} from "./auth";
|
|
13
|
+
export { type InvocationExpectation, requireInvocation } from "./invocation";
|
|
14
|
+
export { middleware } from "./middleware";
|
|
15
|
+
export {
|
|
16
|
+
imageResponse,
|
|
17
|
+
jsonResponse,
|
|
18
|
+
openApiMeta,
|
|
19
|
+
requiresAdmin,
|
|
20
|
+
requiresAuth,
|
|
21
|
+
requiresIpa,
|
|
22
|
+
requiresIpaUser,
|
|
23
|
+
requiresUser,
|
|
24
|
+
} from "./openapi";
|
|
25
|
+
export { type RateLimitConfig, type RateLimitRouteOverride, rateLimit } from "./rate-limit";
|
|
26
|
+
export { requestLogger } from "./request-logger";
|
|
27
|
+
export { type ValidatorError, type ValidatorErrorResolver, v, validator } from "./validator";
|
|
28
|
+
export { isReservedWorkloadCredential, rejectReservedWorkloadCredential } from "./workload";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Context, MiddlewareHandler } from "hono";
|
|
2
|
+
import { createMiddleware } from "hono/factory";
|
|
3
|
+
import { type ResolvedInvocationAuthority, resolveInvocationAuthority } from "../../services/identity/invocation-actor";
|
|
4
|
+
import { type CloudInvocationClaims, isInvocationJwtCandidate, verifyInvocationToken } from "../../services/identity/invocation-token";
|
|
5
|
+
import type { AuthContext } from "./auth";
|
|
6
|
+
|
|
7
|
+
export type InvocationExpectation = {
|
|
8
|
+
targetAppId: string;
|
|
9
|
+
operation: string;
|
|
10
|
+
schemaHash: string | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type InvocationMiddlewareDependencies = {
|
|
14
|
+
verify?: typeof verifyInvocationToken;
|
|
15
|
+
resolve?: typeof resolveInvocationAuthority;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const bearerToken = (c: Context): string | null => {
|
|
19
|
+
const header = c.req.header("Authorization");
|
|
20
|
+
const match = header?.match(/^Bearer\s+(\S+)$/i);
|
|
21
|
+
return match?.[1] ?? null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const reject = (c: Context<AuthContext>): Response =>
|
|
25
|
+
c.json({ code: "UNAUTHORIZED", message: "A valid Cloud invocation is required" }, 401);
|
|
26
|
+
|
|
27
|
+
const installAuthority = (c: Context<AuthContext>, claims: CloudInvocationClaims, authority: ResolvedInvocationAuthority): void => {
|
|
28
|
+
c.set("actor", authority.actor);
|
|
29
|
+
c.set("accessSubject", authority.accessSubject);
|
|
30
|
+
c.set("credentialKind", "invocation");
|
|
31
|
+
c.set("credentialScopes", authority.scopes);
|
|
32
|
+
if (authority.actor.kind === "user") c.set("user", authority.actor.user);
|
|
33
|
+
else if (authority.actor.delegatedUser) c.set("user", authority.actor.delegatedUser);
|
|
34
|
+
if (claims.credential_kind === "oauth") c.set("oauthScopes", authority.scopes);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const requireInvocation = (
|
|
38
|
+
expected: (c: Context<AuthContext>) => InvocationExpectation | null,
|
|
39
|
+
dependencies: InvocationMiddlewareDependencies = {},
|
|
40
|
+
): MiddlewareHandler<AuthContext> =>
|
|
41
|
+
createMiddleware<AuthContext>(async (c, next) => {
|
|
42
|
+
const token = bearerToken(c);
|
|
43
|
+
const expectation = expected(c);
|
|
44
|
+
if (!token || !expectation || !isInvocationJwtCandidate(token)) return reject(c);
|
|
45
|
+
|
|
46
|
+
const claims = await (dependencies.verify ?? verifyInvocationToken)(token, expectation, { deferSchemaBinding: true });
|
|
47
|
+
if (!claims) return reject(c);
|
|
48
|
+
const authority = await (dependencies.resolve ?? resolveInvocationAuthority)(claims);
|
|
49
|
+
if (!authority) return reject(c);
|
|
50
|
+
if (claims.schema_hash !== expectation.schemaHash) {
|
|
51
|
+
return c.json({ code: "SCHEMA_MISMATCH", message: "The invocation schema no longer matches the target" }, 409);
|
|
52
|
+
}
|
|
53
|
+
installAuthority(c, claims, authority);
|
|
54
|
+
return next();
|
|
55
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `middleware` namespace bundles the request-lifecycle primitives
|
|
3
|
+
* that every cloud app composes into its own router. Apps register
|
|
4
|
+
* what they need; the framework no longer injects anything implicitly.
|
|
5
|
+
*
|
|
6
|
+
* import { middleware, auth } from "@k2b/cloud/server"
|
|
7
|
+
*
|
|
8
|
+
* const router = new Hono<AuthContext>()
|
|
9
|
+
* .use("*", middleware.logger())
|
|
10
|
+
* .use("*", middleware.runtime()) // for Layout / Sidebar / dashboard / search
|
|
11
|
+
* .use("*", middleware.settings()) // for c.get("settings")
|
|
12
|
+
* .use("*", middleware.ratelimit())
|
|
13
|
+
* .use(auth.requireRole("user"))
|
|
14
|
+
* .post(
|
|
15
|
+
* "/",
|
|
16
|
+
* middleware.validator("json", Schema),
|
|
17
|
+
* middleware.openapi({ tags: ["foo"], summary: "Create" }),
|
|
18
|
+
* handler,
|
|
19
|
+
* )
|
|
20
|
+
*
|
|
21
|
+
* `auth` lives separately because it has its own surface
|
|
22
|
+
* (requireRole, redirectToLogin, session.*) and is conceptually
|
|
23
|
+
* orthogonal to the request lifecycle.
|
|
24
|
+
*/
|
|
25
|
+
import { describeRoute } from "hono-openapi";
|
|
26
|
+
import { rateLimit } from "./rate-limit";
|
|
27
|
+
import { requestLogger } from "./request-logger";
|
|
28
|
+
import { routeTemplate } from "./route-template";
|
|
29
|
+
import { runtime } from "./runtime";
|
|
30
|
+
import { settings } from "./settings";
|
|
31
|
+
import { validator } from "./validator";
|
|
32
|
+
|
|
33
|
+
export const middleware = {
|
|
34
|
+
/**
|
|
35
|
+
* Live cluster registry on `c.get("runtime")`. Required for Layout/Sidebar.
|
|
36
|
+
* Also reports the matched route template to gateway telemetry — you do not
|
|
37
|
+
* need `observability()` on top of this.
|
|
38
|
+
*/
|
|
39
|
+
runtime,
|
|
40
|
+
/** Frozen per-request settings snapshot on `c.get("settings")`. */
|
|
41
|
+
settings,
|
|
42
|
+
/**
|
|
43
|
+
* Reports the matched Hono route template to gateway telemetry, so the
|
|
44
|
+
* admin telemetry page can break an app down per endpoint instead of
|
|
45
|
+
* lumping every request under one registry prefix.
|
|
46
|
+
*
|
|
47
|
+
* **Only use this if you do NOT already use `middleware.runtime()`** —
|
|
48
|
+
* `runtime()` does the same thing, and installing both just runs it twice.
|
|
49
|
+
* Practically every app renders `<Layout>` and therefore needs `runtime()`
|
|
50
|
+
* anyway; this exists for the rare app that serves an API surface only.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* new Hono().use("*", middleware.observability()) // no runtime() here
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
observability: () => routeTemplate,
|
|
57
|
+
/** HTTP request logger (logs 5xx as error, 429 as warn, 401/403 as info). */
|
|
58
|
+
logger: () => requestLogger,
|
|
59
|
+
/** Sliding-window rate limiter, keyed by user id (auto fallback to IP). */
|
|
60
|
+
ratelimit: rateLimit,
|
|
61
|
+
/** Zod input validator. `c.req.valid(target)` is fully typed afterward. */
|
|
62
|
+
validator,
|
|
63
|
+
/** OpenAPI route metadata — re-export of hono-openapi's `describeRoute`. */
|
|
64
|
+
openapi: describeRoute,
|
|
65
|
+
} as const;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { type GenerateSpecOptions, resolver } from "hono-openapi";
|
|
2
|
+
import type { ZodType } from "zod";
|
|
3
|
+
|
|
4
|
+
// ==========================
|
|
5
|
+
// Response Helpers
|
|
6
|
+
// ==========================
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Helper to define JSON response schema for OpenAPI documentation.
|
|
10
|
+
*
|
|
11
|
+
* @param schema - Zod schema for the response body
|
|
12
|
+
* @param description - Human-readable description of the response
|
|
13
|
+
* @returns OpenAPI response object with application/json content type
|
|
14
|
+
*/
|
|
15
|
+
export const jsonResponse = <T extends ZodType>(schema: T, description: string) => ({
|
|
16
|
+
description,
|
|
17
|
+
content: {
|
|
18
|
+
"application/json": {
|
|
19
|
+
schema: resolver(schema),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Helper to define image response for OpenAPI documentation.
|
|
26
|
+
*
|
|
27
|
+
* @param description - Human-readable description of the response
|
|
28
|
+
* @returns OpenAPI response object with image/webp content type
|
|
29
|
+
*/
|
|
30
|
+
export const imageResponse = (description: string) => ({
|
|
31
|
+
description,
|
|
32
|
+
content: {
|
|
33
|
+
"image/webp": {
|
|
34
|
+
schema: { type: "string" as const, format: "binary" },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ==========================
|
|
40
|
+
// OpenAPI Specification
|
|
41
|
+
// ==========================
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* OpenAPI spec metadata for the API documentation.
|
|
45
|
+
* Includes info, tags, and security schemes.
|
|
46
|
+
*/
|
|
47
|
+
export const openApiMeta: Partial<GenerateSpecOptions> = {
|
|
48
|
+
documentation: {
|
|
49
|
+
info: {
|
|
50
|
+
// Hardcoded — the OpenAPI spec is generated at module-load and
|
|
51
|
+
// settings reads are async. The dynamic per-deployment app name
|
|
52
|
+
// appears elsewhere (Layout header, page titles).
|
|
53
|
+
title: "Cloud API",
|
|
54
|
+
version: "0.0.1",
|
|
55
|
+
description: "IPA Management Tool API",
|
|
56
|
+
},
|
|
57
|
+
servers: [{ url: "/api", description: "API Server" }],
|
|
58
|
+
tags: [
|
|
59
|
+
{
|
|
60
|
+
name: "Auth",
|
|
61
|
+
description: "Authentication endpoints (login, logout, refresh)",
|
|
62
|
+
},
|
|
63
|
+
{ name: "Users", description: "User listing and search (admin)" },
|
|
64
|
+
{ name: "Groups", description: "Group listing and search (admin)" },
|
|
65
|
+
],
|
|
66
|
+
components: {
|
|
67
|
+
securitySchemes: {
|
|
68
|
+
cookieAuth: {
|
|
69
|
+
type: "apiKey",
|
|
70
|
+
in: "cookie",
|
|
71
|
+
name: "session_token",
|
|
72
|
+
description: "Session cookie (automatically set after login)",
|
|
73
|
+
},
|
|
74
|
+
bearerAuth: {
|
|
75
|
+
type: "http",
|
|
76
|
+
scheme: "bearer",
|
|
77
|
+
description: "Bearer token in Authorization header",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// ==========================
|
|
85
|
+
// Security Requirements
|
|
86
|
+
// ==========================
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Security requirement for routes that need authentication.
|
|
90
|
+
* Accepts either cookie or bearer token.
|
|
91
|
+
*/
|
|
92
|
+
export const requiresAuth = {
|
|
93
|
+
security: [{ cookieAuth: [] as string[], bearerAuth: [] as string[] }],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Security requirement for routes that need admin role.
|
|
98
|
+
* Accepts either cookie or bearer token.
|
|
99
|
+
*/
|
|
100
|
+
export const requiresAdmin = {
|
|
101
|
+
security: [{ cookieAuth: [] as string[], bearerAuth: [] as string[] }],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Security requirement for routes that need any authenticated user.
|
|
106
|
+
* Accepts either cookie or bearer token.
|
|
107
|
+
*/
|
|
108
|
+
export const requiresIpa = {
|
|
109
|
+
security: [{ cookieAuth: [] as string[], bearerAuth: [] as string[] }],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Security requirement for routes that need a full user profile.
|
|
114
|
+
* Accepts either cookie or bearer token.
|
|
115
|
+
*/
|
|
116
|
+
export const requiresUser = {
|
|
117
|
+
security: [{ cookieAuth: [] as string[], bearerAuth: [] as string[] }],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Security requirement for routes that need an IPA-backed full user.
|
|
122
|
+
* Accepts either cookie or bearer token.
|
|
123
|
+
*/
|
|
124
|
+
export const requiresIpaUser = {
|
|
125
|
+
security: [{ cookieAuth: [] as string[], bearerAuth: [] as string[] }],
|
|
126
|
+
};
|