Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
69e49a4
feat(helm): add generic gateway TOML serializer
gmenher Sep 15, 2026
fb0128e
docs(architecture): define Helm gateway configuration boundary
gmenher Sep 15, 2026
c285f73
feat(helm): define default gateway configuration map
gmenher Sep 15, 2026
ad1726d
refactor(helm): render gateway config from values map
gmenher Sep 15, 2026
fd35649
test(helm): cover generic gateway TOML rendering
gmenher Sep 15, 2026
b02e6dc
feat(helm): protect gateway config secret boundary
gmenher Sep 15, 2026
efa06eb
docs(helm): classify legacy gateway configuration values
gmenher Sep 15, 2026
799b237
refactor(helm): derive dual-use resources from gateway config
gmenher Sep 15, 2026
556eae2
test(helm): migrate gateway config scenarios
gmenher Sep 15, 2026
81b8f2d
refactor(helm): derive credential resources from gateway config
gmenher Sep 15, 2026
4cb0e8f
refactor(helm): migrate gateway config overlays
gmenher Sep 15, 2026
0731b84
refactor(helm): remove supervisor and proxy shadow values
gmenher Sep 15, 2026
0186da1
refactor(helm): remove gateway configuration shadow values
gmenher Sep 15, 2026
5518fbf
refactor(helm): align runtime config with resources
gmenher Sep 15, 2026
1ad831f
docs(helm): add gateway config migration guide
gmenher Sep 15, 2026
d416a74
test(helm): validate rendered gateway config
gmenher Sep 15, 2026
9cccaad
test(helm): cover config resource coherence
gmenher Sep 15, 2026
57a5b8d
fix(helm): make Kubernetes E2E deployable
gmenher Sep 15, 2026
e6b1c2f
fix(e2e): pass host aliases through gateway config
gmenher Sep 16, 2026
2104de4
docs(e2e): reference gateway config host alias
gmenher Sep 16, 2026
81a51ba
fix(helm): enforce gateway resource ownership
gmenher Sep 16, 2026
b0bdc28
test(e2e): cover chart host gateway input
gmenher Sep 16, 2026
b93bacd
fix(helm): address gateway config review findings
gmenher Sep 16, 2026
825acdb
fix(helm): repair RFC 0012 migration integration
gmenher Sep 16, 2026
c16f4f0
fix(e2e): align Kubernetes parity with RFC 0012
gmenher Sep 16, 2026
716c114
fix(helm): complete gateway config migration
gmenher Sep 18, 2026
46d70d8
test(e2e): handle paginated provider lists
gmenher Sep 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ OpenShell is built agent-first. Issues should include a user story, problem stat

OpenShell collects anonymous telemetry to help improve the project for developers. This data is not used to track individual user behavior. It helps us understand aggregate usage of sandbox, provider, and policy workflows so we can prioritize product improvements and share usage trends with the community.

Disable telemetry at runtime by setting `OPENSHELL_TELEMETRY_ENABLED=false` on the gateway deployment. For Helm installs, set `server.telemetryEnabled=false`. OpenShell propagates this deployment setting into sandbox supervisor environments so sandbox-side telemetry collection is disabled as well.
Disable telemetry at runtime by setting `OPENSHELL_TELEMETRY_ENABLED=false` on the gateway deployment. For Helm installs, set `server.telemetryEnabled=false`; the chart wires that deployment setting into the gateway and sandbox supervisor environments.

You can also compile telemetry out entirely. Telemetry support is a default-on `telemetry` Cargo feature, and each crate that carries it also defines a `defaults-without-telemetry` alias covering every other default feature. Build telemetry-free artifacts with `--no-default-features --features defaults-without-telemetry`:

Expand Down
64 changes: 64 additions & 0 deletions architecture/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,70 @@ the gateway listener uses TLS; package-managed local TLS can supply that bundle.
Kubernetes instead projects guest credentials through its configured Secret.
The gateway validates this requirement before constructing the selected driver.

### Helm configuration boundary

The Kubernetes Helm chart exposes `gatewayConfig` as its non-secret gateway
application-configuration boundary. Each top-level map key names a TOML table,
and the chart serializes its fields into the mounted `gateway.toml` ConfigMap.
This keeps the chart independent of individual gateway fields: a new non-secret
gateway option does not require a chart template change.

Secret material never belongs in `gatewayConfig` or the ConfigMap. Database
URLs, credentials, private keys, and equivalent values use Kubernetes Secrets
through the chart's supported environment-variable, file, or volume wiring.
Helm serializes unknown fields generically, so it cannot determine whether an
arbitrary string such as `api_token` is confidential. It rejects
`database_url`, inline URL credentials, and PEM private keys, but is not a
general secret scanner; operators must keep all secret material out of this
map.
The gateway's normal precedence still applies: CLI flags and `OPENSHELL_*`
environment variables override the mounted TOML file.

The chart rejects the gateway's `database_url` file field and unambiguous
inline credential or PEM private-key strings before rendering a ConfigMap.
It validates Secret references as Kubernetes Secret names, but never reads or
copies referenced Secret data into `gateway.toml`.

The serializer has a deterministic YAML-to-TOML contract. YAML `null` fields
are omitted; `null` array members are rejected because TOML has no equivalent.
Strings, booleans, integers, and floats preserve their types. Scalar arrays
become TOML arrays, maps become inline tables, and arrays of maps become arrays
of inline tables. Keys are ordered alphabetically, so equivalent input produces
the same ConfigMap checksum. Helm `tpl` expressions are evaluated only in
string values, never in keys or YAML structure.

