Repository navigation
feat(gateway/sandbox): support arbitrary gateway-to-sandbox runtime settings over SandboxConfig #405
Description
Problem Statement
Issue #393 currently combines two concerns:
- OCSF logging integration in
openshell-sandbox(the primary deliverable) - A broader gateway -> sandbox operational settings channel via
SandboxConfig
The OCSF work only requires a narrow config shape (config_revision + logging.ocsf_enabled) to support hot-reload. Expanding #393 to define and implement a general arbitrary-settings channel increases implementation risk, review scope, and delivery time for the logging migration.
We need a dedicated follow-up issue for designing and implementing a robust gateway -> sandbox arbitrary settings mechanism on top of the existing policy poll response channel.
Proposed Design
Implement a generalized settings channel in the SandboxConfig transport path, separate from #393:
- Keep transport path:
GetSandboxPolicyResponse.sandbox_config - Keep revision semantics: gateway maintains a monotonic
config_revision; sandbox applies only newer revisions - Add a dedicated settings payload for arbitrary runtime settings, using a namespaced key/value model with typed values
- Define sandbox-side apply semantics:
- unknown keys are ignored safely
- invalid values are rejected with structured log output
- partial apply does not roll back unrelated valid keys
- Keep backward compatibility:
- older gateway (no arbitrary settings payload) remains no-op
- older sandbox ignores unknown payload fields from newer gateway
Delivery should include:
- Proto schema update for arbitrary settings payload
- Gateway config source plumbing and response population
- Sandbox apply loop integration and revision tracking
- Unit tests for backward compatibility and apply semantics
- E2E test covering runtime update without sandbox restart
- Documentation update in
architecture/and user-facing docs where relevant
Alternatives Considered
-
Keep arbitrary-settings work inside feat(sandbox): integrate openshell-ocsf crate with dual-file output, OCSF toggle config, and log site migration #393
- Rejected because it couples a large schema/control-plane change to the OCSF migration, increasing risk and delaying primary logging outcomes.
-
Add only more typed fields immediately (no generic settings payload)
- Rejected because each new runtime setting would require another proto/control-plane roundtrip and repeated plumbing work.
-
Send untyped JSON blobs only
- Rejected because it weakens validation, compatibility guarantees, and observability of invalid config.
Agent Investigation
- Reviewed
#393and confirmed it already introducesSandboxConfigonGetSandboxPolicyResponsewith revision-driven hot-reload. - Confirmed
#393currently framesSandboxConfigas a general-purpose operational config channel while also carrying the OCSF migration and 93-site logging refactor. - This split keeps
#393focused on OCSF deliverables and moves generalized settings-channel design into an independent issue.
Activity
johntmyers commentedon Mar 17, 2026
Implementation Plan
Issue type:
featComplexity: High
Confidence: Medium-High — core design decisions are now fixed; remaining work is implementation detail and migration mechanics.
Review Decisions (Locked 2026-03-17)
--global(avoid separate scope syntax).Summary
Refactor sandbox policy polling into a broader settings channel so the sandbox pulls a single settings envelope (policy + keyed settings) from the gateway. Add durable global gateway settings with per-key precedence over sandbox settings, enforce mutual exclusivity for a key at gateway vs sandbox scope, and expose CLI commands for get/set/delete flows with one-setting-per-request semantics. Global policy is treated as a reserved setting and, when present globally, fully overrides sandbox-level policy.
State Machine (Settings Resolution)
flowchart TD A[Sandbox check-in poll] --> B[Load gateway global settings singleton] B --> C[Load sandbox-scoped settings object] C --> D[Per-key resolve: global key wins] D --> E[Resolve policy: global policy key present?] E -->|yes| F[Use global policy payload entirely] E -->|no| G[Use sandbox policy payload] F --> H[Compose effective response: policy + settings] G --> H H --> I[Return settings response] J[CLI: set gateway setting key K] --> K[Prompt: this disables sandbox-level K] K --> L[Persist global K + bump global revision] L --> M[Sandboxes receive K from global on next poll] N[CLI: set sandbox setting key K] --> O{Is global K set?} O -->|yes| P[Reject with FailedPrecondition] O -->|no| Q[Persist sandbox K + bump sandbox revision] R[CLI: delete gateway setting key K] --> S[Remove global K + bump global revision] S --> T[Sandbox-level K becomes effective again]Scope
proto/sandbox.proto: Replace policy-only poll payload with settings envelope carrying effective policy + effective settings metadata.proto/openshell.proto: Add/extend setting management RPCs for gateway/global and sandbox scopes (one setting per request).crates/openshell-server/src/grpc.rs: Implement effective settings resolution, global-precedence enforcement, and per-key lock error semantics.crates/openshell-server/src/persistence/mod.rs+crates/openshell-server/src/persistence/{sqlite,postgres}.rs: Add durable models/helpers for global singleton settings and sandbox-scoped settings.crates/openshell-server/src/sandbox/mod.rs(or new settings module): Define settings domain model, merge logic, reserved policy key handling, and revision tracking.crates/openshell-sandbox/src/grpc_client.rs: Refactor polling client to consume settings envelope.crates/openshell-sandbox/src/lib.rs: Replace policy-only poll/apply loop with settings poll/apply loop while preserving LKG behavior for policy failures.crates/openshell-cli/src/main.rs+crates/openshell-cli/src/run.rs: Extend commands with--globalfor global operations and enforce one-setting-per-request UX.e2e/python/test_sandbox_policy.py(and/or new e2e file): Add end-to-end coverage for precedence, lock behavior, and global-policy propagation.architecture/+docs/: Update architecture and CLI documentation.Implementation Steps
Define settings wire model.
SettingValueenvelope and reserved policy key payload.GLOBALorSANDBOX) in effective responses.Add durable global + sandbox settings persistence.
global).Direct in-place poll channel refactor.
Implement precedence, locking, and unlock rules.
FailedPrecondition.Implement global policy-as-setting semantics.
CLI UX with
--global.--global(example:openshell policy set --global --policy <file>).--globalpattern for key/value operations.--yes) for automation.Validation and verification.
--globaland--yes.Test Plan
Risks & Open Questions
oneofprimitives vs structured payload) and long-term extensibility.settingsvs extending another existing namespace) while preserving--globalUX.Documentation Impact
architecture/: settings control-plane model, precedence rules, and poll-state machine.docs/:--globalusage for policy/settings, lockout behavior, and unlock via global delete.Revision 2 — locked global policy replacement semantics,
--globalCLI direction, and direct in-place refactor strategy from stakeholder feedbackRevision 1 — initial plan based on expanded UX and scope split from #393
feat(gateway/sandbox): add global and sandbox runtime settings flow
feat(gateway/sandbox): add global and sandbox runtime settings flow
feat(gateway/sandbox): add global and sandbox runtime settings flow
feat(gateway/sandbox): add global and sandbox runtime settings flow