@treeseed/sdk 0.13.0-rc.102 → 0.13.0-rc.104
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/dist/.treeseed-build-complete.json +1 -1
- package/dist/deployment/index.d.ts +1 -0
- package/dist/deployment/index.js +1 -0
- package/dist/deployment/postgres/contracts.js +5 -1
- package/dist/deployment/postgres/requirements.d.ts +14 -0
- package/dist/deployment/postgres/requirements.js +30 -0
- package/dist/deployment/schemas.d.ts +1173 -4
- package/dist/deployment/schemas.js +32 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AI_MODE_INTERNAL_PATH } from "./ai-mode.js";
|
|
3
|
-
import { postgresComponentRequirementSchema } from "./postgres/contracts.js";
|
|
3
|
+
import { postgresComponentRequirementSchema, postgresTopologySchema } from "./postgres/contracts.js";
|
|
4
4
|
const identifier = z.string().regex(/^[a-z][a-z0-9.-]{1,63}$/u);
|
|
5
5
|
const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/u);
|
|
6
6
|
const gitCommit = z.string().regex(/^[a-f0-9]{40}$/u);
|
|
@@ -121,6 +121,7 @@ const hostConfigurationSchema = z.object({
|
|
|
121
121
|
development: z.object({ pollSeconds: z.literal(60) }).strict()
|
|
122
122
|
}).strict(),
|
|
123
123
|
components: z.record(identifier, hostComponentSchema),
|
|
124
|
+
postgres: postgresTopologySchema.optional(),
|
|
124
125
|
network: z.object({
|
|
125
126
|
manager: z.object({ binding, aliases: z.array(localAlias).default([]), sans: z.array(z.string().min(1)).default([]), trustedLanCidrs: z.array(z.string().min(1)).default([]) }).strict()
|
|
126
127
|
}).strict(),
|
|
@@ -149,7 +150,21 @@ const hostConfigurationSchema = z.object({
|
|
|
149
150
|
providerVolume: z.object({ encryption: z.literal("luks2"), backingPath: z.string().startsWith("/"), mountPath: z.string().startsWith("/"), sizeBytes: z.number().int().min(1073741824), unlock: z.enum(["tpm2", "systemd-credential"]), recoveryRequired: z.literal(true) }).strict(),
|
|
150
151
|
applicationEncryption: z.object({ provider: z.literal("systemd-credential"), activeKeyVersion: z.number().int().positive(), diagnosticsKeyVersion: z.number().int().positive() }).strict()
|
|
151
152
|
}).strict().optional()
|
|
152
|
-
}).strict()
|
|
153
|
+
}).strict().superRefine((host, context) => {
|
|
154
|
+
if (!host.postgres) return;
|
|
155
|
+
const fail = (message) => context.addIssue({ code: z.ZodIssueCode.custom, path: ["postgres"], message });
|
|
156
|
+
for (const requirement of host.postgres.requirements) {
|
|
157
|
+
if (!host.components[requirement.componentId] || host.components[requirement.componentId].enabled !== requirement.enabled) fail("Database requirements must match configured component activation.");
|
|
158
|
+
}
|
|
159
|
+
for (const allocation of host.postgres.allocations) {
|
|
160
|
+
if (!host.postgres.requirements.find((item) => item.id === allocation.requirementId)?.enabled) continue;
|
|
161
|
+
for (const reference of [allocation.migrationCredentialReference, allocation.runtimeCredentialReference]) {
|
|
162
|
+
if (host.secrets[reference]?.provider !== "systemd-credential" || host.secrets[reference]?.reference !== `/etc/treeseed/credentials/${reference}.cred`) fail("Database credentials require explicit fixed OS custody bindings.");
|
|
163
|
+
}
|
|
164
|
+
const server = host.postgres.servers.find((item) => item.id === allocation.serverId);
|
|
165
|
+
if (server.mode !== "external" && !host.components.postgres?.enabled) fail("Managed database allocations require the PostgreSQL component.");
|
|
166
|
+
}
|
|
167
|
+
});
|
|
153
168
|
const packageEndpointSchema = z.object({
|
|
154
169
|
id: identifier,
|
|
155
170
|
protocol: z.enum(["http", "https", "tcp"]),
|
|
@@ -205,6 +220,12 @@ const packageRuntimeSchema = z.object({
|
|
|
205
220
|
configuration: componentRuntimeConfigurationSchema,
|
|
206
221
|
// Optional without a default: parsing published artifacts must not change their digest.
|
|
207
222
|
postgresRequirements: z.array(postgresComponentRequirementSchema).max(32).optional(),
|
|
223
|
+
postgresLifecycle: z.array(z.object({
|
|
224
|
+
requirementId: identifier,
|
|
225
|
+
credentialOwner: z.object({ uid: z.number().int().min(0).max(65535), gid: z.number().int().min(0).max(65535) }).strict(),
|
|
226
|
+
migration: z.object({ composeService: identifier, completion: z.enum(["exit-zero", "healthy-stop"]), timeoutSeconds: z.number().int().min(1).max(3600) }).strict(),
|
|
227
|
+
runtimeServices: z.array(identifier).min(1).max(128)
|
|
228
|
+
}).strict()).max(32).optional(),
|
|
208
229
|
services: z.array(z.object({ id: identifier, composeService: identifier, endpoints: z.array(packageEndpointSchema) }).strict()).min(1),
|
|
209
230
|
stateVolumes: z.array(z.object({ id: identifier, volume: z.string().min(1), backup: z.enum(["required", "optional", "none"]) }).strict()),
|
|
210
231
|
migrations: z.array(z.object({ id: identifier, order: z.number().int().nonnegative(), backupRequired: z.boolean() }).strict()),
|
|
@@ -236,6 +257,15 @@ const packageRuntimeSchema = z.object({
|
|
|
236
257
|
}).strict().superRefine((runtime, context) => {
|
|
237
258
|
const requirements = runtime.postgresRequirements ?? [];
|
|
238
259
|
if (new Set(requirements.map(({ id }) => id)).size !== requirements.length) context.addIssue({ code: z.ZodIssueCode.custom, path: ["postgresRequirements"], message: "PostgreSQL requirement identities must be unique within a component." });
|
|
260
|
+
const lifecycles = runtime.postgresLifecycle ?? [];
|
|
261
|
+
const lifecycleFailure = () => context.addIssue({ code: z.ZodIssueCode.custom, path: ["postgresLifecycle"], message: "Each database requirement needs a distinct declared migration service and explicit runtime services." });
|
|
262
|
+
if (lifecycles.length !== requirements.length || new Set(lifecycles.map((item) => item.requirementId)).size !== lifecycles.length) lifecycleFailure();
|
|
263
|
+
const composeServices = new Set(runtime.services.map((item) => item.composeService));
|
|
264
|
+
const migrationServices = new Set(lifecycles.map((item) => item.migration.composeService));
|
|
265
|
+
if (migrationServices.size !== lifecycles.length) lifecycleFailure();
|
|
266
|
+
for (const item of lifecycles) {
|
|
267
|
+
if (!requirements.some((requirement) => requirement.id === item.requirementId) || !composeServices.has(item.migration.composeService) || new Set(item.runtimeServices).size !== item.runtimeServices.length || item.runtimeServices.some((service) => !composeServices.has(service) || migrationServices.has(service))) lifecycleFailure();
|
|
268
|
+
}
|
|
239
269
|
const endpointIds = runtime.services.flatMap((service) => service.endpoints.map((endpoint) => `${service.id}.${endpoint.id}`));
|
|
240
270
|
if (new Set(endpointIds).size !== endpointIds.length) context.addIssue({ code: z.ZodIssueCode.custom, path: ["services"], message: "Endpoint identities must be unique within a component." });
|
|
241
271
|
for (const file of runtime.configuration.files) if (!file.path.startsWith(`/etc/treeseed/components/${runtime.componentId}/`)) context.addIssue({ code: z.ZodIssueCode.custom, path: ["configuration", "files"], message: `Configuration file ${file.id} is outside component ${runtime.componentId} custody.` });
|