Skip to content

feat(workspace): add workspace resource model with scoping, membershi…#2243

Draft
derekwaynecarr wants to merge 2 commits into
NVIDIA:mainfrom
derekwaynecarr:decarr/workspace-model
Draft

feat(workspace): add workspace resource model with scoping, membershi…#2243
derekwaynecarr wants to merge 2 commits into
NVIDIA:mainfrom
derekwaynecarr:decarr/workspace-model

Conversation

@derekwaynecarr

@derekwaynecarr derekwaynecarr commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements Phase 1 of RFC 0011 — the workspace and membership model that provides hard isolation boundaries for multi-player OpenShell deployments.

  • Workspace resource: CRUD RPCs (CreateWorkspace, GetWorkspace, ListWorkspaces, DeleteWorkspace) with a default workspace created on gateway startup for backwards compatibility
  • Workspace-scoped resources: workspace field on ObjectMeta — sandboxes, providers, service endpoints, SSH sessions, policies, settings, provider refresh state, and inference routes all inherit workspace
    from context
  • Membership model: AddWorkspaceMember, RemoveWorkspaceMember, ListWorkspaceMembers RPCs with (workspace, principal_subject) → role records; membership records cleaned up on workspace deletion
  • Persistence: Name uniqueness shifts from (object_type, name) to (object_type, workspace, name) via migration 006; existing resources backfilled to default workspace; cross-workspace list_by_type store
    method for infrastructure operations (reconciler, resume, provider refresh)
  • Provider profiles: Two-tier scoping with independent scope listing — workspace custom + built-in, or platform custom + built-in (no merged cross-scope view)
  • Service routing: Endpoint hostnames include workspace prefix ({workspace}--{sandbox}--{service}.{domain}); default workspace preserves current format
  • Inference routes: Rename Cluster* RPCs and messages to Route* (SetInferenceRoute, GetInferenceRoute, InferenceRouteConfig); workspace-scope route storage and lookup; derive workspace from sandbox
    principal for bundle resolution; thread --workspace through CLI inference commands
  • ObjectWorkspace trait: requires_workspace() method with debug_assert! validation in store write helpers to catch empty-workspace bugs in debug builds
  • CLI: --workspace and --all-workspaces flags on all resource commands; WORKSPACE column as first column in list outputs when --all-workspaces (matching kubectl --all-namespaces convention); workspace
    CRUD and membership subcommands; inference help text updated from "gateway-level" to "workspace-level"
  • TUI: Workspace state with [w] key cycling, WORKSPACE column in sandbox/provider tables, workspace indicator in title bar, workspace-scoped gRPC requests

Related Issue

#1977

Changes

  • 50 files changed, +7188 / -886 lines
  • New files: workspace.rs (gRPC handlers + tests), migration 006 (sqlite + postgres), workspace_lifecycle.rs (e2e test), RFC 0011
  • Proto: Workspace, WorkspaceMember messages; workspace CRUD and membership RPCs; workspace field on ObjectMeta; all_workspaces on list requests; inference route rename (Cluster* → Route*) with
    workspace fields on set/get request/response messages

Testing

  • 897 server unit tests passing (11 workspace-specific, 2 inference route workspace isolation)
  • 24 TUI tests passing
  • 7 CLI integration tests passing
  • e2e workspace lifecycle test covering create/list/get/delete and cross-workspace isolation
  • Inference route isolation tests: verify workspace-scoped route resolution (alpha/beta workspaces with different providers/models) and empty bundle for workspace with no route configured

Known remaining work

  • Phase 2: Expanded role model (Platform Admin / Workspace Admin / User) and authorization enforcement — not started, sequential dependency on this PR.

@derekwaynecarr derekwaynecarr requested review from a team, maxamillion and mrunalp as code owners July 13, 2026 12:23
@copy-pr-bot

copy-pr-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@derekwaynecarr derekwaynecarr marked this pull request as draft July 13, 2026 12:26
@derekwaynecarr derekwaynecarr force-pushed the decarr/workspace-model branch 2 times, most recently from 2468c04 to 77ef1fc Compare July 13, 2026 22:31
Thread workspace through compute drivers (Docker, Podman, K8s) with
converged container naming (openshell-{workspace}--{name}-{id}),
workspace labels, and label-based lookup. Fix sandbox-side policy sync
to use learned workspace instead of hardcoding empty string. Fix TUI
all-workspaces view to use per-row workspace for sandbox actions. Add
provider profile to workspace deletion blocking check. Thread workspace
through SSH config generation with workspace-qualified host aliases.

Signed-off-by: Derek Carr <decarr@redhat.com>
Add workspace as a required parameter for resource-scoped operations
(create, get, delete, wait_ready, wait_deleted) and optional for
list operations that support all_workspaces global lookup. Rename
ClusterInferenceConfig to InferenceRouteConfig and set_cluster/get_cluster
to set_route/get_route to match the renamed proto RPCs.

Signed-off-by: Derek Carr <decarr@redhat.com>
@derekwaynecarr derekwaynecarr force-pushed the decarr/workspace-model branch from 77ef1fc to 7bba756 Compare July 14, 2026 18:18
for (object_type, label) in [
(Sandbox::object_type(), "sandbox"),
(Provider::object_type(), "provider"),
(StoredProviderProfile::object_type(), "provider profile"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From CodeRabbit review:
Include SSH sessions in workspace deletion blockers.

ObjectWorkspace for SshSession marks sessions as workspace-scoped, but deletion does not check SshSession::object_type(). A workspace can therefore be removed while session records still reference it.

     for (object_type, label) in [
         (Sandbox::object_type(), "sandbox"),
         (Provider::object_type(), "provider"),
+        (SshSession::object_type(), "SSH session"),
         (ServiceEndpoint::object_type(), "service"),
         (InferenceRoute::object_type(), "inference route"),
     ] {

&self,
request: Request<ListWorkspacesRequest>,
) -> Result<Response<ListWorkspacesResponse>, Status> {
workspace::handle_list_workspaces(&self.state, request).await

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From CodeRabbit review:

Enforce target-workspace membership on read RPCs.

The static workspace:read/user check does not bind authorization to request.workspace or request.name. Any user holding that scope can enumerate workspaces and list another workspace’s member subjects. Add workspace-specific membership authorization before delegating to these handlers.

Also applies to: 728-733

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants