Problem Statement
The gateway.toml configuration schema has accumulated 21 distinct inconsistencies across the gateway core and its four compute-driver tables (kubernetes, docker, podman, vm): misplaced fields (Kubernetes-specific settings living at the gateway level), inconsistent field naming for the same concept across drivers, overloaded 0 sentinel values instead of Option<T>, duplicated TLS fields with no practical use for the duplication, and feature parity gaps (proxy config, AppArmor, SPIFFE support present on some drivers but arbitrarily missing from others). This makes the config surface harder to document, harder to reason about, and easy to misconfigure. This issue proposes a structured cleanup.
Technical Context
[openshell.gateway] holds both true gateway concerns (listeners, TLS, auth) and driver-inheritable defaults (image names, TLS paths, K8s-only settings) that flow into driver tables via an allowlist-based inheritance mechanism. Because only one compute driver is active per gateway process, several fields designed for "shared defaults across drivers" provide no real value and only add indirection. Driver tables also evolved independently, so equivalent concepts (SSH socket path, gRPC endpoint, image pull policy, PID limits) have different names, types, and default-value conventions per driver.
Affected Components
| Component |
Key Files |
Role |
| Gateway config core |
crates/openshell-core/src/config.rs (lines ~413-1576) |
Core config types: GatewayJwtConfig, GatewayInterceptorConfig, compute driver kind parsing |
| Gateway config file loader |
crates/openshell-server/src/config_file.rs (lines ~45-1073) |
TOML parsing, schema versioning, driver-table inheritance (driver_table(), inheritable_keys(), gateway_inherited_value()) |
| Kubernetes driver config |
crates/openshell-driver-kubernetes/src/config.rs (lines ~93-1473) |
Most complete driver config: AppArmor, SPIFFE, proxy, topology |
| Docker driver config |
crates/openshell-driver-docker/src/lib.rs (lines ~88-500) |
Missing proxy config, AppArmor; raw-string pull policy |
| Podman driver config |
crates/openshell-driver-podman/src/config.rs (lines ~72-633) |
Typed pull-policy enum; has proxy config; inconsistent SSH socket field name |
| VM driver config |
crates/openshell-driver-vm/src/driver.rs (lines ~216-300) |
Inconsistent endpoint field name; divergent UID default; no proxy config |
| Published docs |
docs/reference/gateway-config.mdx (~1051 lines) |
Full schema reference; needs updates for any renamed/moved/retyped field |
Technical Investigation
Architecture Overview
Config loading happens in crates/openshell-server/src/config_file.rs:
- TOML is parsed into
ConfigFile (schema version validated against SCHEMA_VERSION = 1; secrets like database_url are rejected if present in the file).
driver_table() (config_file.rs:331-360) merges [openshell.gateway] defaults into the active driver's table.
- Each driver has an allowlist of inheritable keys,
inheritable_keys() (config_file.rs:362-386):
- Kubernetes:
namespace, default_image, supervisor_image, client_tls_secret_name, service_account_name, host_gateway_ip, enable_user_namespaces, sa_token_ttl_secs
- Docker:
sandbox_namespace, default_image, supervisor_image, host_gateway_ip, guest_tls_ca, guest_tls_cert, guest_tls_key
- Podman:
default_image, supervisor_image, host_gateway_ip, guest_tls_ca, guest_tls_cert, guest_tls_key
- VM:
default_image, guest_tls_ca, guest_tls_cert, guest_tls_key
- Remote/custom drivers: no inheritance
gateway_inherited_value() (config_file.rs:388-402) copies the value, and this is where a single gateway key (sandbox_namespace) maps to different meanings per driver (K8s namespace vs. Docker sandbox_namespace/label) - a source of confusion in its own right.
- Precedence overall: CLI flag >
OPENSHELL_* env var > TOML file > built-in default (documented in docs/reference/gateway-config.mdx:16-19).
Because only one compute driver runs per gateway process (despite compute_drivers being typed as Vec<String>, config_file.rs:115 / config.rs:480), the inheritance model exists mainly to avoid repeating a handful of values, but has become a source of misplaced ownership (Kubernetes-only settings sitting at the "shared" gateway level) rather than genuine cross-driver sharing.
Code References
| # |
Issue |
File:Line |
Current Behavior |
| 1 |
gateway.sandbox_namespace is K8s-specific |
config_file.rs:122 |
Gateway-level field inherited as namespace by K8s (:373), as sandbox_namespace by Docker (:377); unused by Podman/VM |
| 2 |
gateway.service_account_name is K8s-specific |
config_file.rs:125 |
Inherited only by Kubernetes (:374) |
| 3 |
gateway.enable_user_namespaces is driver-level |
config_file.rs:127 |
Inherited only by Kubernetes (:376) |
| 4 |
gateway.compute_drivers plural but single-active |
config_file.rs:115, config.rs:480 |
Option<Vec<String>>; runtime only uses the first entry |
| 5 |
gateway_jwt.ttl_secs overloads 0 |
config.rs:767-768 |
default_sandbox_token_ttl_secs() -> 0; 0 means "never expire" |
| 6 |
docker.sandbox_namespace should be sandbox_label |
docker/lib.rs:105 |
Named like a K8s namespace but is actually a Docker container label |
| 7 |
docker.sandbox_pids_limit overloads 0 |
docker/lib.rs:174, config.rs:123 |
i64; 0 means "use Docker default" |
| 8 |
docker.image_pull_policy raw String |
docker/lib.rs:103 |
Podman has a typed ImagePullPolicy enum (podman/config.rs:25) |
| 9 |
Docker missing proxy config |
docker/lib.rs:88-180 vs podman/config.rs:170-196 |
No https_proxy/no_proxy/proxy_auth_* fields |
| 10 |
podman.sandbox_pids_limit overloads 0 |
podman/config.rs:108 |
Same pattern as #7 |
| 11 |
podman.health_check_interval_secs overloads 0 |
podman/config.rs:119-126 |
u64; 0 = disable |
| 12 |
podman.sandbox_ssh_socket_path inconsistent name |
podman/config.rs:82 |
K8s/Docker use ssh_socket_path |
| 13 |
vm.openshell_endpoint inconsistent name |
vm/driver.rs:217 |
Other drivers use grpc_endpoint |
| 14 |
VM sandbox_uid default (10001) vs K8s (1000) |
vm/driver.rs:244 vs kubernetes/config.rs:341 |
DEFAULT_SANDBOX_UID diverges across drivers |
| 15 |
VM missing proxy config |
vm/driver.rs:216-260 |
No proxy fields, unlike Podman/K8s |
| 16 |
grpc_endpoint/openshell_endpoint duplicated per driver |
all driver configs |
Gateway already knows its own bind address; endpoint is redundantly configured per driver |
| 17 |
Interceptors vs. supervisor middleware field naming |
config.rs:653 (max_response_bytes) vs config_file.rs:228 (max_body_bytes) |
Structurally identical concept (body size limit), different field name |
| 18 |
provider_spiffe_workload_api_socket_path is K8s-only |
kubernetes/config.rs:368-371 |
Not exposed for Docker/Podman/VM despite SPIFFE being runtime-agnostic |
| 19 |
image_pull_policy type inconsistency |
podman/config.rs:25-57 (enum) vs kubernetes/config.rs:251 (String) vs docker/lib.rs:103 (String) |
No shared type; valid values differ per runtime (Podman adds Newer) |
| 20 |
guest_tls_ca/cert/key duplicated everywhere |
config_file.rs:129-131 + docker/lib.rs:159-161, podman/config.rs:101-103, vm/driver.rs:233-235 |
Defined at gateway level and independently in every driver struct; only one driver active at a time |
| 21 |
kubernetes.app_armor_profile is K8s-only |
kubernetes/config.rs:141-209, config.rs:302-307 |
AppArmor is a Linux kernel feature; Docker/Podman support --security-opt apparmor=<profile> too |
Current Behavior
0-overload pattern appears in at least 3 independent fields (gateway_jwt.ttl_secs, sandbox_pids_limit in both Docker and Podman, health_check_interval_secs in Podman), each reimplementing the same "0 means special case" convention with no shared idiom, and no way to distinguish "unset" from "explicitly zero."
- Inheritance key remapping: the same gateway key can mean different things per driver (
sandbox_namespace maps to K8s namespace vs. Docker label), which is confusing when reading gateway.toml in isolation.
- TLS field triplication:
guest_tls_ca/cert/key exist at the gateway level (for inheritance) and are redundantly redeclared in Docker, Podman, and VM driver structs, even though only one driver is ever active.
- Image pull policy has three different representations across drivers with no shared validation, so typos in Kubernetes/Docker configs are only caught (or not caught) at runtime by the container runtime itself, not at config-parse time.
- Feature parity gaps: proxy config (Podman, Kubernetes only), AppArmor (Kubernetes only), SPIFFE (Kubernetes only) are runtime-agnostic capabilities arbitrarily scoped to specific drivers, apparently because that's where they were first implemented rather than because of a technical constraint.
What Would Need to Change
Config file schema (config_file.rs)
- Remove Kubernetes-only fields (
sandbox_namespace, service_account_name, enable_user_namespaces) from GatewayFileSection; move to [openshell.drivers.kubernetes] only, updating inheritable_keys() accordingly.
- Resolve
compute_drivers: Vec<String> vs. single-active-driver reality - either narrow the type to a scalar or explicitly validate/document the "first wins" semantics.
- Add serde aliases for any renamed keys to support a migration window.
Core config types (config.rs)
- Convert
0-overloaded fields to Option<T> (gateway_jwt.ttl_secs, sandbox_pids_limit in Docker/Podman, health_check_interval_secs in Podman), with None meaning the "special" behavior and explicit validation rejecting nonsensical Some(0) where relevant.
- Rename
GatewayInterceptorConfig.max_response_bytes to align with middleware's max_body_bytes (or vice versa) for naming consistency.
Driver configs
- Docker: rename
sandbox_namespace to sandbox_label; adopt a shared typed ImagePullPolicy enum; add proxy config fields matching Podman; add app_armor_profile.
- Podman: rename
sandbox_ssh_socket_path to ssh_socket_path.
- VM: rename
openshell_endpoint to grpc_endpoint; reconcile sandbox_uid default with Kubernetes' 1000; add proxy config fields; add app_armor_profile.
- Kubernetes: no field changes needed, but should be the source of truth for shared types/fields being generalized (typed pull policy, SPIFFE, AppArmor, proxy).
Cross-cutting
- Decide TLS field ownership: consolidate
guest_tls_ca/cert/key to gateway-level only, removing the redundant per-driver copies (recommended, since only one driver is active and the paths are host-side).
- Decide
grpc_endpoint/openshell_endpoint ownership: move toward gateway-computed/auto-injected endpoint rather than per-driver configuration, with an explicit override escape hatch.
- Generalize SPIFFE workload socket support beyond Kubernetes (Docker/Podman via host socket bind-mount, VM via virtio-vsock).
- Update
docs/reference/gateway-config.mdx for every renamed, moved, or retyped field (currently documents ~250+ TOML examples touching these fields).
- Update Helm chart values/templates and packaged default configs (
deploy/rpm/gateway.toml.default, Homebrew default config) that reference any changed field.
Alternative Approaches Considered
- Alias lifetime: keep serde aliases for renamed fields permanently vs. a deprecation window (e.g., 2-3 releases) vs. a hard break tied to a major version. This needs a human/product decision, not an agent one.
0-overload replacement: Option<T> (idiomatic Rust, recommended) vs. sentinel constants (e.g., u64::MAX) vs. separate enable_* boolean fields. Option<T> is recommended for clarity, but changes TOML ergonomics (must omit the key rather than write = 0).
- TLS field ownership: gateway-only (recommended - single active driver, host-side paths, no case where drivers need different values) vs. keeping per-driver copies for hypothetical future multi-driver support.
grpc_endpoint auto-detection: full auto-injection by the gateway vs. keeping explicit per-driver config as an opt-in override when auto-detection is insufficient (e.g., custom network topologies). A pure auto-detect-only approach risks removing power-user control drivers currently rely on.
compute_drivers cardinality: whether OpenShell's roadmap ever intends multi-driver support. If never, collapse to a scalar field; if it's a real future possibility, keep the Vec type but tighten validation/docs now.
Patterns to Follow
- Existing driver
validate() methods (e.g., Podman's validate_tls_config(), validate_proxy_config() in podman/config.rs) are the established place to add validation for new Option<T> semantics or shared enum values.
- Podman's
ImagePullPolicy enum (podman/config.rs:25-57) is the best existing example of a typed pull-policy and should be promoted to openshell-core as the shared base type, with driver-specific extension (e.g., Podman's Newer variant) layered on top.
- Serde
#[serde(alias = "...")] is the established idiom in this codebase for backward-compatible renames.
Proposed Approach
Stage the cleanup rather than attempting all 21 issues in one PR. A natural split: (1) low-risk pure field renames with serde aliases (#6, #12, #13), (2) Option<T> conversions for 0-overloaded fields with validation (#5, #7, #10, #11), (3) feature-parity additions copying existing Kubernetes/Podman patterns to Docker/VM (#9, #15, #18, #21), and (4) architectural moves requiring the most human input (#1-3, #4, #16, #17, #19, #20 - field ownership changes, endpoint auto-detection, shared enum promotion, TLS consolidation). Each stage should update docs/reference/gateway-config.mdx, Helm chart values, and packaged default configs in the same PR, and ship serde aliases for any renamed/relocated key to avoid breaking existing deployments outright.
Scope Assessment
- Complexity: High (overall) - individual issues range from Low (pure renames) to High (field ownership moves, endpoint auto-detection)
- Confidence: Medium - the technical mapping is well understood and file:line-verified, but several design decisions (alias lifetime, TLS/endpoint ownership,
compute_drivers cardinality) require human/product input before implementation
- Estimated files to change: ~10-15 core files (
config.rs, config_file.rs, 4 driver config modules) plus docs/reference/gateway-config.mdx, Helm chart values/templates, and packaged default configs (RPM, Homebrew)
- Issue type:
refactor
Risks & Open Questions
- Backward compatibility is the dominant risk. OpenShell has real deployments with existing
gateway.toml files (hand-written, RPM/Homebrew defaults, Helm-generated). Field renames and moves are breaking changes unless serde aliases are added; type changes (0 to Option<T>) change what's valid TOML syntax for a field.
- VM
sandbox_uid default change (10001 to 1000) is a semantic behavior change, not just a rename - could affect file ownership expectations in already-running VM sandboxes. Needs explicit call-out in release notes if changed.
grpc_endpoint auto-detection could remove power-user control for custom network topologies if implemented as auto-detect-only rather than auto-detect-with-override.
compute_drivers Vec vs. scalar depends on whether multi-driver support is ever on the roadmap - a product decision, not a technical one.
- Helm chart and packaged defaults (
deploy/rpm/gateway.toml.default, Homebrew config) must be updated in lockstep with any renamed/moved field, or upgrades will silently stop applying settings.
- Alias retention window (how long to support old field names before removal) needs a human decision - likely tied to release/versioning policy.
Disposition Readiness
- State:
state:validated
- Assessment: The investigation confirms all 21 issues against current source with exact file:line references, maps the inheritance mechanism, and surfaces the key design decisions and backward-compatibility risks needed for a human accept/decline call. No further investigation is needed to make that decision.
- Missing evidence: None for disposition. Implementation-time decisions (alias lifetime, TLS/endpoint ownership model,
compute_drivers cardinality) are flagged above and should be resolved during build-from-issue planning or via explicit human direction before implementation begins.
Test Considerations
crates/openshell-server/src/config_file.rs has 30+ existing tests including driver-table inheritance tests (~lines 729-858) that will need updates for any moved/renamed inheritable keys.
crates/openshell-core/src/config.rs has 40+ tests covering compute driver kind parsing, gateway JWT defaults, interceptor failure policies - default-value tests will need updates for 0 to Option<T> conversions.
- Kubernetes driver config has 56 tests (
kubernetes/config.rs), Podman has 31 (podman/config.rs) covering TLS validation, proxy config, runtime limits - new tests needed for any fields ported to Docker/VM.
- A packaged-default "contract test" exists (referenced around
config_file.rs:860) validating RPM/Homebrew default configs parse correctly - must be updated alongside any schema change.
- Any staged PR should include: unit tests for new
Option<T> validation logic, alias-parsing tests (old name still works, new name works, both present is an error or new-name-wins), and updated inheritance tests for any relocated keys.
docs/reference/gateway-config.mdx should be treated as a documentation contract - every schema example in the ~1051-line file touching a changed field must be updated in the same PR as the code change.
Created by spike investigation. state:validated means the issue is ready for human disposition; state:needs-info means specific evidence is still required. A human applies state:accepted or places the issue on the roadmap if OpenShell should pursue the work. To queue unattended agent planning, a human applies agent:plan-requested; a direct request to an agent does not require that label.
Problem Statement
The
gateway.tomlconfiguration schema has accumulated 21 distinct inconsistencies across the gateway core and its four compute-driver tables (kubernetes,docker,podman,vm): misplaced fields (Kubernetes-specific settings living at the gateway level), inconsistent field naming for the same concept across drivers, overloaded0sentinel values instead ofOption<T>, duplicated TLS fields with no practical use for the duplication, and feature parity gaps (proxy config, AppArmor, SPIFFE support present on some drivers but arbitrarily missing from others). This makes the config surface harder to document, harder to reason about, and easy to misconfigure. This issue proposes a structured cleanup.Technical Context
[openshell.gateway]holds both true gateway concerns (listeners, TLS, auth) and driver-inheritable defaults (image names, TLS paths, K8s-only settings) that flow into driver tables via an allowlist-based inheritance mechanism. Because only one compute driver is active per gateway process, several fields designed for "shared defaults across drivers" provide no real value and only add indirection. Driver tables also evolved independently, so equivalent concepts (SSH socket path, gRPC endpoint, image pull policy, PID limits) have different names, types, and default-value conventions per driver.Affected Components
crates/openshell-core/src/config.rs(lines ~413-1576)GatewayJwtConfig,GatewayInterceptorConfig, compute driver kind parsingcrates/openshell-server/src/config_file.rs(lines ~45-1073)driver_table(),inheritable_keys(),gateway_inherited_value())crates/openshell-driver-kubernetes/src/config.rs(lines ~93-1473)crates/openshell-driver-docker/src/lib.rs(lines ~88-500)crates/openshell-driver-podman/src/config.rs(lines ~72-633)crates/openshell-driver-vm/src/driver.rs(lines ~216-300)docs/reference/gateway-config.mdx(~1051 lines)Technical Investigation
Architecture Overview
Config loading happens in
crates/openshell-server/src/config_file.rs:ConfigFile(schema version validated againstSCHEMA_VERSION = 1; secrets likedatabase_urlare rejected if present in the file).driver_table()(config_file.rs:331-360) merges[openshell.gateway]defaults into the active driver's table.inheritable_keys()(config_file.rs:362-386):namespace, default_image, supervisor_image, client_tls_secret_name, service_account_name, host_gateway_ip, enable_user_namespaces, sa_token_ttl_secssandbox_namespace, default_image, supervisor_image, host_gateway_ip, guest_tls_ca, guest_tls_cert, guest_tls_keydefault_image, supervisor_image, host_gateway_ip, guest_tls_ca, guest_tls_cert, guest_tls_keydefault_image, guest_tls_ca, guest_tls_cert, guest_tls_keygateway_inherited_value()(config_file.rs:388-402) copies the value, and this is where a single gateway key (sandbox_namespace) maps to different meanings per driver (K8snamespacevs. Dockersandbox_namespace/label) - a source of confusion in its own right.OPENSHELL_*env var > TOML file > built-in default (documented indocs/reference/gateway-config.mdx:16-19).Because only one compute driver runs per gateway process (despite
compute_driversbeing typed asVec<String>,config_file.rs:115/config.rs:480), the inheritance model exists mainly to avoid repeating a handful of values, but has become a source of misplaced ownership (Kubernetes-only settings sitting at the "shared" gateway level) rather than genuine cross-driver sharing.Code References
gateway.sandbox_namespaceis K8s-specificconfig_file.rs:122namespaceby K8s (:373), assandbox_namespaceby Docker (:377); unused by Podman/VMgateway.service_account_nameis K8s-specificconfig_file.rs:125:374)gateway.enable_user_namespacesis driver-levelconfig_file.rs:127:376)gateway.compute_driversplural but single-activeconfig_file.rs:115,config.rs:480Option<Vec<String>>; runtime only uses the first entrygateway_jwt.ttl_secsoverloads0config.rs:767-768default_sandbox_token_ttl_secs() -> 0;0means "never expire"docker.sandbox_namespaceshould besandbox_labeldocker/lib.rs:105docker.sandbox_pids_limitoverloads0docker/lib.rs:174,config.rs:123i64;0means "use Docker default"docker.image_pull_policyrawStringdocker/lib.rs:103ImagePullPolicyenum (podman/config.rs:25)docker/lib.rs:88-180vspodman/config.rs:170-196https_proxy/no_proxy/proxy_auth_*fieldspodman.sandbox_pids_limitoverloads0podman/config.rs:108podman.health_check_interval_secsoverloads0podman/config.rs:119-126u64;0= disablepodman.sandbox_ssh_socket_pathinconsistent namepodman/config.rs:82ssh_socket_pathvm.openshell_endpointinconsistent namevm/driver.rs:217grpc_endpointsandbox_uiddefault (10001) vs K8s (1000)vm/driver.rs:244vskubernetes/config.rs:341DEFAULT_SANDBOX_UIDdiverges across driversvm/driver.rs:216-260grpc_endpoint/openshell_endpointduplicated per driverconfig.rs:653(max_response_bytes) vsconfig_file.rs:228(max_body_bytes)provider_spiffe_workload_api_socket_pathis K8s-onlykubernetes/config.rs:368-371image_pull_policytype inconsistencypodman/config.rs:25-57(enum) vskubernetes/config.rs:251(String) vsdocker/lib.rs:103(String)Newer)guest_tls_ca/cert/keyduplicated everywhereconfig_file.rs:129-131+docker/lib.rs:159-161,podman/config.rs:101-103,vm/driver.rs:233-235kubernetes.app_armor_profileis K8s-onlykubernetes/config.rs:141-209,config.rs:302-307--security-opt apparmor=<profile>tooCurrent Behavior
0-overload pattern appears in at least 3 independent fields (gateway_jwt.ttl_secs,sandbox_pids_limitin both Docker and Podman,health_check_interval_secsin Podman), each reimplementing the same "0 means special case" convention with no shared idiom, and no way to distinguish "unset" from "explicitly zero."sandbox_namespacemaps to K8snamespacevs. Docker label), which is confusing when reading gateway.toml in isolation.guest_tls_ca/cert/keyexist at the gateway level (for inheritance) and are redundantly redeclared in Docker, Podman, and VM driver structs, even though only one driver is ever active.What Would Need to Change
Config file schema (
config_file.rs)sandbox_namespace,service_account_name,enable_user_namespaces) fromGatewayFileSection; move to[openshell.drivers.kubernetes]only, updatinginheritable_keys()accordingly.compute_drivers: Vec<String>vs. single-active-driver reality - either narrow the type to a scalar or explicitly validate/document the "first wins" semantics.Core config types (
config.rs)0-overloaded fields toOption<T>(gateway_jwt.ttl_secs,sandbox_pids_limitin Docker/Podman,health_check_interval_secsin Podman), withNonemeaning the "special" behavior and explicit validation rejecting nonsensicalSome(0)where relevant.GatewayInterceptorConfig.max_response_bytesto align with middleware'smax_body_bytes(or vice versa) for naming consistency.Driver configs
sandbox_namespacetosandbox_label; adopt a shared typedImagePullPolicyenum; add proxy config fields matching Podman; addapp_armor_profile.sandbox_ssh_socket_pathtossh_socket_path.openshell_endpointtogrpc_endpoint; reconcilesandbox_uiddefault with Kubernetes'1000; add proxy config fields; addapp_armor_profile.Cross-cutting
guest_tls_ca/cert/keyto gateway-level only, removing the redundant per-driver copies (recommended, since only one driver is active and the paths are host-side).grpc_endpoint/openshell_endpointownership: move toward gateway-computed/auto-injected endpoint rather than per-driver configuration, with an explicit override escape hatch.docs/reference/gateway-config.mdxfor every renamed, moved, or retyped field (currently documents ~250+ TOML examples touching these fields).deploy/rpm/gateway.toml.default, Homebrew default config) that reference any changed field.Alternative Approaches Considered
0-overload replacement:Option<T>(idiomatic Rust, recommended) vs. sentinel constants (e.g.,u64::MAX) vs. separateenable_*boolean fields.Option<T>is recommended for clarity, but changes TOML ergonomics (must omit the key rather than write= 0).grpc_endpointauto-detection: full auto-injection by the gateway vs. keeping explicit per-driver config as an opt-in override when auto-detection is insufficient (e.g., custom network topologies). A pure auto-detect-only approach risks removing power-user control drivers currently rely on.compute_driverscardinality: whether OpenShell's roadmap ever intends multi-driver support. If never, collapse to a scalar field; if it's a real future possibility, keep theVectype but tighten validation/docs now.Patterns to Follow
validate()methods (e.g., Podman'svalidate_tls_config(),validate_proxy_config()inpodman/config.rs) are the established place to add validation for newOption<T>semantics or shared enum values.ImagePullPolicyenum (podman/config.rs:25-57) is the best existing example of a typed pull-policy and should be promoted toopenshell-coreas the shared base type, with driver-specific extension (e.g., Podman'sNewervariant) layered on top.#[serde(alias = "...")]is the established idiom in this codebase for backward-compatible renames.Proposed Approach
Stage the cleanup rather than attempting all 21 issues in one PR. A natural split: (1) low-risk pure field renames with serde aliases (#6, #12, #13), (2)
Option<T>conversions for0-overloaded fields with validation (#5, #7, #10, #11), (3) feature-parity additions copying existing Kubernetes/Podman patterns to Docker/VM (#9, #15, #18, #21), and (4) architectural moves requiring the most human input (#1-3, #4, #16, #17, #19, #20 - field ownership changes, endpoint auto-detection, shared enum promotion, TLS consolidation). Each stage should updatedocs/reference/gateway-config.mdx, Helm chart values, and packaged default configs in the same PR, and ship serde aliases for any renamed/relocated key to avoid breaking existing deployments outright.Scope Assessment
compute_driverscardinality) require human/product input before implementationconfig.rs,config_file.rs, 4 driver config modules) plusdocs/reference/gateway-config.mdx, Helm chart values/templates, and packaged default configs (RPM, Homebrew)refactorRisks & Open Questions
gateway.tomlfiles (hand-written, RPM/Homebrew defaults, Helm-generated). Field renames and moves are breaking changes unless serde aliases are added; type changes (0toOption<T>) change what's valid TOML syntax for a field.sandbox_uiddefault change (10001 to 1000) is a semantic behavior change, not just a rename - could affect file ownership expectations in already-running VM sandboxes. Needs explicit call-out in release notes if changed.grpc_endpointauto-detection could remove power-user control for custom network topologies if implemented as auto-detect-only rather than auto-detect-with-override.compute_driversVec vs. scalar depends on whether multi-driver support is ever on the roadmap - a product decision, not a technical one.deploy/rpm/gateway.toml.default, Homebrew config) must be updated in lockstep with any renamed/moved field, or upgrades will silently stop applying settings.Disposition Readiness
state:validatedcompute_driverscardinality) are flagged above and should be resolved duringbuild-from-issueplanning or via explicit human direction before implementation begins.Test Considerations
crates/openshell-server/src/config_file.rshas 30+ existing tests including driver-table inheritance tests (~lines 729-858) that will need updates for any moved/renamed inheritable keys.crates/openshell-core/src/config.rshas 40+ tests covering compute driver kind parsing, gateway JWT defaults, interceptor failure policies - default-value tests will need updates for0toOption<T>conversions.kubernetes/config.rs), Podman has 31 (podman/config.rs) covering TLS validation, proxy config, runtime limits - new tests needed for any fields ported to Docker/VM.config_file.rs:860) validating RPM/Homebrew default configs parse correctly - must be updated alongside any schema change.Option<T>validation logic, alias-parsing tests (old name still works, new name works, both present is an error or new-name-wins), and updated inheritance tests for any relocated keys.docs/reference/gateway-config.mdxshould be treated as a documentation contract - every schema example in the ~1051-line file touching a changed field must be updated in the same PR as the code change.Created by spike investigation.
state:validatedmeans the issue is ready for human disposition;state:needs-infomeans specific evidence is still required. A human appliesstate:acceptedor places the issue on the roadmap if OpenShell should pursue the work. To queue unattended agent planning, a human appliesagent:plan-requested; a direct request to an agent does not require that label.