openova/products
e3mrah 401ab6713a
feat(catalyst-api): /api/v1/sandbox/sessions CRUD + sandboxes GVR in k8sCache + cutover-driver RBAC (#1637)
Wires the catalyst-api backend the Sandbox FE (PR #1621 — getSandboxes /
createSandbox / getByosStatus in sandbox.api.ts) has been calling into.
Without this handler the /sandbox surface on the Sovereign Console rendered
its empty state forever — every getSandboxes() 404'd at the catalyst-api
ingress and every "Start a session" click hit the same wall.

Handler — products/catalyst/bootstrap/api/internal/handler/sandbox_sessions.go
- GET    /api/v1/sandbox/sessions          — list Sandbox CRs in the
                                              operator's Org namespace
- POST   /api/v1/sandbox/sessions          — create Sandbox CR with agent
                                              validated against the 6-agent
                                              catalogue (aider / claude-code /
                                              cursor-agent / little-coder /
                                              opencode / qwen-code)
- GET    /api/v1/sandbox/sessions/{id}     — fetch single Sandbox detail
- DELETE /api/v1/sandbox/sessions/{id}     — graceful delete (the controller
                                              fires finalizers + cleans up
                                              the per-Sandbox vcluster
                                              namespace + PVCs + RBAC)

Client resolution mirrors the Family E compliance + k8s_resource_actions.go
seam: k8sCache.Factory.DynamicClientFor(resolveChrootClusterID("")) is the
primary path; sovereignDepsFor() — rest.InClusterConfig() — is the chroot
in-cluster fallback per feedback_chroot_in_cluster_fallback.md. Both 503
when unavailable so the FE renders its "API pending" pill rather than a
spinner.

Org-scoping uses claims.Org (the org_id Keycloak claim PR #1619 lit up)
for the CR namespace + spec.owner.orgRef.slug. Single-tenant chroots
without an org_id fall back through CATALYST_SANDBOX_DEFAULT_NAMESPACE
to a sensible default per docs/INVIOLABLE-PRINCIPLES.md #4. Wave-1 quota
defaults (4 CPU / 8Gi memory / 50Gi storage / 3 concurrent sessions)
mirror products/sandbox/docs/architecture.md §7 — the FE doesn't yet
expose a quota picker.

Status projection: CRD vocabulary (Pending|Provisioning|Ready|Failed)
maps to FE vocabulary (pending|running|stopped|failed|unknown) in
mapSandboxStatus so a fresh Sandbox shows the spinner rather than
"unknown" until the controller catches up.

k8sCache.DefaultKinds — products/catalyst/bootstrap/api/internal/k8scache/kinds.go
- Adds sandbox.openova.io/v1 Sandbox so the generic /k8s/{kind} surface
  enumerates Sandboxes the same way it does Applications + UserAccess.
  Per feedback_chroot_in_cluster_fallback.md every new GVR here needs a
  matching rule on the cutover-driver SA.

Cutover-driver RBAC — products/catalyst/chart/templates/clusterrole-cutover-driver.yaml
- Adds sandboxes.sandbox.openova.io with verbs split per
  feedback_rbac_create_no_resourcenames.md:
    rule 1: ["create"]
    rule 2: ["get","list","watch","delete"]
- Read-only on status (the controller owns status); write is spec-only
  on POST + the apiserver delete on DELETE.

Routes — products/catalyst/bootstrap/api/cmd/api/main.go
- Registered inside the RequireSession group alongside the existing
  /api/v1/sandbox/byos/claude-code/* surface; same auth gate, same
  patternless leading "/api/v1/sandbox/...".

Verified: go build clean, go vet clean, k8scache test suite green
(2.7s), helm template renders the new RBAC block.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 10:45:05 +04:00
..
axon feat(axon): make qwen3-coder thinking mode toggleable via request parameter 2026-04-26 09:20:33 +02:00
catalyst feat(catalyst-api): /api/v1/sandbox/sessions CRUD + sandboxes GVR in k8sCache + cutover-driver RBAC (#1637) 2026-05-18 10:45:05 +04:00
continuum feat(continuum): F — dry-run report + post-switchover health check + audit-emit coverage (slice F-1+F-2+F-3, #1101) (#1161) 2026-05-09 08:33:37 +04:00
cortex docs(pass-52): bundled date-sweep + cross-component namespace clean; knative clean 2026-04-28 00:37:21 +02:00
dmz-vcluster fix: mark bp-dmz-vcluster + bp-netbird default-off for smoke-render gate (#1286) 2026-05-10 15:57:18 +04:00
fabric docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay 2026-04-28 10:23:46 +02:00
fingate docs(pass-52): bundled date-sweep + cross-component namespace clean; knative clean 2026-04-28 00:37:21 +02:00
openova-flow fix(openova-flow): COPY go.sum + go mod download in Dockerfile (#1475) 2026-05-14 14:23:57 +04:00
relay docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay 2026-04-28 10:23:46 +02:00
sandbox docs: session 2026-05-17/18 convergence report + DoD D32-D35 + Sandbox status update (#1635) 2026-05-18 10:28:11 +04:00