UI Impact — Platform Config (WIP)
Status: Work in Progress. This document captures the product design intent for the Platform Config concept and its effect on the control plane UI. It is a design proposal, not a finalized decision.
Problem
The capability-centric ADR (this series) defines optional Layer 3 capability toggles, but a deeper configuration problem remains unaddressed: platform-wide infrastructure settings are scattered, hardcoded, and have no dedicated management interface.
Today in production, the following resources already exist as first-class DB objects:
| Resource name | ResourceType | Owned by |
|---|---|---|
trino_presets | trino-presets | compute-profiles stack (via compose) |
hive_metastore_presets | hive-metastore-presets | compute-profiles stack (via compose) |
observability_presets | observability-presets | compute-profiles stack (via compose) |
jupyterhub_profile_set | jupyterhub-profile-set | compute-profiles stack (via compose) |
These resources already have their own ResourceType with a seeded JSON Schema. But they are managed as compose members, not as platform configuration. The result:
- No dedicated UI page — users have no clear path to view or change compute sizing
- Hardcoded values in
bundle.yaml— no runtime configurability - Tightly coupled to compose — a compute profile change requires a full compose re-apply
- No clear "what tier is this workspace using?" signal in the UI
The Core Insight
These resources represent platform configuration, not deployed software. Trino, Hive Metastore, and Airflow are software — they have a deploy/destroy lifecycle, Helm versions, and an upgrade path. Compute presets, storage classes, and observability profiles are settings — they have an edit/apply lifecycle, no versioning needed, and immediate effect.
The VS Code analogy is instructive:
VS Code has a dedicated top-level Settings page — not embedded inside an extension's management page, but a first-class UI element. Settings are grouped by category, schema-driven, and applied independently of the extension deployment flow.
The platform needs the same: a "Platform Config" page scoped to a workspace, distinct from the compose/stack management view.
New Concept: PlatformConfig
A PlatformConfig is a first-class workspace entity that maps 1:1 to a ResourceType:
PlatformConfig (workspace-scoped)
├── resourceTypeId → ResourceType (schema already seeded)
├── config: Json ← user-supplied values (e.g. { tier: "large" })
├── applyStatus ← K8s-style status
└── generates → Resource → ResourceRevision.config
One PlatformConfig per ResourceType per workspace. The ResourceType's existing JSON Schema drives the configuration form. ResourceRevision.config becomes the rendered output of PlatformConfig.config — not a direct user input.
This separates two things that are currently conflated:
- What are the available tier options? → Defined by
ResourceType.schema(operator-owned) - Which tier does this workspace use? → Stored in
PlatformConfig.config(user-owned)
Revised Workspace Creation Flow
Current:
Create Workspace → cluster + credentials → done
Proposed three-step wizard:
Step 1: Cloud Config
Target cluster, region, credentials
(currently exists — needs better UI surfacing, not new logic)
Step 2: Platform Config ← NEW
┌─────────────────────────────────────────────────┐
│ Compute │
│ Trino Cluster Size [medium ▼] │
│ Hive Metastore Size [small ▼] │
│ JupyterHub Profiles [medium ▼] │
│ │
│ Observability │
│ Observability Size [dev ▼] │
└─────────────────────────────────────────────────┘
Creates a PlatformConfig row per ResourceType.
Applies them to the cluster before any application stack is deployed.
Step 3: Product Capability ← NEW FRAMING
┌─────────────────────────────────────────────────┐
│ Platform [Delta Spark ▼] │
│ Git revision [main ] │
│ │
│ Optional capabilities: │
│ ☐ Trino (query engine) │
│ ☐ JupyterHub (notebooks) │
│ ☐ MLflow (experiment tracking) │
└─────────────────────────────────────────────────┘
Creates StackCompose. Compose consumes PlatformConfig outputs.
compute-profiles is no longer a compose member — it is a precondition.
Step 2 can also be skipped (accept defaults) for quick workspace creation.
Workspace Navigation Change
Current nav:
Overview | Resources | Events | Agent Commands | Settings
Proposed:
Overview | Resources | Events | Platform Config | Agent Commands | Settings
"Platform Config" is a dedicated nav item — not a sub-section of Settings, because its lifecycle (apply, status, history) is different from workspace lifecycle settings (provision, delete).
Platform Config Page Design
Workspace → Platform Config
─ Compute ──────────────────────────────────────────────────
Trino Presets Ready ✓ [Edit]
Current: medium tier (r6g.xlarge coordinator, 4×r6g.2xlarge workers)
Hive Metastore Presets Ready ✓ [Edit]
Current: small tier (t3.large, 2G JVM)
JupyterHub Profile Set Ready ✓ [Edit]
Current: memory_optimized_small / medium / large
─ Observability ─────────────────────────────────────────────
Observability Presets Ready ✓ [Edit]
Current: dev tier (SingleBinary, 10Gi Loki)
Clicking "Edit" opens an inline or slide-over form driven by ResourceType.schema. The form shows the user-editable fields only (tier selection, optional custom overrides). Saving calls PATCH /platform-configs/:uid and triggers apply for that resource type only — no other compose members are touched.
Apply status flows back to the page in real time (same K8s-style conditions as StackCompose).
Compose Changes
With PlatformConfig owning the preset resources, compute-profile is removed as a compose member and replaced with a preconditions declaration:
# delta-spark.yaml (revised)
preconditions:
platformConfigs:
- resourceTypeId: trino-presets
- resourceTypeId: hive-metastore-presets
- resourceTypeId: observability-presets
compose:
members:
- name: karpenter ...
- name: observability ... # consumes observability_presets via resource dependency
- name: spark-operator ...
# compute-profile REMOVED
At applyCompose() time, the compose service verifies each precondition is Applied, then auto-wires the generated resource UIDs into the members that depend on them. The existing resource dependency mechanism handles this — only the source changes (PlatformConfig instead of compose-owned stack).
Day 2 Resize Flow
Before:
- No clear path — edit bundle.yaml, redeploy compose, all members re-apply
After:
Workspace → Platform Config → Trino Presets → Edit
Cluster Size: [medium] → [xlarge]
[Save & Apply]
→ Only the trino_presets resource re-applies
→ Trino picks up new JVM heap + instance type on next pod restart
→ No compose re-apply, no other stacks affected
Open Questions
-
Schema granularity: Does
ResourceType.schemaalready contain the user-editable subset, or does it describe the full K8s resource shape? If the latter, we need a separateconfigSchema(user-facing, a subset) alongside the existing schema (full resource validation). -
Standalone Stack ownership: A
PlatformConfigneeds to apply K8s resources to the cluster. The cleanest path is to have it create and own a standaloneStack(not inside a Compose). This preserves the existing Resource → Stack → ResourceRevision chain. But how does this standalone stack appear in the Resources tab? Should it be hidden? -
Migration of existing resources: Workspaces already running with
trino_presetsetc. deployed via the old compose need a migration path. How do we transfer ownership from the compose-created Stack to a new PlatformConfig without disrupting the running cluster? -
Default values: When a workspace skips Step 2 (accepts defaults), the PlatformConfig rows should be auto-created with
defaultConfigvalues. Where are these defaults defined — inResourceType.defaultConfig(new column) or in the compose kind'spreconditionsdeclaration? -
Account-level defaults: An account admin may want to set org-wide defaults (e.g., "all prod workspaces start at large tier"). This is a future extension —
PlatformConfigvalues could inherit from account-levelPlatformConfigDefaultrows if no workspace-level override exists. -
UI label: "Platform Config" vs "Platform Settings" — which is clearer to operators? "Config" implies infrastructure configuration; "Settings" implies user preferences. The content is closer to configuration.
PoC Plan
Before backend implementation, validate the UX with a UI-only PoC using mocked data.
Scope (control-plane/ui only, no backend changes):
- Add "Platform Config" nav item to
workspace-nav.tsx - Add route
/workspaces/:id/platform-configinApp.tsx - New page
WorkspacePlatformConfig.tsx(mirrorsWorkspaceSettings.tsxstructure) - New component
platform-config-page.tsxwith hardcoded mock data - Per-card edit form driven by inline schema (simple
<select>for enum fields — no RJSF dependency yet)
The PoC answers: does the per-resource-type granularity (one card per preset type) feel right, or should we group all compute presets into a single card? Does "Platform Config" as a nav item feel natural?