Skip to content

Commit e7729ec

Browse files
committed
chore: merge main into supervisor configuration delivery
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
2 parents 43f4c94 + 5643e1f commit e7729ec

72 files changed

Lines changed: 3701 additions & 2026 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/tui-development/SKILL.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -469,14 +469,20 @@ Proto types come from `openshell-core` which generates them from `OUT_DIR` via `
469469

470470
```rust
471471
use openshell_core::proto::openshell_client::OpenShellClient;
472-
use openshell_core::proto::{ListSandboxesRequest, GetSandboxLogsRequest, ...};
472+
use openshell_core::proto::{
473+
all_workspaces_selector, workspace_selector, GetSandboxLogsRequest,
474+
ListSandboxesRequest, ...
475+
};
473476
```
474477

475478
### Proto field gotchas
476479

477480
- `DeleteSandboxRequest` uses the `name` field (not `id`):
478481
```rust
479-
let req = openshell_core::proto::DeleteSandboxRequest { name: sandbox_name };
482+
let req = openshell_core::proto::DeleteSandboxRequest {
483+
name: sandbox_name,
484+
workspace_scope: Some(workspace_selector(workspace)),
485+
};
480486
```
481487
- `WatchSandboxRequest` has extra fields beyond what you might need — always use `..Default::default()`:
482488
```rust
@@ -490,12 +496,24 @@ use openshell_core::proto::{ListSandboxesRequest, GetSandboxLogsRequest, ...};
490496
};
491497
```
492498
- `SandboxLogLine` proto fields: `sandbox_id`, `timestamp_ms`, `level`, `target`, `message`, `source`, `fields` (HashMap<String, String>).
493-
- `GetSandboxLogsRequest` fields: `sandbox_id`, `lines` (u32), `since_ms` (i64), `sources` (Vec<String>), `min_level` (String), `workspace` (String).
494-
- `ListSandboxesRequest` fields: `limit` (i64), `offset` (i64), `label_selector` (String), `workspace` (String), `all_workspaces` (bool).
495-
- `ListProvidersRequest` fields: `limit` (i64), `offset` (i64), `workspace` (String), `all_workspaces` (bool).
496-
- `ListWorkspacesRequest` fields: `limit` (i64), `offset` (i64), `label_selector` (String).
497-
- `UpdateConfigRequest` fields: `name` (String, sandbox name or empty for global), `setting_key`, `setting_value`, `delete_setting` (bool), `global` (bool), `workspace`.
498-
- Most resource requests include a `workspace` field that scopes the operation to the current workspace.
499+
- Workspace-scoped request fields use `workspace_scope: Option<WorkspaceSelector>`.
500+
Select one workspace with `Some(workspace_selector(name))`. List requests that
501+
explicitly support cross-workspace access also accept
502+
`Some(all_workspaces_selector())`; do not use that marker on other requests.
503+
- `GetSandboxLogsRequest` fields: `sandbox_id`, `lines` (u32), `since_ms` (i64),
504+
`sources` (Vec<String>), `min_level` (String), `workspace_scope`.
505+
- `ListSandboxesRequest` fields: `limit` (u32), `offset` (u32),
506+
`label_selector` (String), `workspace_scope`.
507+
- `ListProvidersRequest` fields: `limit` (u32), `offset` (u32),
508+
`workspace_scope`.
509+
- `ListWorkspacesRequest` fields: `limit` (u32), `offset` (u32),
510+
`label_selector` (String).
511+
- `UpdateConfigRequest` fields include `name` (String, sandbox name or empty for
512+
global), `setting_key`, `setting_value`, `delete_setting` (bool), `global`
513+
(bool), and `workspace_scope`. Sandbox-scoped updates require a named selector;
514+
gateway-global updates must leave `workspace_scope` as `None`.
515+
- Most resource requests require an explicit named `workspace_scope`, including
516+
the `default` workspace. An omitted selector is not an implicit default.
499517

500518
### gRPC timeouts
501519

architecture/gateway.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ health, metrics, or tunnel routes. The plaintext service router also rejects
5454
browser requests whose Fetch Metadata, Origin, or Referer headers indicate a
5555
cross-origin or sibling-subdomain request.
5656

57+
Public workspace-scoped RPCs carry a typed `WorkspaceSelector`. A request must
58+
select one non-empty workspace explicitly; `default` is an ordinary explicit
59+
name, not an omitted-value fallback. Sandbox, sandbox template, provider, and
60+
service list RPCs also accept an all-workspaces marker after Platform Admin
61+
authorization. Single-workspace handlers reject that marker. Platform-global
62+
policy operations require the selector to be absent, while workspace policy
63+
operations require it. The gateway authorizes the selected scope before
64+
performing resource lookup so malformed, unsupported, and unauthorized scopes
65+
have consistent behavior across resource types.
66+
5767
Docker and Podman report the local address through which their sandboxes can
5868
reach the gateway. When the primary listener covers that address, the gateway
5969
reuses it; sandbox JWT authentication and its RPC allowlist remain the callback
@@ -318,8 +328,8 @@ public descriptor set generated by `openshell-core`; a fingerprint test in
318328
Compute-driver, credential-driver, gateway-interceptor, and
319329
supervisor-middleware services are compiled contracts for internal extension
320330
boundaries, not public gateway RPCs. The current public inventory has 74
321-
methods, 289 messages, and 15 enums
322-
(`dae343fe6c3a53059655e72516d479dcfc0f44936734f53bad5d8ec89bfeeaf9`).
331+
methods, 291 messages, and 15 enums
332+
(`973d8476775316f198e1ded3805b13b6122b647186c763fde9dec73b93ae2da2`).
323333

324334
Storage-only messages live in the private, versioned
325335
`openshell.storage.v1` package under `crates/openshell-server/proto`. The server

crates/openshell-cli/src/commands/provider.rs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub async fn sandbox_provider_list(
5959
let response = client
6060
.list_sandbox_providers(ListSandboxProvidersRequest {
6161
sandbox_name: name.to_string(),
62-
workspace: workspace.to_string(),
62+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
6363
})
6464
.await
6565
.into_diagnostic()?;
@@ -91,7 +91,7 @@ pub async fn sandbox_provider_attach(
9191
let sandbox = client
9292
.get_sandbox(GetSandboxRequest {
9393
name: name.to_string(),
94-
workspace: workspace.to_string(),
94+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
9595
})
9696
.await
9797
.into_diagnostic()?
@@ -106,7 +106,7 @@ pub async fn sandbox_provider_attach(
106106
sandbox_name: name.to_string(),
107107
provider_name: provider.to_string(),
108108
expected_resource_version: resource_version,
109-
workspace: workspace.to_string(),
109+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
110110
})
111111
.await
112112
{
@@ -147,7 +147,7 @@ pub async fn sandbox_provider_detach(
147147
let sandbox = client
148148
.get_sandbox(GetSandboxRequest {
149149
name: name.to_string(),
150-
workspace: workspace.to_string(),
150+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
151151
})
152152
.await
153153
.into_diagnostic()?
@@ -162,7 +162,7 @@ pub async fn sandbox_provider_detach(
162162
sandbox_name: name.to_string(),
163163
provider_name: provider.to_string(),
164164
expected_resource_version: resource_version,
165-
workspace: workspace.to_string(),
165+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
166166
})
167167
.await
168168
{
@@ -305,8 +305,7 @@ pub async fn ensure_required_providers(
305305
.list_providers(ListProvidersRequest {
306306
limit,
307307
offset,
308-
workspace: workspace.to_string(),
309-
all_workspaces: false,
308+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
310309
})
311310
.await
312311
.into_diagnostic()?;
@@ -490,7 +489,7 @@ async fn auto_create_provider(
490489
profile_workspace: workspace.to_string(),
491490
credential_handles: HashMap::new(),
492491
}),
493-
workspace: workspace.to_string(),
492+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
494493
};
495494

496495
let response = client.create_provider(request).await.map_err(|status| {
@@ -538,7 +537,7 @@ async fn auto_create_provider(
538537
profile_workspace: workspace.to_string(),
539538
credential_handles: HashMap::new(),
540539
}),
541-
workspace: workspace.to_string(),
540+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
542541
};
543542

544543
match client.create_provider(request).await {
@@ -672,7 +671,7 @@ async fn rollback_provider_create_after_gcloud_adc_failure(
672671
match client
673672
.delete_provider(DeleteProviderRequest {
674673
name: provider_name.to_string(),
675-
workspace: workspace.to_string(),
674+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
676675
})
677676
.await
678677
{
@@ -1119,7 +1118,7 @@ pub async fn provider_create_with_options(options: ProviderCreateOptions<'_>) ->
11191118
profile_workspace: profile_workspace.to_string(),
11201119
credential_handles: HashMap::new(),
11211120
}),
1122-
workspace: workspace.to_string(),
1121+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
11231122
})
11241123
.await
11251124
.into_diagnostic()?;
@@ -1149,7 +1148,7 @@ pub async fn provider_create_with_options(options: ProviderCreateOptions<'_>) ->
11491148
"refresh_token".to_string(),
11501149
],
11511150
expires_at_ms: None,
1152-
workspace: workspace.to_string(),
1151+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
11531152
})
11541153
.await
11551154
{
@@ -1167,7 +1166,7 @@ pub async fn provider_create_with_options(options: ProviderCreateOptions<'_>) ->
11671166
.rotate_provider_credential(RotateProviderCredentialRequest {
11681167
provider: provider_name.clone(),
11691168
credential_key: adc_credential_key,
1170-
workspace: workspace.to_string(),
1169+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
11711170
})
11721171
.await
11731172
{
@@ -1208,7 +1207,7 @@ pub async fn provider_get(
12081207
let response = client
12091208
.get_provider(GetProviderRequest {
12101209
name: name.to_string(),
1211-
workspace: workspace.to_string(),
1210+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
12121211
})
12131212
.await
12141213
.into_diagnostic()?;
@@ -1339,12 +1338,11 @@ pub async fn provider_list(
13391338
.list_providers(ListProvidersRequest {
13401339
limit,
13411340
offset,
1342-
workspace: if all_workspaces {
1343-
String::new()
1341+
workspace_scope: Some(if all_workspaces {
1342+
openshell_core::proto::all_workspaces_selector()
13441343
} else {
1345-
workspace.to_string()
1346-
},
1347-
all_workspaces,
1344+
openshell_core::proto::workspace_selector(workspace)
1345+
}),
13481346
})
13491347
.await
13501348
.into_diagnostic()?;
@@ -1713,7 +1711,7 @@ pub async fn provider_refresh_status(
17131711
.get_provider_refresh_status(GetProviderRefreshStatusRequest {
17141712
provider: name.to_string(),
17151713
credential_key: credential_key.unwrap_or_default().to_string(),
1716-
workspace: workspace.to_string(),
1714+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
17171715
})
17181716
.await
17191717
.into_diagnostic()?
@@ -1794,7 +1792,7 @@ pub async fn provider_refresh_config(
17941792
material,
17951793
secret_material_keys,
17961794
expires_at_ms: input.credential_expires_at_ms,
1797-
workspace: workspace.to_string(),
1795+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
17981796
})
17991797
.await
18001798
.into_diagnostic()?
@@ -1823,7 +1821,7 @@ pub async fn provider_rotate(
18231821
.rotate_provider_credential(RotateProviderCredentialRequest {
18241822
provider: name.to_string(),
18251823
credential_key: credential_key.to_string(),
1826-
workspace: workspace.to_string(),
1824+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
18271825
})
18281826
.await
18291827
.into_diagnostic()?
@@ -1860,7 +1858,7 @@ pub async fn provider_refresh_delete(
18601858
.delete_provider_refresh(DeleteProviderRefreshRequest {
18611859
provider: name.to_string(),
18621860
credential_key: credential_key.to_string(),
1863-
workspace: workspace.to_string(),
1861+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
18641862
})
18651863
.await
18661864
.into_diagnostic()?
@@ -2236,7 +2234,7 @@ pub async fn provider_update(options: ProviderUpdateOptions<'_>) -> Result<()> {
22362234
let existing = match client
22372235
.get_provider(GetProviderRequest {
22382236
name: name.to_string(),
2239-
workspace: workspace.to_string(),
2237+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
22402238
})
22412239
.await
22422240
{
@@ -2319,7 +2317,7 @@ pub async fn provider_update(options: ProviderUpdateOptions<'_>) -> Result<()> {
23192317
credential_handles: HashMap::new(),
23202318
}),
23212319
credential_expires_at_ms,
2322-
workspace: workspace.to_string(),
2320+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
23232321
})
23242322
.await
23252323
.into_diagnostic()?;
@@ -2349,7 +2347,7 @@ pub async fn provider_delete(
23492347
let response = match client
23502348
.delete_provider(DeleteProviderRequest {
23512349
name: name.clone(),
2352-
workspace: workspace.to_string(),
2350+
workspace_scope: Some(openshell_core::proto::workspace_selector(workspace)),
23532351
})
23542352
.await
23552353
{

crates/openshell-cli/src/completers.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ pub fn complete_sandbox_names(_prefix: &OsStr) -> Vec<CompletionCandidate> {
3939
limit: 200,
4040
offset: 0,
4141
label_selector: String::new(),
42-
workspace: workspace_from_args(),
43-
all_workspaces: false,
42+
workspace_scope: Some(openshell_core::proto::workspace_selector(
43+
workspace_from_args(),
44+
)),
4445
})
4546
.await
4647
.ok()?;
@@ -64,8 +65,9 @@ pub fn complete_provider_names(_prefix: &OsStr) -> Vec<CompletionCandidate> {
6465
.list_providers(ListProvidersRequest {
6566
limit: 200,
6667
offset: 0,
67-
workspace: workspace_from_args(),
68-
all_workspaces: false,
68+
workspace_scope: Some(openshell_core::proto::workspace_selector(
69+
workspace_from_args(),
70+
)),
6971
})
7072
.await
7173
.ok()?;

0 commit comments

Comments
 (0)