From 1d7d22fae0ecb45026fe2d5472ccbfefc2e624fb Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 11 Aug 2026 17:12:11 +0000 Subject: [PATCH] feat(db): persist runtime-service exposure state (PAP-17051) Add a nullable `exposure` jsonb column to `workspace_runtime_services`, typed with the shared `RuntimeExposureStatus` contract, plus additive migration 0212. Exposure lifecycle (pending/ready/failed/cleanup_pending/removed), listener ownership, resolved hostname, canonical public URL, broker ref, and last non-sensitive error persist here, modelled independently of process `status`/`healthStatus` so HTTPS provisioning/failure/cleanup surface separately from backend health. Flows through the runtime-service read model automatically via drizzle `$inferSelect`. Migration is additive-only (single ADD COLUMN) and hand-authored to avoid sweeping unrelated stale-dist schema; numbering + safety gates pass. Co-Authored-By: Claude Opus 4.8 --- .../db/src/migrations/0219_runtime_service_exposure.sql | 1 + packages/db/src/migrations/meta/_journal.json | 9 ++++++++- packages/db/src/schema/workspace_runtime_services.ts | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 packages/db/src/migrations/0219_runtime_service_exposure.sql diff --git a/packages/db/src/migrations/0219_runtime_service_exposure.sql b/packages/db/src/migrations/0219_runtime_service_exposure.sql new file mode 100644 index 00000000000..686c18c25de --- /dev/null +++ b/packages/db/src/migrations/0219_runtime_service_exposure.sql @@ -0,0 +1 @@ +ALTER TABLE "workspace_runtime_services" ADD COLUMN "exposure" jsonb; diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index e8ee2e3ee30..fd7a9ef3050 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -1520,6 +1520,13 @@ "when": 1786711898729, "tag": "0218_mushy_jack_murdock", "breakpoints": true + }, + { + "idx": 219, + "version": "7", + "when": 1786908241945, + "tag": "0219_runtime_service_exposure", + "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/packages/db/src/schema/workspace_runtime_services.ts b/packages/db/src/schema/workspace_runtime_services.ts index 150c332dec8..405b0d5d4ab 100644 --- a/packages/db/src/schema/workspace_runtime_services.ts +++ b/packages/db/src/schema/workspace_runtime_services.ts @@ -7,6 +7,7 @@ import { timestamp, uuid, } from "drizzle-orm/pg-core"; +import type { RuntimeExposureStatus } from "@paperclipai/shared"; import { companies } from "./companies.js"; import { projects } from "./projects.js"; import { projectWorkspaces } from "./project_workspaces.js"; @@ -42,6 +43,11 @@ export const workspaceRuntimeServices = pgTable( startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(), stoppedAt: timestamp("stopped_at", { withTimezone: true }), stopPolicy: jsonb("stop_policy").$type>(), + // Structured opt-in `tailscale_https` exposure state (PAP-17049/17050). + // Null when the service is not configured for HTTPS exposure. Modelled + // independently of process `status`/`healthStatus` so HTTPS provisioning / + // failure / cleanup surface separately from backend process health. + exposure: jsonb("exposure").$type(), healthStatus: text("health_status").notNull().default("unknown"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),