@k2b/cloud 0.7.0 → 0.8.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/package.json +5 -4
- package/scripts/README.md +10 -0
- package/scripts/build.ts +6 -1
- package/scripts/preload.ts +7 -0
- package/scripts/sync-recovery-smoke.ts +21 -6
- package/src/_internal/build-metadata.ts +8 -1
- package/src/_internal/capabilities.ts +35 -12
- package/src/_internal/define-app.ts +28 -11
- package/src/_internal/page-responses.ts +1 -1
- package/src/_internal/readiness.ts +4 -1
- package/src/_internal/runtime-context.ts +1 -1
- package/src/access/PermissionEditor.tsx +15 -9
- package/src/access/PrincipalPicker.tsx +9 -3
- package/src/access/ResourceApiKeys.tsx +1 -1
- package/src/access/ui.ts +1 -2
- package/src/ai/admin.ts +2 -2
- package/src/ai/assistant-models.ts +1 -1
- package/src/ai/audio-tool.ts +1 -1
- package/src/ai/browser-code-contracts.ts +174 -52
- package/src/ai/browser.ts +4 -2
- package/src/ai/capabilities.ts +3 -3
- package/src/ai/chat/blocks.tsx +127 -50
- package/src/ai/chat/builtin-tools.tsx +2 -4
- package/src/ai/chat/capability-table.tsx +1 -1
- package/src/ai/chat/composer-adapter.ts +48 -13
- package/src/ai/chat/message-utils.ts +18 -5
- package/src/ai/chat/presentation.tsx +24 -11
- package/src/ai/chat/tool-disclosure.tsx +6 -2
- package/src/ai/chat/tool-groups.ts +8 -2
- package/src/ai/chat-quotas.ts +2 -2
- package/src/ai/chat-task-contracts.ts +9 -13
- package/src/ai/chat-tasks.ts +39 -18
- package/src/ai/client/controller.ts +87 -38
- package/src/ai/client/projection.ts +13 -6
- package/src/ai/client/transport.ts +4 -2
- package/src/ai/code-capability-routes.ts +22 -27
- package/src/ai/code-capability-transport.ts +2 -1
- package/src/ai/code-execution.ts +53 -0
- package/src/ai/code-mode-skill.ts +9 -5
- package/src/ai/code-runtime-tools.ts +16 -3
- package/src/ai/code-source-contracts.ts +100 -32
- package/src/ai/code-source-tools.ts +49 -6
- package/src/ai/compaction.ts +3 -3
- package/src/ai/default-tools.ts +87 -14
- package/src/ai/dictation-runtime.ts +1 -1
- package/src/ai/draft-content.ts +27 -12
- package/src/ai/enrich.ts +1 -1
- package/src/ai/executor.ts +8 -12
- package/src/ai/fetch-file-tool.ts +2 -2
- package/src/ai/file-content-version.ts +6 -1
- package/src/ai/file-tools.ts +8 -2
- package/src/ai/files-store.ts +55 -17
- package/src/ai/grids-skill.ts +2 -0
- package/src/ai/http.ts +18 -7
- package/src/ai/index.ts +24 -25
- package/src/ai/inference-calls.ts +3 -4
- package/src/ai/live.ts +1 -1
- package/src/ai/markdown-pdf-tool.ts +2 -2
- package/src/ai/memories.ts +30 -27
- package/src/ai/memory-learning-runs.ts +1 -1
- package/src/ai/memory-learning.ts +5 -5
- package/src/ai/memory-tool.ts +1 -5
- package/src/ai/memory-workflow-evidence.ts +9 -4
- package/src/ai/message-queue.ts +23 -12
- package/src/ai/migrate.ts +6 -4
- package/src/ai/model-pricing.ts +4 -4
- package/src/ai/pdf-render-worker.ts +3 -2
- package/src/ai/projects-routes.ts +15 -40
- package/src/ai/projects.ts +18 -13
- package/src/ai/quota-provider.ts +6 -5
- package/src/ai/quota-report.ts +5 -5
- package/src/ai/quotas.ts +3 -3
- package/src/ai/routes.ts +60 -34
- package/src/ai/run-timeout.ts +3 -1
- package/src/ai/runtime-tools.ts +6 -7
- package/src/ai/runtime.ts +38 -20
- package/src/ai/settings.ts +1 -1
- package/src/ai/short-id.ts +4 -5
- package/src/ai/skill-search.ts +21 -10
- package/src/ai/skill-seeds.ts +16 -7
- package/src/ai/skill-tool.ts +6 -1
- package/src/ai/skills-routes.ts +32 -15
- package/src/ai/skills.ts +54 -28
- package/src/ai/store.ts +80 -47
- package/src/ai/structured.ts +6 -2
- package/src/ai/system-prompt.ts +7 -5
- package/src/ai/task-contracts.ts +84 -0
- package/src/ai/task-execution.ts +92 -0
- package/src/ai/todo-contracts.ts +24 -12
- package/src/ai/todo-tool.ts +16 -13
- package/src/ai/tools.ts +1 -1
- package/src/ai/transcription.ts +4 -4
- package/src/ai/turn-timing.ts +1 -1
- package/src/ai/ui.tsx +2 -1
- package/src/ai/usage.ts +1 -1
- package/src/ai/vision-tool.ts +38 -17
- package/src/api/admin-ai-quotas.ts +14 -8
- package/src/api/admin-ai-skills.ts +3 -3
- package/src/api/admin-linux-identities.ts +10 -4
- package/src/api/admin-rail.ts +2 -2
- package/src/api/app-approval.ts +2 -2
- package/src/api/auth.ts +1 -1
- package/src/api/capabilities.ts +45 -13
- package/src/api/index.ts +5 -5
- package/src/api/mcp.ts +5 -12
- package/src/browser/CloudResourceSearch.tsx +1 -1
- package/src/browser/command-bridge.ts +1 -1
- package/src/browser/command-shortcuts.ts +1 -1
- package/src/browser/commands.ts +8 -7
- package/src/browser/testing.ts +2 -1
- package/src/capabilities/client.ts +1 -1
- package/src/capabilities/command-link.ts +1 -1
- package/src/capabilities/server.ts +2 -1
- package/src/cli/admin/ai-quotas.ts +5 -5
- package/src/cli/admin/app-credentials.ts +7 -9
- package/src/cli/admin/index.ts +3 -3
- package/src/cli/admin/instance.ts +2 -2
- package/src/cli/admin/linux.ts +4 -2
- package/src/cli/admin/sync.ts +1 -1
- package/src/cli/capabilities.ts +42 -29
- package/src/config/define-env.ts +97 -0
- package/src/config/env.ts +147 -54
- package/src/config/index.ts +3 -1
- package/src/contracts/capabilities.ts +8 -1
- package/src/contracts/index.ts +5 -7
- package/src/contracts/registry.ts +2 -0
- package/src/contracts/shared.ts +12 -3
- package/src/desktop/index.ts +1 -0
- package/src/desktop/solid.tsx +1 -1
- package/src/index.ts +8 -2
- package/src/server/index.ts +2 -2
- package/src/server/ratelimit.ts +1 -1
- package/src/server/services/access-revision.ts +3 -3
- package/src/server/services/access.ts +3 -1
- package/src/services/accounts/notification-sender.ts +12 -2
- package/src/services/announcements/index.ts +4 -4
- package/src/services/app-approval.ts +11 -10
- package/src/services/identity/key-config.ts +6 -4
- package/src/services/identity/runtime-config.ts +6 -5
- package/src/services/ipa/posix.ts +1 -1
- package/src/services/ipa/sync.ts +1 -1
- package/src/services/logging/index.ts +1 -1
- package/src/services/logging/trace.ts +1 -1
- package/src/services/mandates/index.ts +5 -2
- package/src/services/mandates/policy.ts +106 -6
- package/src/services/notifications/batches.ts +1 -0
- package/src/services/pdf/gotenberg.ts +50 -14
- package/src/services/pdf/index.ts +11 -11
- package/src/services/public-http.ts +1 -1
- package/src/services/rail-shortcuts.ts +1 -1
- package/src/services/rail-snapshot.ts +1 -1
- package/src/services/request-cache-redis.ts +2 -1
- package/src/services/session/user.ts +1 -1
- package/src/services/settings/app.ts +5 -1
- package/src/services/settings/core-settings.ts +6 -8
- package/src/services/settings/index.ts +3 -3
- package/src/services/settings/store.ts +3 -3
- package/src/services/webauthn.ts +1 -1
- package/src/shared/index.ts +3 -6
- package/src/shared/markdown/extensions/info-blocks.ts +1 -1
- package/src/ssr/AdminSidebar.tsx +2 -2
- package/src/ssr/AppLaunchpad.island.tsx +12 -2
- package/src/ssr/AppLaunchpadPanel.tsx +4 -4
- package/src/ssr/GlobalSearchDialog.tsx +6 -6
- package/src/ssr/GlobalSearchTrigger.island.tsx +2 -2
- package/src/ssr/Layout.tsx +2 -2
- package/src/ssr/LayoutHeader.tsx +1 -1
- package/src/ssr/LayoutHelp.tsx +4 -6
- package/src/ssr/LayoutHelpTrigger.island.tsx +2 -2
- package/src/ssr/MinimalLayout.tsx +1 -1
- package/src/ssr/MobileNavigation.tsx +2 -2
- package/src/ssr/ProfilePreferences.island.tsx +1 -1
- package/src/ssr/admin-navigation.ts +6 -2
- package/src/ssr/islands/index.ts +2 -2
- package/src/ssr/mobile-menu-history.ts +1 -1
- package/src/workflows/ai/index.ts +2 -2
- package/src/workflows/ai/runtime.ts +16 -91
- package/src/workflows/ai/store.ts +1 -1
- package/src/workflows/ai/types.ts +6 -82
- package/src/workflows/store/index.ts +2 -2
- package/src/server/services/freeipa/test-certificates.ts +0 -44
- package/src/services/session/test-fixture.ts +0 -18
package/src/config/env.ts
CHANGED
|
@@ -1,57 +1,150 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Platform environment registry.
|
|
3
|
+
* Every variable a Cloud process reads is declared here or in an application's
|
|
4
|
+
* `src/env.ts`; values are parsed lazily on access (see `define-env.ts`).
|
|
4
5
|
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { defineEnv, envBoolean, envList } from "./define-env";
|
|
5
8
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
9
|
+
const syncReplicas = z
|
|
10
|
+
.enum(["1", "3", "5"], { error: "must be 1, 3, or 5" })
|
|
11
|
+
.transform((value): 1 | 3 | 5 => (value === "1" ? 1 : value === "3" ? 3 : 5));
|
|
12
|
+
|
|
13
|
+
const registry = defineEnv({
|
|
14
|
+
// Process
|
|
15
|
+
NODE_ENV: {
|
|
16
|
+
schema: z.string(),
|
|
17
|
+
doc: "Release images set `production`; `development` enables development-only behavior such as insecure cookies and HTTP issuers.",
|
|
18
|
+
},
|
|
19
|
+
HOSTNAME: {
|
|
20
|
+
schema: z.string(),
|
|
21
|
+
doc: "Container or host name; the default instance identity in worker leases and gateway registration.",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Infrastructure
|
|
25
|
+
DATABASE_URL: {
|
|
26
|
+
schema: z.string(),
|
|
27
|
+
example: "postgresql://ipa:ipa@localhost:5432/ipa",
|
|
28
|
+
doc: "Postgres connection read by Bun SQL and by application-owned pools.",
|
|
29
|
+
},
|
|
30
|
+
REDIS_URL: {
|
|
31
|
+
schema: z.string(),
|
|
32
|
+
aliases: ["VALKEY_URL"],
|
|
33
|
+
example: "redis://localhost:6379",
|
|
34
|
+
doc: "Valkey connection used by Bun Redis for caches and rate limits.",
|
|
35
|
+
},
|
|
36
|
+
NATS_SERVERS: {
|
|
37
|
+
schema: envList,
|
|
38
|
+
default: [],
|
|
39
|
+
example: "nats://localhost:4222",
|
|
40
|
+
doc: "Comma-separated NATS JetStream bootstrap servers; the client dials all of them.",
|
|
41
|
+
},
|
|
42
|
+
SYNC_NAMESPACE: {
|
|
43
|
+
schema: z.string(),
|
|
44
|
+
default: "",
|
|
45
|
+
example: "dev",
|
|
46
|
+
doc: "Sync namespace shared by every app of one installation; isolates its NATS resources from other installations on the same cluster.",
|
|
47
|
+
},
|
|
48
|
+
SYNC_REPLICAS: {
|
|
49
|
+
schema: syncReplicas,
|
|
50
|
+
default: 3,
|
|
51
|
+
example: "1",
|
|
52
|
+
doc: "Persistent JetStream replica count (1, 3 or 5); keep it consistent across the installation and migrate existing resources before changing it.",
|
|
53
|
+
},
|
|
54
|
+
NATS_CREDS_FILE: {
|
|
55
|
+
schema: z.string(),
|
|
56
|
+
doc: "Path to a mounted NATS `.creds` file (JWT and NKey); unset means server-side no-auth.",
|
|
57
|
+
},
|
|
58
|
+
NATS_TLS_CA_FILE: {
|
|
59
|
+
schema: z.string(),
|
|
60
|
+
doc: "Path to a mounted CA certificate that enables TLS to NATS; unset means plain TCP.",
|
|
61
|
+
},
|
|
62
|
+
NATS_IGNORE_CLUSTER_UPDATES: {
|
|
63
|
+
schema: envBoolean,
|
|
64
|
+
default: true,
|
|
65
|
+
example: "true",
|
|
66
|
+
doc: "Only dial the listed servers and ignore cluster-advertised peer addresses; set to `false` when every advertised address is reachable.",
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Installation
|
|
70
|
+
APP_URL: {
|
|
71
|
+
schema: z.string(),
|
|
72
|
+
example: "localhost:3000",
|
|
73
|
+
doc: "Public Cloud URL used to bootstrap the `app.url` setting; the stored setting wins once written.",
|
|
74
|
+
},
|
|
75
|
+
APP_SECRET: {
|
|
76
|
+
schema: z.string(),
|
|
77
|
+
default: "",
|
|
78
|
+
scope: "secret",
|
|
79
|
+
example: "change-me-generate-with-openssl-rand-hex-32",
|
|
80
|
+
doc: "Deployment-wide secret for at-rest settings encryption and signed cursors; generate once with `openssl rand -hex 32` and never change it after data has been written.",
|
|
81
|
+
},
|
|
82
|
+
ADMIN_LOGIN_TOKEN: {
|
|
83
|
+
schema: z.string(),
|
|
84
|
+
default: "",
|
|
85
|
+
scope: "secret",
|
|
86
|
+
doc: "Core only: temporary first-administrator or recovery login token; remove it after normal administrator sign-in works.",
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// Identity and OAuth
|
|
90
|
+
CLOUD_IDENTITY_KEY_ENCRYPTION_KEY: {
|
|
91
|
+
schema: z.string(),
|
|
92
|
+
scope: "secret",
|
|
93
|
+
example: "change-me-generate-with-openssl-rand-hex-32",
|
|
94
|
+
doc: "Core only: 64-hex KEK for platform identity signing keys stored in Postgres; generate once with `openssl rand -hex 32` and never distribute it to other apps.",
|
|
95
|
+
},
|
|
96
|
+
CLOUD_IDENTITY_NEXT_KEY: {
|
|
97
|
+
schema: z.string(),
|
|
98
|
+
scope: "secret",
|
|
99
|
+
doc: "Core only: pre-distributed next KEK before promoting it to current; cannot be combined with `CLOUD_IDENTITY_PREVIOUS_KEY`.",
|
|
100
|
+
},
|
|
101
|
+
CLOUD_IDENTITY_PREVIOUS_KEY: {
|
|
102
|
+
schema: z.string(),
|
|
103
|
+
scope: "secret",
|
|
104
|
+
doc: "Core only: old KEK set during an explicit rolling rewrap of stored identity signing keys, then removed.",
|
|
105
|
+
},
|
|
106
|
+
CLOUD_OAUTH_BROKER_SECRET: {
|
|
107
|
+
schema: z.string(),
|
|
108
|
+
scope: "secret",
|
|
109
|
+
doc: "Core and OAuth only: 64-hex secret that lets the OAuth app request token signatures from Core; generate independently with `openssl rand -hex 32`.",
|
|
110
|
+
},
|
|
111
|
+
CLOUD_CORE_INTERNAL_ORIGIN: {
|
|
112
|
+
schema: z.string(),
|
|
113
|
+
doc: "Direct Core listener origin for OAuth issuance, mandate-backed background work and internal callbacks; host-run callers use the Core port, not the gateway.",
|
|
114
|
+
},
|
|
115
|
+
CLOUD_APP_CREDENTIAL: {
|
|
116
|
+
schema: z.string(),
|
|
117
|
+
scope: "secret",
|
|
118
|
+
doc: "App-bound workload credential provisioned through Core's admin workload API; background apps such as Mail need one with `identity:invoke`.",
|
|
119
|
+
},
|
|
120
|
+
CLOUD_IDENTITY_JWKS_ORIGIN: {
|
|
121
|
+
schema: z.string(),
|
|
122
|
+
doc: "Optional private origin of the Core listener for session and invocation JWKS; unset fetches keys from the public issuer origin.",
|
|
123
|
+
},
|
|
124
|
+
CLOUD_OAUTH_JWKS_ORIGIN: {
|
|
125
|
+
schema: z.string(),
|
|
126
|
+
doc: "Optional private origin of the OAuth listener for OAuth JWKS; unset fetches keys from the public issuer origin.",
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Build and standalone development
|
|
130
|
+
APP_DIR: {
|
|
131
|
+
schema: z.string(),
|
|
132
|
+
scope: "development",
|
|
133
|
+
doc: "Application directory for standalone consumers running `@k2b/cloud/scripts/build.ts` or the SSR dev server outside the monorepo; defaults to the package directory.",
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// Desktop
|
|
137
|
+
CLOUD_DESKTOP_SQLITE_PATH: {
|
|
138
|
+
schema: z.string(),
|
|
139
|
+
scope: "development",
|
|
140
|
+
doc: "SQLite file used by `desktop.sql` in the Bun desktop process; defaults to `.local/desktop.sqlite` in the working directory.",
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
type PlatformEnv = typeof registry & { readonly IS_DEVELOPMENT: boolean };
|
|
145
|
+
|
|
146
|
+
/** Platform environment; `IS_DEVELOPMENT` derives from `NODE_ENV === "development"`. */
|
|
147
|
+
export const env: PlatformEnv = Object.defineProperty(registry as PlatformEnv, "IS_DEVELOPMENT", {
|
|
148
|
+
enumerable: true,
|
|
149
|
+
get: () => registry.NODE_ENV === "development",
|
|
150
|
+
});
|
package/src/config/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main config export.
|
|
3
|
-
* Re-exports
|
|
3
|
+
* Re-exports the platform environment registry and the registry factory used
|
|
4
|
+
* by application `src/env.ts` files. SSR config is per-app via defineApp().
|
|
4
5
|
*/
|
|
5
6
|
|
|
7
|
+
export { defineEnv, type EnvRegistry, type EnvScope, type EnvSpec, type EnvSpecs, envBoolean, envList, envString } from "./define-env";
|
|
6
8
|
export { env } from "./env";
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type CapabilityStream,
|
|
3
|
+
type CapabilityStreamDefinition,
|
|
4
|
+
CapabilityStreamPolicySchema,
|
|
5
|
+
CapabilityStreamSchema,
|
|
6
|
+
} from "./capability-streams";
|
|
7
|
+
|
|
2
8
|
export * from "./capability-streams";
|
|
9
|
+
|
|
3
10
|
import { z } from "zod";
|
|
4
11
|
import { CommandPathSchema } from "./commands";
|
|
5
12
|
import type { AccessSubject, RequestActor, User } from "./shared";
|
package/src/contracts/index.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
export * from "./account-categories";
|
|
2
2
|
export * from "./announcements";
|
|
3
3
|
export * from "./app";
|
|
4
|
+
export * from "./app-approval";
|
|
4
5
|
export * from "./capabilities";
|
|
6
|
+
export * from "./commands";
|
|
5
7
|
export * from "./notification-types";
|
|
6
8
|
export * from "./posix";
|
|
7
9
|
export * from "./profile";
|
|
10
|
+
// Core app bar administration and shared shortcut values.
|
|
11
|
+
export { RailAccessSchema, type RailAdminEntry, RailAdminInputSchema, RailAdminSchema, type RailAdminState } from "./rail-admin";
|
|
12
|
+
export type { RailShortcut } from "./rail-preferences";
|
|
8
13
|
export * from "./registry";
|
|
9
14
|
export * from "./resource-clipboard";
|
|
10
15
|
export * from "./shared";
|
|
11
16
|
export * from "./user-notifications";
|
|
12
17
|
export * from "./widgets";
|
|
13
|
-
export * from "./app-approval";
|
|
14
|
-
|
|
15
|
-
// Core app bar administration and shared shortcut values.
|
|
16
|
-
export { RailAccessSchema, RailAdminInputSchema, RailAdminSchema, type RailAdminEntry, type RailAdminState } from "./rail-admin";
|
|
17
|
-
export type { RailShortcut } from "./rail-preferences";
|
|
18
|
-
|
|
19
|
-
export * from "./commands";
|
package/src/contracts/shared.ts
CHANGED
|
@@ -289,9 +289,18 @@ export const AccountActivityListResponseSchema = z.object({
|
|
|
289
289
|
export type AccountActivityListResponse = z.infer<typeof AccountActivityListResponseSchema>;
|
|
290
290
|
|
|
291
291
|
export const AuthenticatedPrincipalSchema = z.discriminatedUnion("type", [
|
|
292
|
-
z.object({
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
z.object({
|
|
293
|
+
type: z.literal("user").describe("One user account."),
|
|
294
|
+
userId: z.uuid().describe("Exact user ID returned by the Accounts directory."),
|
|
295
|
+
}),
|
|
296
|
+
z.object({
|
|
297
|
+
type: z.literal("group").describe("Members of one group."),
|
|
298
|
+
groupId: z.uuid().describe("Exact group ID returned by the Accounts directory."),
|
|
299
|
+
}),
|
|
300
|
+
z.object({
|
|
301
|
+
type: z.literal("service_account").describe("One service account."),
|
|
302
|
+
serviceAccountId: z.uuid().describe("Exact service account ID returned by the Accounts directory."),
|
|
303
|
+
}),
|
|
295
304
|
z.object({ type: z.literal("authenticated").describe("Every authenticated identity; no directory ID is needed.") }),
|
|
296
305
|
]);
|
|
297
306
|
|
package/src/desktop/index.ts
CHANGED
|
@@ -311,6 +311,7 @@ const getSql = (): DesktopSql => {
|
|
|
311
311
|
const { Database } = requireFn("bun:sqlite") as { Database: new (path: string) => any };
|
|
312
312
|
const { dirname, resolve } = requireFn("node:path") as typeof import("node:path");
|
|
313
313
|
const { mkdirSync } = requireFn("node:fs") as typeof import("node:fs");
|
|
314
|
+
// Declared in the platform registry; this browser-safe module must not import zod-backed config.
|
|
314
315
|
const dbPath = process.env.CLOUD_DESKTOP_SQLITE_PATH ?? resolve(process.cwd(), ".local", "desktop.sqlite");
|
|
315
316
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
316
317
|
const db = new Database(dbPath);
|
package/src/desktop/solid.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { deserialize as deserializeProps, serialize as serializeProps } from "seroval";
|
|
2
2
|
import type { Component, JSX, ParentProps } from "solid-js";
|
|
3
|
-
import { children, createContext,
|
|
3
|
+
import { children, createContext, createMemo, createSignal, For, onCleanup, onMount, Show, useContext } from "solid-js";
|
|
4
4
|
import { Dynamic, Portal } from "solid-js/web";
|
|
5
5
|
import {
|
|
6
6
|
type DesktopEnvironment,
|
package/src/index.ts
CHANGED
|
@@ -10,13 +10,19 @@ export {
|
|
|
10
10
|
unbindProcessSync,
|
|
11
11
|
} from "./_internal/process-sync";
|
|
12
12
|
export { APP_READINESS_PATH } from "./_internal/readiness";
|
|
13
|
-
export type {
|
|
13
|
+
export type {
|
|
14
|
+
AppRegistryDetail,
|
|
15
|
+
AppRegistryIssue,
|
|
16
|
+
AppRegistrySnapshot,
|
|
17
|
+
CapabilityRegistryRecord,
|
|
18
|
+
DashboardWidget,
|
|
19
|
+
} from "./_internal/registry";
|
|
14
20
|
export {
|
|
15
21
|
APP_REGISTRY_CONFIG,
|
|
16
22
|
APP_REGISTRY_PREFIX,
|
|
17
23
|
APP_REGISTRY_TTL_MS,
|
|
18
|
-
CAPABILITY_REGISTRY_CONFIG,
|
|
19
24
|
appRegistry,
|
|
25
|
+
CAPABILITY_REGISTRY_CONFIG,
|
|
20
26
|
capabilityRegistry,
|
|
21
27
|
getApp,
|
|
22
28
|
listApps,
|
package/src/server/index.ts
CHANGED
|
@@ -8,8 +8,8 @@ export { defineHelp, defineHelpCollection, type HelpCollection, type HelpDefinit
|
|
|
8
8
|
export { DEFAULT_LOCALE, getLocale, LOCALE_COOKIE, LOCALE_HEADER, locale, preferredLocale, resolveLocale } from "./locale";
|
|
9
9
|
export type {
|
|
10
10
|
AuthContext,
|
|
11
|
-
InvocationProvenance,
|
|
12
11
|
InvocationExpectation,
|
|
12
|
+
InvocationProvenance,
|
|
13
13
|
RateLimitConfig,
|
|
14
14
|
RateLimitRouteOverride,
|
|
15
15
|
RequestActor,
|
|
@@ -26,8 +26,8 @@ export {
|
|
|
26
26
|
openApiMeta,
|
|
27
27
|
rateLimit,
|
|
28
28
|
rejectReservedWorkloadCredential,
|
|
29
|
-
requireInvocation,
|
|
30
29
|
requestLogger,
|
|
30
|
+
requireInvocation,
|
|
31
31
|
requiresAdmin,
|
|
32
32
|
requiresAuth,
|
|
33
33
|
requiresIpa,
|
package/src/server/ratelimit.ts
CHANGED
|
@@ -86,7 +86,7 @@ export class RateLimitError extends Error {
|
|
|
86
86
|
// ==========================
|
|
87
87
|
|
|
88
88
|
export const ratelimit = (config: RateLimiterConfig): RateLimiter => {
|
|
89
|
-
// windowSecs: 0 — e.g. Number(
|
|
89
|
+
// windowSecs: 0 — e.g. `Number()` of an unset variable — made
|
|
90
90
|
// both window keys Infinity and collapse to one, elapsedRatio NaN, and
|
|
91
91
|
// `EXPIRE key 0` delete the counter on every first increment, so the
|
|
92
92
|
// weighted count never exceeded the limit and the limiter allowed
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import type {
|
|
2
|
+
import type { PermissionLevel, Principal } from "./access";
|
|
3
3
|
|
|
4
4
|
/** Compare the grants that were reviewed with those held under a resource lock. */
|
|
5
5
|
export function accessRevision(entries: ReadonlyArray<{ id: string; principal: Principal; permission: PermissionLevel }>): string {
|
|
6
|
-
const state = entries
|
|
7
|
-
Object.entries(entry.principal).sort(([left], [right]) => left.localeCompare(right))])
|
|
6
|
+
const state = entries
|
|
7
|
+
.map((entry) => [entry.id, entry.permission, Object.entries(entry.principal).sort(([left], [right]) => left.localeCompare(right))])
|
|
8
8
|
.sort((left, right) => String(left[0]).localeCompare(String(right[0])));
|
|
9
9
|
return createHash("sha256").update(JSON.stringify(state)).digest("hex");
|
|
10
10
|
}
|
|
@@ -568,7 +568,9 @@ export const listUsersWithAccess = async (params: {
|
|
|
568
568
|
* Resolve display names for access entries.
|
|
569
569
|
* Populates the displayName field based on principal type.
|
|
570
570
|
*/
|
|
571
|
-
export const resolveDisplayNames = async <T extends { principal: Principal }>(
|
|
571
|
+
export const resolveDisplayNames = async <T extends { principal: Principal }>(
|
|
572
|
+
entries: T[],
|
|
573
|
+
): Promise<(T & { displayName: string; avatarHash?: string | null })[]> => {
|
|
572
574
|
const userIds = entries.filter((e) => e.principal.type === "user").map((e) => (e.principal as { type: "user"; userId: string }).userId);
|
|
573
575
|
|
|
574
576
|
const groupIds = entries
|
|
@@ -3,7 +3,12 @@ import type { TypedNotificationSendResult } from "../notifications/platform";
|
|
|
3
3
|
export type AccountNotificationDeliveryResult = Pick<TypedNotificationSendResult, "id" | "status">;
|
|
4
4
|
|
|
5
5
|
export type AccountsNotificationSender = {
|
|
6
|
-
sendLoginLink: (input: {
|
|
6
|
+
sendLoginLink: (input: {
|
|
7
|
+
email: string;
|
|
8
|
+
token: string;
|
|
9
|
+
magicLink: string;
|
|
10
|
+
locale?: string;
|
|
11
|
+
}) => Promise<AccountNotificationDeliveryResult>;
|
|
7
12
|
sendFreeIpaWelcome: (input: {
|
|
8
13
|
userId: string;
|
|
9
14
|
uid: string;
|
|
@@ -11,7 +16,12 @@ export type AccountsNotificationSender = {
|
|
|
11
16
|
accountExpires: string | null;
|
|
12
17
|
locale?: string;
|
|
13
18
|
}) => Promise<AccountNotificationDeliveryResult>;
|
|
14
|
-
sendLocalWelcome: (input: {
|
|
19
|
+
sendLocalWelcome: (input: {
|
|
20
|
+
userId: string;
|
|
21
|
+
email: string;
|
|
22
|
+
accountExpires: string | null;
|
|
23
|
+
locale?: string;
|
|
24
|
+
}) => Promise<AccountNotificationDeliveryResult>;
|
|
15
25
|
sendRequestDenied: (input: {
|
|
16
26
|
requestId: string;
|
|
17
27
|
userId: string;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { requestCacheRedis } from "../request-cache-redis";
|
|
2
1
|
import { err, fail, ok, type Result } from "@k2b/stdlib";
|
|
3
2
|
import { sql } from "bun";
|
|
4
3
|
import { HTTPException } from "hono/http-exception";
|
|
5
|
-
import { hasRole, type User } from "../../contracts/shared";
|
|
6
4
|
import { z } from "zod";
|
|
7
|
-
import { AnnouncementDisplayEntrySchema } from "../../contracts/announcements";
|
|
8
|
-
import { claimCacheFill, completeCacheFill } from "../cache-fill";
|
|
9
5
|
import type {
|
|
10
6
|
AnnouncementCookieState,
|
|
11
7
|
AnnouncementDisplayEntry,
|
|
@@ -13,8 +9,12 @@ import type {
|
|
|
13
9
|
CreateAnnouncement,
|
|
14
10
|
UpdateAnnouncement,
|
|
15
11
|
} from "../../contracts/announcements";
|
|
12
|
+
import { AnnouncementDisplayEntrySchema } from "../../contracts/announcements";
|
|
13
|
+
import { hasRole, type User } from "../../contracts/shared";
|
|
16
14
|
import { markdown } from "../../shared/markdown";
|
|
15
|
+
import { claimCacheFill, completeCacheFill } from "../cache-fill";
|
|
17
16
|
import { logger } from "../logging";
|
|
17
|
+
import { requestCacheRedis } from "../request-cache-redis";
|
|
18
18
|
|
|
19
19
|
const log = logger("announcements");
|
|
20
20
|
const ACTIVE_CACHE_KEY = "appglobalcache:shared:announcements:v1";
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomInt, timingSafeEqual } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { type SQL, sql } from "bun";
|
|
3
|
+
import { env } from "../config/env";
|
|
4
|
+
import { type AccountCategory, accountCategory } from "../contracts/account-categories";
|
|
3
5
|
import {
|
|
4
|
-
APP_APPROVAL_LIMITS as limits,
|
|
5
6
|
APP_APPROVAL_PATH,
|
|
6
7
|
APP_APPROVAL_PROTOCOL,
|
|
7
|
-
AppDevicePublicKeySchema,
|
|
8
|
-
appDeviceProofMessage,
|
|
9
|
-
appPairingProofMessage,
|
|
10
8
|
type AppDevicePublicKey,
|
|
9
|
+
AppDevicePublicKeySchema,
|
|
11
10
|
type AppDeviceRequest,
|
|
12
11
|
type AppDeviceView,
|
|
13
12
|
type AppPendingLogin,
|
|
13
|
+
appDeviceProofMessage,
|
|
14
|
+
appPairingProofMessage,
|
|
15
|
+
APP_APPROVAL_LIMITS as limits,
|
|
14
16
|
} from "../contracts/app-approval";
|
|
15
|
-
import {
|
|
17
|
+
import { publicCloudOrigin } from "../shared/app-url";
|
|
16
18
|
import { isAccountCategoryAllowed } from "./account-category-policy";
|
|
17
19
|
import { audit } from "./audit";
|
|
18
|
-
import { decryptValue } from "./settings/crypto";
|
|
19
|
-
import { publicCloudOrigin } from "../shared/app-url";
|
|
20
20
|
import { CORE_SETTINGS } from "./settings/core-settings";
|
|
21
|
+
import { decryptValue } from "./settings/crypto";
|
|
21
22
|
|
|
22
23
|
export class AppApprovalError extends Error {
|
|
23
24
|
constructor(
|
|
@@ -47,13 +48,13 @@ export const readAppApprovalConfig = async (db: SQL = sql, requireAppOrigin = tr
|
|
|
47
48
|
const adminPairing = values.get("user.app_approval.admin_pairing") ?? false;
|
|
48
49
|
if (typeof enabled !== "boolean" || typeof adminPairing !== "boolean") return reject("UNAVAILABLE", 503);
|
|
49
50
|
// The issuer is the operator's canonical URL, never the request Host header.
|
|
50
|
-
const rawIssuer = values.get("app.url") ??
|
|
51
|
+
const rawIssuer = values.get("app.url") ?? env.APP_URL ?? "localhost:3000";
|
|
51
52
|
const rawOrigin = values.get("user.app_approval.origin") ?? CORE_SETTINGS["user.app_approval.origin"].default;
|
|
52
53
|
const origin = (value: unknown) => {
|
|
53
54
|
if (typeof value !== "string") return reject("UNAVAILABLE", 503);
|
|
54
55
|
try {
|
|
55
56
|
const url = new URL(value);
|
|
56
|
-
const development =
|
|
57
|
+
const development = env.NODE_ENV !== "production" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
57
58
|
if (
|
|
58
59
|
(url.protocol !== "https:" && !(url.protocol === "http:" && development)) ||
|
|
59
60
|
url.username ||
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { crypto } from "@k2b/stdlib";
|
|
2
2
|
import { getProcessApplicationId } from "../../_internal/process-identity";
|
|
3
|
+
import { env } from "../../config/env";
|
|
3
4
|
|
|
4
5
|
const KEY_PATTERN = /^[0-9a-f]{64}$/i;
|
|
5
6
|
|
|
@@ -27,9 +28,9 @@ export const readIdentityKeyEncryptionConfig = async (): Promise<IdentityKeyEncr
|
|
|
27
28
|
if (getProcessApplicationId() !== "core") {
|
|
28
29
|
throw new Error("Private Cloud identity keys may only be loaded by the Core application");
|
|
29
30
|
}
|
|
30
|
-
const current = parseKey("CLOUD_IDENTITY_KEY_ENCRYPTION_KEY",
|
|
31
|
-
const previous = parseKey("CLOUD_IDENTITY_PREVIOUS_KEY",
|
|
32
|
-
const next = parseKey("CLOUD_IDENTITY_NEXT_KEY",
|
|
31
|
+
const current = parseKey("CLOUD_IDENTITY_KEY_ENCRYPTION_KEY", env.CLOUD_IDENTITY_KEY_ENCRYPTION_KEY, true)!;
|
|
32
|
+
const previous = parseKey("CLOUD_IDENTITY_PREVIOUS_KEY", env.CLOUD_IDENTITY_PREVIOUS_KEY, false);
|
|
33
|
+
const next = parseKey("CLOUD_IDENTITY_NEXT_KEY", env.CLOUD_IDENTITY_NEXT_KEY, false);
|
|
33
34
|
const currentIdentified = await identify(current);
|
|
34
35
|
const previousIdentified = previous ? await identify(previous) : null;
|
|
35
36
|
const nextIdentified = next ? await identify(next) : null;
|
|
@@ -37,6 +38,7 @@ export const readIdentityKeyEncryptionConfig = async (): Promise<IdentityKeyEncr
|
|
|
37
38
|
if (new Set(ids).size !== ids.length) {
|
|
38
39
|
throw new Error("Configured Cloud identity KEKs must be distinct");
|
|
39
40
|
}
|
|
40
|
-
if (previousIdentified && nextIdentified)
|
|
41
|
+
if (previousIdentified && nextIdentified)
|
|
42
|
+
throw new Error("Configure either CLOUD_IDENTITY_PREVIOUS_KEY or CLOUD_IDENTITY_NEXT_KEY, not both");
|
|
41
43
|
return { current: currentIdentified, previous: previousIdentified, next: nextIdentified };
|
|
42
44
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { env } from "../../config/env";
|
|
1
2
|
import { publicCloudOrigin } from "../../shared/app-url";
|
|
2
3
|
import { get as getSetting } from "../settings";
|
|
3
4
|
import { CLOUD_INVOCATION_JWKS_PATH, CLOUD_OAUTH_JWKS_PATH, CLOUD_SESSION_JWKS_PATH } from "./constants";
|
|
@@ -28,7 +29,7 @@ const normalizeGroups = (value: unknown): string[] => {
|
|
|
28
29
|
return groups.length > 0 ? groups : ["admins"];
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
const resolveCoreJwksUrl = (issuer: string, path: string, transportOrigin =
|
|
32
|
+
const resolveCoreJwksUrl = (issuer: string, path: string, transportOrigin = env.CLOUD_IDENTITY_JWKS_ORIGIN): URL => {
|
|
32
33
|
if (!transportOrigin) return new URL(path, issuer);
|
|
33
34
|
const origin = new URL(transportOrigin);
|
|
34
35
|
if (origin.protocol !== "http:" && origin.protocol !== "https:") {
|
|
@@ -37,13 +38,13 @@ const resolveCoreJwksUrl = (issuer: string, path: string, transportOrigin = proc
|
|
|
37
38
|
return new URL(path, origin);
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
export const resolveSessionJwksUrl = (issuer: string, transportOrigin =
|
|
41
|
+
export const resolveSessionJwksUrl = (issuer: string, transportOrigin = env.CLOUD_IDENTITY_JWKS_ORIGIN): URL =>
|
|
41
42
|
resolveCoreJwksUrl(issuer, CLOUD_SESSION_JWKS_PATH, transportOrigin);
|
|
42
43
|
|
|
43
|
-
export const resolveInvocationJwksUrl = (issuer: string, transportOrigin =
|
|
44
|
+
export const resolveInvocationJwksUrl = (issuer: string, transportOrigin = env.CLOUD_IDENTITY_JWKS_ORIGIN): URL =>
|
|
44
45
|
resolveCoreJwksUrl(issuer, CLOUD_INVOCATION_JWKS_PATH, transportOrigin);
|
|
45
46
|
|
|
46
|
-
export const resolveOAuthJwksUrl = (issuer: string, transportOrigin =
|
|
47
|
+
export const resolveOAuthJwksUrl = (issuer: string, transportOrigin = env.CLOUD_OAUTH_JWKS_ORIGIN): URL => {
|
|
47
48
|
if (!transportOrigin) return new URL(CLOUD_OAUTH_JWKS_PATH, issuer);
|
|
48
49
|
|
|
49
50
|
const url = new URL(CLOUD_OAUTH_JWKS_PATH, transportOrigin);
|
|
@@ -56,7 +57,7 @@ export const resolveOAuthJwksUrl = (issuer: string, transportOrigin = process.en
|
|
|
56
57
|
const load = async (): Promise<IdentityRuntimeConfig> => {
|
|
57
58
|
const [appUrl, rawGroupsAdmin] = await Promise.all([getSetting<string>("app.url"), getSetting<unknown>("freeipa.groups.admin")]);
|
|
58
59
|
const issuer = publicCloudOrigin(appUrl);
|
|
59
|
-
if (
|
|
60
|
+
if (env.NODE_ENV === "production" && new URL(issuer).protocol !== "https:") {
|
|
60
61
|
throw new Error("Cloud identity issuer must use HTTPS in production");
|
|
61
62
|
}
|
|
62
63
|
return {
|
package/src/services/ipa/sync.ts
CHANGED
|
@@ -6,9 +6,9 @@ import { applyIpaAccountTransitionPolicy } from "../accounts/switching";
|
|
|
6
6
|
import { getFreeIpaConfig } from "../freeipa-config";
|
|
7
7
|
import { logger } from "../logging";
|
|
8
8
|
import { session } from "../session";
|
|
9
|
-
import { mirrorIpaPosix } from "./posix";
|
|
10
9
|
import * as settings from "../settings";
|
|
11
10
|
import { buildEffectiveIpaGroupsByUid } from "./effective-groups";
|
|
11
|
+
import { mirrorIpaPosix } from "./posix";
|
|
12
12
|
import { calculateIpaProfileFromEffectiveProjection, getEffectiveUserGroups } from "./profile";
|
|
13
13
|
import { createIpaRuntimeCheckpoint, type IpaSyncRuntime } from "./runtime";
|
|
14
14
|
import { assessIpaDestructionGuard, formatIpaDestructionGuardFailure, readCompleteIpaList, selectStaleLocalIpaRows } from "./sync-planning";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { readWebVitals } from "./web-vitals";
|
|
2
1
|
import { sql } from "bun";
|
|
3
2
|
import type { PaginationParams } from "../../contracts/shared";
|
|
4
3
|
import { escapeLikePattern, parsePgJsonRecord, toPgTextArray } from "../postgres";
|
|
5
4
|
import { registerSettings } from "../settings/defaults";
|
|
6
5
|
import { redactMetadata } from "./redaction";
|
|
7
6
|
import { observeSyncEvent, TRACE_STUCK_AFTER_MS, trace } from "./trace";
|
|
7
|
+
import { readWebVitals } from "./web-vitals";
|
|
8
8
|
|
|
9
9
|
export type {
|
|
10
10
|
TraceAttributes,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getProcessApplicationId } from "../../_internal/process-identity";
|
|
2
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
2
|
import type { SyncEvent } from "@k2b/sync";
|
|
4
3
|
import { sql } from "bun";
|
|
4
|
+
import { getProcessApplicationId } from "../../_internal/process-identity";
|
|
5
5
|
import type { PaginationParams } from "../../contracts/shared";
|
|
6
6
|
import { escapeLikePattern, parsePgJsonRecord, toPgTextArray } from "../postgres";
|
|
7
7
|
import { isSensitiveMetadataKey, REDACTED, redactMetadata } from "./redaction";
|
|
@@ -9,9 +9,9 @@ import { parsePgJsonValue } from "../postgres";
|
|
|
9
9
|
import { isMandatePolicyNarrowing, type MandatePolicyV1, MandatePolicyV1Schema, mandatePolicyAllows, parseMandatePolicy } from "./policy";
|
|
10
10
|
|
|
11
11
|
export {
|
|
12
|
-
CapabilityGrantsSchema,
|
|
13
|
-
CapabilityGrantSchema,
|
|
14
12
|
type CapabilityGrant,
|
|
13
|
+
CapabilityGrantSchema,
|
|
14
|
+
CapabilityGrantsSchema,
|
|
15
15
|
capabilityGrantAllows,
|
|
16
16
|
isMandatePolicyNarrowing,
|
|
17
17
|
MANDATE_POLICY_MAX_IDENTIFIERS,
|
|
@@ -20,6 +20,9 @@ export {
|
|
|
20
20
|
MandatePolicyV1Schema,
|
|
21
21
|
mandatePolicyAllows,
|
|
22
22
|
parseMandatePolicy,
|
|
23
|
+
type TaskGrant,
|
|
24
|
+
TaskGrantSchema,
|
|
25
|
+
TaskGrantsSchema,
|
|
23
26
|
} from "./policy";
|
|
24
27
|
|
|
25
28
|
export type MandateState = "active" | "paused" | "revoked";
|