The implementation intentionally uses only long-standing Helm 3 template and
Sprig functions (tpl, kindIs, keys, sortAlpha, splitList, quote, and toJson);
it does not rely on a Helm-specific TOML encoder. This preserves the chart's
documented Helm 3 compatibility while making the serialization rules explicit
in the chart itself.

Helm retains ownership of values that create or modify Kubernetes resources,
including Services, workloads, probes, Secrets, certificate resources, Routes,
RBAC, NetworkPolicies, and mounts. When one of those inputs also determines a
gateway runtime value, the chart derives one from the other rather than
exposing two independently configurable settings.

### Legacy Helm value inventory

The former hand-written ConfigMap template read the following values. This
inventory is the migration boundary for `gatewayConfig`; it prevents a legacy
knob from silently surviving as a second source of truth.

| Classification | Legacy values read by the ConfigMap | Migration |
| --- | --- | --- |
| Application-only | `server.name`, `server.logLevel`, `server.enableLoopbackServiceHttp`, `server.policyValidationFailureMode`, `server.grpcRateLimit.requests`, `server.grpcRateLimit.windowSeconds` | `openshell.gateway`; defaulted runtime values are now in `gatewayConfig`, optional values are omitted unless the operator adds them. |
| Application-only | `server.otlp.endpoint`, `server.otlp.serviceName`, `server.auth.allowUnauthenticatedUsers`, `server.oidc.{issuer,audience,jwksTtl,rolesClaim,adminRole,userRole,scopesClaim}` | `openshell.gateway.{otlp,auth,oidc}`. Empty optional tables are not rendered. |
| Application-only | `server.sandboxImage`, `server.sandboxImagePullPolicy`, `server.sandboxImagePullSecrets`, `server.workspaceDefaultStorageSize`, `server.workspaceStorageClass`, `server.defaultRuntimeClassName`, `server.enableUserNamespaces` | `openshell.drivers.kubernetes`; `server.appArmorProfile` is removed by RFC 0012. |
| Application-only | `server.drivers.kubernetes.{workspaceMode,operatorNamespaceLabel,operatorNamespaceFile}`, `server.sandboxJwt.{gatewayId,ttlSecs,k8sSaTokenTtlSecs}` | `openshell.drivers.kubernetes`, `.managed_ssh_ingress`, and `openshell.gateway.gateway_jwt`. `supervisor.topology` and `supervisor.sidecar.*` are removed by RFC 0012. |
| Application-only | `server.credentialDrivers.kubernetesSecrets.allowReferenceNamespace`, `server.credentialDrivers.vault.{address,mount,kvVersion,authMethod,role,kubernetesAuthMount,serviceAccountTokenPath,tokenPath,timeoutSecs}`, `server.providerTokenGrants.spiffe.{enabled,workloadApiSocketPath}` | The corresponding `openshell.drivers.kubernetes` or `openshell.credential_drivers.*` table. Credential-driver and SPIFFE runtime configuration selects any required Helm resources; Secret names and keys remain references, never Secret data. |
| Application-only | `upstreamProxy.{url,noProxy,authSecret.name,authSecret.key,authAllowInsecure,connectByHostname}`, `sandboxRuntime.image.*`, `supervisor.image.*`, `supervisor.sandboxRuntime.*` | `openshell.drivers.kubernetes.{https_proxy,no_proxy,proxy_auth_*,sandbox_runtime_image,supervisor_image,sandbox_runtime}`. |
| Deployment-only | `server.dbUrl`, `server.externalDbSecret` | Gateway process args and `OPENSHELL_DB_URL` Secret reference. They are never TOML. |
| Deployment-only | `server.credentialStorage.existingSecret`, `server.sandboxJwt.signingSecretName`, `server.tls.certSecretName`, `server.tls.clientCaSecretName` | Secret creation, mounting, and environment wiring. TOML contains only stable paths or an environment-variable name. |
| Dual-use | `service.{port,healthPort,metricsPort}`, `server.disableTls`, `server.tls.clientTlsSecretName` | The chart owns the Service, workload ports, mounts, and Secret references; `gatewayConfig` derives listener addresses and runtime references from them. |
| Dual-use | `certManager.{enabled,serverIssuerRef.name,serverDnsNames}`, `pkiInitJob.{enabled,serverDnsNames}` | Certificate resources and mounts remain chart-owned; the TLS table and server SANs are derived from their selected certificate source. |
| Dual-use | `networkPolicy.enabled`, `server.hostGatewayIP` | The chart owns NetworkPolicies and pod host aliases. `server.hostGatewayIP` is the sole input for both host aliases and the derived `host_gateway_ip` runtime field. |

## Protocol and Auth

Gateway validation and concurrency errors use the standard rich gRPC error
Expand Down
11 changes: 11 additions & 0 deletions crates/openshell-server/src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,17 @@ mod tests {
}
}

#[test]
fn gateway_rate_limits_reject_negative_values_during_toml_parsing() {
for field in ["grpc_rate_limit_requests", "grpc_rate_limit_window_seconds"] {
let tmp = write_tmp(&format!("[openshell.gateway]\n{field} = -1\n"));
assert!(
matches!(load(tmp.path()), Err(ConfigFileError::Parse { .. })),
"{field} must reject negative values because gateway rate limits are unsigned"
);
}
}

#[test]
fn canonical_compute_driver_is_singular() {
let file: ConfigFile = toml::from_str(
Expand Down
Loading
Loading