Skip to content

feat(settings): gateway-to-sandbox runtime settings channel#474

Open
johntmyers wants to merge 21 commits intomainfrom
codex/feat-405-sandbox-settings-channel
Open

feat(settings): gateway-to-sandbox runtime settings channel#474
johntmyers wants to merge 21 commits intomainfrom
codex/feat-405-sandbox-settings-channel

Conversation

@johntmyers
Copy link
Collaborator

@johntmyers johntmyers commented Mar 19, 2026

Summary

Adds a general-purpose settings channel between the gateway and sandboxes, enabling runtime configuration changes without sandbox restarts. This separates the settings transport concern from issue #393 (OCSF logging) so it can land independently.

Related Issue

Closes #405

UX Changes

CLI

New commands:

  • openshell settings get [sandbox] [--global] [--json] -- show effective settings with scope indicators (sandbox/global/unset)
  • openshell settings set [sandbox] --key K --value V [--global] [--yes] -- set a setting at sandbox or global scope
  • openshell settings delete [sandbox] --key K [--global] [--yes] -- delete a setting (sandbox-scoped when not globally managed, or global)
  • openshell policy list --global -- list global policy revision history
  • openshell policy get --global [--rev N] [--full] -- show a specific global policy revision

Changed commands:

  • openshell policy set --global now creates versioned revisions (deduped by hash) instead of overwriting in-place
  • openshell policy set --global --wait is rejected with a message ("global policies are effective immediately")
  • All --global mutations require HITL confirmation (bypass with --yes)

Scope resolution:

  • Two-tier precedence: global overrides sandbox. A globally-managed key blocks sandbox set/delete with FailedPrecondition.
  • Deleting a global setting restores sandbox-level control.

TUI

Dashboard:

  • Middle pane now has tabs: Providers | Global Settings (switch with h/l)
  • Global Settings tab: list all registered keys, edit with type-aware input (bool toggle, string/int text), HITL confirmation modals for set and delete
  • Gateway row shows Global Policy Active (v3) in yellow when a global policy is set

Sandbox screen:

  • Bottom pane now has tabs: Policy | Settings (switch with h/l)
  • Settings tab: shows effective settings with scope column (sandbox/global/unset), edit/delete with HITL
  • Globally-managed keys show status bar message when edit/delete attempted
  • Metadata pane shows Policy: managed globally (v3) when sandbox policy is globally overridden
  • All sandbox settings and policy source auto-refresh on tick (no need to exit/re-enter)

Sandbox Logs

  • Poll loop messages changed from Policy poll: to Settings poll:
  • Individual setting changes logged: Setting changed key=log_level new=debug old=<unset>
  • Policy reloaded successfully only appears when the policy hash actually changed (not for settings-only changes)
  • Global policy reloads include global_version=N in the log

Architecture (tl;dr)

Proto changes (sandbox.proto, openshell.proto)

New types: SettingValue (oneof string/bool/int64/bytes), EffectiveSetting (value + scope), SettingScope, PolicySource.

New RPCs:

  • GetSandboxSettings -- returns effective policy + merged settings + config_revision + global_policy_version
  • GetGatewaySettings -- returns global-only settings

Renamed: UpdateSandboxPolicy -> UpdateSettings -- now handles policy and setting mutations at both scopes through field-based dispatch (global, setting_key, setting_value, delete_setting).

Extended: GetSandboxPolicyStatus and ListSandboxPolicies gained a global bool to query global policy revisions.

Settings storage

Settings are stored as JSON blobs in the existing objects table with gateway_settings and sandbox_settings object types. Sandbox settings use a prefixed ID (settings:{sandbox_uuid}) to avoid PK collision with sandbox objects. A tokio::sync::Mutex serializes all settings mutations to prevent read-modify-write races.

Global policy versioning

Global policies are now versioned in the sandbox_policies table using a sentinel sandbox_id of __global__. Revisions are marked loaded immediately (no sandbox confirmation for global policies). The existing get_latest_policy, list_policies, and supersede_older_policies Store methods work unchanged with the sentinel.

Settings registry (openshell-core/src/settings.rs)

Compile-time REGISTERED_SETTINGS array defines allowed keys with typed SettingValueKind. Test keys (dummy_bool, dummy_int) gated behind dev-settings feature flag (on by default). The doc comment on the registry documents the process for adding new settings.

Config revision

config_revision is a 64-bit content hash (first 8 bytes of SHA-256 over policy + settings + source). The sandbox poll loop compares it to detect changes. Not a monotonic counter -- uses wrapping_add for the settings revision counter.

TUI Dashboard with a global policy set:

image

Sandbox view with a global policy and one global setting override:

image

Changes

  • Proto: New settings types, GetSandboxSettings/GetGatewaySettings RPCs, UpdateSettings rename, global flag on policy status/list RPCs, global_policy_version field
  • Server: Settings persistence, merge resolution, per-key mutual exclusion, global policy versioning, settings cleanup on sandbox delete, concurrency mutex
  • Sandbox: Poll loop refactor (settings diff, conditional OPA reload, global version logging)
  • CLI: settings get/set/delete, policy list/get --global, --json output, --wait rejection for global
  • TUI: Global settings tab, sandbox settings tab, dashboard global policy indicator, auto-refresh, HITL modals, scope indicators
  • Core: Settings registry with feature-gated test keys, parse_bool_like, display_setting_value
  • E2E: settings_management.rs covering full lifecycle (sandbox set/delete, global override, lock/unlock)
  • Unit tests: 35 new tests across openshell-core and openshell-server (merge algorithm, conflict guard, delete-unlock, concurrency, round-trips)
  • Docs: Architecture docs for settings subsystem, updated gateway/sandbox/TUI/security-policy docs

Testing

  • mise run pre-commit passes
  • Unit tests added/updated (35 new tests, all passing)
  • E2E tests added/updated (settings_management.rs)
  • Manual CLI smoke testing (all settings/policy flows)
  • Manual TUI testing (both tabs, edit/delete/HITL, auto-refresh, global policy indicator)

Checklist

  • Follows Conventional Commits
  • Architecture docs updated
  • User-facing docs deferred (no real settings to document yet -- dummy keys are dev-only)

@johntmyers johntmyers requested a review from a team as a code owner March 19, 2026 15:37
@johntmyers johntmyers self-assigned this Mar 19, 2026
@github-actions
Copy link

github-actions bot commented Mar 19, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://NVIDIA.github.io/OpenShell/pr-preview/pr-474/

Built to branch gh-pages at 2026-03-19 23:09 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@johntmyers johntmyers added the test:e2e Requires end-to-end coverage label Mar 19, 2026
@johntmyers johntmyers requested review from drew and pimlock March 19, 2026 21:19
Closes #405

Refactor the sandbox policy polling channel into an effective settings response with config revision tracking, global policy source metadata, and merged global/sandbox key resolution.

Add gateway-global and sandbox-scoped setting mutations with per-key mutual exclusion, global delete unlock semantics, and global policy override behavior.

Extend the CLI with settings get/set/delete and --global policy flows, then document the new control-plane behavior in architecture and user docs.

Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
@johntmyers johntmyers force-pushed the codex/feat-405-sandbox-settings-channel branch from 4d4e3ce to 85fef54 Compare March 19, 2026 21:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(gateway/sandbox): support arbitrary gateway-to-sandbox runtime settings over SandboxConfig

1 participant