chore(deps): modernize YAML parser dependencies
Problem Statement
OpenShell currently depends directly on serde_yml, whose latest release is marked deprecated and points users to noyalib. The workspace also still has transitive serde_yaml / unsafe-libyaml exposure through regorus's YAML feature and the current kube client stack. We should modernize YAML parsing dependencies without changing policy semantics, provider profile behavior, or fail-closed sandbox policy loading.
Technical Context
OpenShell uses YAML in several independent areas: sandbox policies, supervisor OPA data loading, provider profiles, router config, CLI YAML output, and prover input files. The main security-sensitive flow parses policy YAML into typed data or JSON before it reaches regorus; OpenShell does not rely on regorus YAML builtins for normal policy evaluation.
serde_yml is not an ideal long-term dependency because the latest published release is explicitly marked deprecated/unmaintained and is only a compatibility shim over noyalib. Keeping serde_yml as the public dependency name would leave OpenShell anchored to an abandoned crate API even if the implementation is forwarded underneath. If noyalib is selected, OpenShell should prefer depending on it directly or through a deliberate short-term Cargo rename; if kube 4 is selected, serde-saphyr may be preferable because it avoids carrying a second YAML parser.
Two changes look small and directly relevant: replace the direct serde_yml dependency with a maintained parser, and remove the unused yaml feature from regorus. serde-saphyr should be evaluated as a first-class replacement candidate, especially if the kube stack is upgraded to kube 4 because kube-client then already brings serde-saphyr into the dependency graph. A kube-client upgrade also removes a serde_yaml path, but that is a separate major dependency migration with source API changes.
Affected Components
| Component |
Key Files |
Role |
| Workspace dependencies |
Cargo.toml |
Declares direct serde_yml, kube, kube-runtime, and k8s-openapi versions. |
| Policy parser |
crates/openshell-policy/src/lib.rs |
Parses sandbox policy YAML into proto and serializes proto back to YAML. |
| Supervisor policy engine |
crates/openshell-supervisor-network/src/opa.rs |
Parses YAML policy data into JSON, preprocesses it, and feeds JSON into regorus. |
| Provider profiles |
crates/openshell-providers/src/profiles.rs |
Parses and exports provider profile YAML. |
| CLI output |
crates/openshell-cli/src/output.rs |
Emits YAML output for list/detail commands. |
| Router config |
crates/openshell-router/src/config.rs |
Loads router YAML config files. |
| Prover inputs |
crates/openshell-prover/src/*.rs |
Parses policy, credentials, registry, and accepted-risk YAML files. |
| Kubernetes driver/server |
crates/openshell-driver-kubernetes/, crates/openshell-server/ |
Uses kube 0.90, whose kube-client depends on deprecated serde_yaml. |
Technical Investigation
Architecture Overview
openshell-policy is the canonical policy YAML boundary. It parses YAML strings through serde_yml::from_str and serializes through serde_yml::to_string. The sandbox then loads policy YAML from /etc/openshell/policy.yaml or the legacy path, validates it, and falls back to restrictive defaults on invalid policy.
The supervisor network policy path uses regorus as the embedded Rego evaluator, but OpenShell preprocesses YAML itself. OpaEngine::from_files and OpaEngine::from_strings parse YAML policy data into serde_json::Value, normalize and validate it, then call regorus::Engine::add_data_json. The proto runtime path skips YAML entirely by converting typed policy data to JSON before loading regorus.
Provider profiles, router config, CLI output, and prover inputs are separate YAML call sites. They mostly use from_str, to_string, serde_yml::Error, and serde_yml::Value.
Code References
| Location |
Description |
Cargo.toml:73 |
Workspace direct dependency is serde_yml = "0.0.12". |
Cargo.toml:111 |
Workspace uses kube = "0.90" and kube-runtime = "0.90". |
crates/openshell-supervisor-network/Cargo.toml:28 |
Enables regorus feature set ["std", "arc", "glob", "yaml"]; yaml brings in serde_yaml. |
crates/openshell-policy/src/lib.rs:545 |
Parses sandbox policy YAML through serde_yml::from_str. |
crates/openshell-policy/src/lib.rs:557 |
Serializes sandbox policy YAML through serde_yml::to_string. |
crates/openshell-supervisor-network/src/opa.rs:140 |
Loads Rego policy files and YAML data files. |
crates/openshell-supervisor-network/src/opa.rs:194 |
Runtime proto path converts policy to JSON and loads regorus with add_data_json. |
crates/openshell-supervisor-network/src/opa.rs:720 |
Parses YAML policy data into serde_json::Value before preprocessing. |
crates/openshell-providers/src/profiles.rs:39 |
Provider profile parse error is currently typed as serde_yml::Error. |
crates/openshell-providers/src/profiles.rs:931 |
Provider profile YAML parse/export calls. |
crates/openshell-router/src/config.rs:88 |
Router config YAML parsing. |
crates/openshell-prover/src/policy.rs:63 |
Prover uses serde_yml::Value for ignored policy sections. |
crates/openshell-cli/src/output.rs:71 |
CLI YAML output uses serde_yml::to_string. |
Current Dependency Paths
cargo tree -i serde_yml on main shows direct use by:
openshell-cli
openshell-policy
openshell-prover
openshell-providers
openshell-router
openshell-supervisor-network
cargo tree -i serde_yaml on main shows deprecated serde_yaml v0.9.34+deprecated through:
kube-client v0.90.0, via kube and kube-runtime
regorus v0.9.1, via the enabled yaml feature
unsafe-libyaml v0.2.11 is only under serde_yaml.
Feasibility
Replacing direct serde_yml is medium-low complexity if using noyalib in compatibility mode. A temporary compile check passed with this workspace dependency shape:
serde_yml = { package = "noyalib", version = "0.0.8", default-features = false, features = ["std", "compat-serde-yaml"] }
The check covered the direct users:
mise exec -- cargo check -p openshell-policy -p openshell-prover -p openshell-providers -p openshell-router -p openshell-supervisor-network -p openshell-cli
Removing the regorus yaml feature is low complexity. No OpenShell Rego policy or Rust call site uses yaml.* builtins, and OpenShell already feeds JSON into regorus. A temporary compile check of openshell-supervisor-network passed after removing only that feature.
Using serde-saphyr as the direct replacement may be a better long-term outcome if kube 4 is adopted. That would avoid carrying both serde-saphyr through kube and noyalib for OpenShell's own YAML parsing. It likely requires more code churn than the noyalib compatibility alias because OpenShell currently references serde_yml::Error and serde_yml::Value, so it needs a behavior and API compatibility pass before selection.
A temporary compatibility check showed that serde-saphyr can replace the direct serde_yml package with small source changes. The experiment used this workspace dependency alias:
serde_yml = { package = "serde-saphyr", version = "0.0.28" }
The required source changes were:
- Replace the prover's ignored
serde_yml::Value fields with serde::de::IgnoredAny for landlock and process, avoiding a parser-specific dynamic value type.
- Add a provider-profile YAML serialization error variant for
serde_yml::ser::Error, because serde-saphyr has distinct deserialize and serialize error types.
- Serialize provider-profile slices as
serde_yml::to_string(&profiles) because serde-saphyr::to_string requires a sized type parameter.
Focused checks passed in the temp worktree:
mise exec -- cargo check -p openshell-policy -p openshell-prover -p openshell-providers -p openshell-router -p openshell-supervisor-network -p openshell-cli
mise exec -- cargo test -p openshell-policy -p openshell-providers -p openshell-router -p openshell-prover
mise exec -- cargo test -p openshell-supervisor-network opa
mise exec -- cargo test -p openshell-cli output
After the alias, cargo tree -i serde_yml and cargo tree -i libyml no longer matched any packages, and cargo tree -i serde-saphyr showed the existing direct YAML users now routed through serde-saphyr. Deprecated serde_yaml still remained through kube-client 0.90 and regorus's yaml feature, so those remain separate cleanup steps.
Upgrading kube should be split into a separate issue. A temporary kube 4 check showed it removes the kube-client serde_yaml path and uses serde-saphyr, but requires coordinated dependency and source changes: k8s-openapi must move with kube, watcher event variants changed, and Kubernetes timestamp types now use jiff.
Alternative Approaches Considered
noyalib
Best near-term candidate. It is the migration path named by the deprecated serde_yml release, is pure Rust, and can be used with a Cargo package rename to keep current serde_yml:: call sites initially.
serde-saphyr
Pure-Rust Serde YAML library and used by newer kube-client. This is the strongest consolidation candidate if OpenShell upgrades to kube 4 because it avoids introducing another parser while kube already depends on serde-saphyr. A temporary compile and focused-test pass shows it is feasible with small code changes, but it still needs final review of YAML edge-case behavior, output formatting, and whether the temporary Cargo alias should become a direct serde_saphyr:: migration or an internal YAML adapter.
saneyaml
Pure-Rust, Serde-compatible, and config-focused. It may fit the domain, but it is young and should be tested against OpenShell policy/profile fixtures before adoption.
yaml_serde
Maintained by the YAML Organization with a familiar API, but it depends on libyaml-rs. This may address unmaintained-crate concerns, but not libyaml-style parser concerns.
serde_norway, serde_yaml_ng, serde_yaml_neo
Not preferred for this concern. They still rely on unsafe-libyaml variants.
Proposed Approach
Treat direct YAML parser replacement and regorus feature cleanup as one dependency-modernization issue. Evaluate both noyalib compatibility mode and serde-saphyr against the existing YAML round-trip, provider profile, router config, prover, and supervisor policy tests. Prefer serde-saphyr if kube 4 is accepted and behavior parity is good, because it would standardize OpenShell on the same maintained parser already used by kube-client; otherwise use noyalib compatibility mode to minimize code churn. Remove regorus's unused yaml feature in the same change if tests confirm no Rego YAML builtins are required.
Handle the kube-client serde_yaml path in a separate kube upgrade issue because that migration changes kube APIs and Kubernetes type versions.
Scope Assessment
- Complexity: Medium
- Confidence: Medium-high for direct
serde_yml replacement and regorus feature cleanup; medium for behavior parity until fixture tests are run.
- Estimated files to change: 2-8 for the direct dependency cleanup, depending on whether the implementation uses a Cargo rename or explicit module rename.
- Issue type:
chore
Risks & Open Questions
- Should OpenShell prioritize compatibility mode with
noyalib, or standardize on serde-saphyr if kube 4 brings it into the dependency graph?
- If
serde-saphyr is chosen, should OpenShell rename call sites directly or introduce a small internal YAML adapter module to isolate parser APIs?
- Does OpenShell need to preserve exact YAML output formatting, ordering, quoting, and error messages for CLI/provider/profile workflows?
- What behavior should be expected for duplicate keys, anchors/aliases, merge keys, tags, non-string map keys, numeric coercion, and multi-document YAML?
- Invalid sandbox policy YAML must continue to fail closed to restrictive defaults.
- If the parser changes error text, tests that assert specific error messages may need to be updated carefully.
- Kube should remain out of scope unless the issue is expanded into a dependency epic.
Test Considerations
- Run focused cargo checks for all direct parser users:
mise exec -- cargo check -p openshell-policy -p openshell-prover -p openshell-providers -p openshell-router -p openshell-supervisor-network -p openshell-cli
- Run policy parser tests in
crates/openshell-policy/src/lib.rs, especially YAML parse and round-trip coverage.
- Run supervisor-network OPA policy preprocessing tests in
crates/openshell-supervisor-network/src/opa.rs.
- Run provider profile YAML import/export tests in
crates/openshell-providers/src/profiles.rs and CLI provider integration tests that assert YAML output.
- Run router config parse-error tests in
crates/openshell-router/src/config.rs.
- Run sandbox policy-load tests that confirm invalid YAML falls back safely.
- Add targeted parser behavior tests if the selected replacement differs for duplicate keys, anchors, merge keys, or multi-document YAML.
Documentation Impact
Architecture docs describe policy YAML and runtime flow, but not parser implementation. A compatible dependency swap probably does not require architecture or published docs changes. Update docs/reference/policy-schema.mdx only if accepted YAML syntax or serialization semantics change.
docs/reference/gateway-config.mdx is not affected unless a later kube upgrade changes gateway TOML fields, driver-specific config options, config defaults, or Helm rendering of gateway.toml.
LSM Impact
No Linux Security Module impact is expected for parser dependency changes. The existing OPA proto path includes /proc/<pid>/root symlink resolution and remains LSM-sensitive, but this spike does not change that behavior.
Created by spike investigation. Use build-from-issue to plan and implement after human review.
chore(deps): modernize YAML parser dependencies
Problem Statement
OpenShell currently depends directly on
serde_yml, whose latest release is marked deprecated and points users tonoyalib. The workspace also still has transitiveserde_yaml/unsafe-libyamlexposure throughregorus's YAML feature and the current kube client stack. We should modernize YAML parsing dependencies without changing policy semantics, provider profile behavior, or fail-closed sandbox policy loading.Technical Context
OpenShell uses YAML in several independent areas: sandbox policies, supervisor OPA data loading, provider profiles, router config, CLI YAML output, and prover input files. The main security-sensitive flow parses policy YAML into typed data or JSON before it reaches
regorus; OpenShell does not rely onregorusYAML builtins for normal policy evaluation.serde_ymlis not an ideal long-term dependency because the latest published release is explicitly marked deprecated/unmaintained and is only a compatibility shim overnoyalib. Keepingserde_ymlas the public dependency name would leave OpenShell anchored to an abandoned crate API even if the implementation is forwarded underneath. Ifnoyalibis selected, OpenShell should prefer depending on it directly or through a deliberate short-term Cargo rename; if kube 4 is selected,serde-saphyrmay be preferable because it avoids carrying a second YAML parser.Two changes look small and directly relevant: replace the direct
serde_ymldependency with a maintained parser, and remove the unusedyamlfeature fromregorus.serde-saphyrshould be evaluated as a first-class replacement candidate, especially if the kube stack is upgraded to kube 4 because kube-client then already bringsserde-saphyrinto the dependency graph. A kube-client upgrade also removes aserde_yamlpath, but that is a separate major dependency migration with source API changes.Affected Components
Cargo.tomlserde_yml, kube, kube-runtime, and k8s-openapi versions.crates/openshell-policy/src/lib.rscrates/openshell-supervisor-network/src/opa.rsregorus.crates/openshell-providers/src/profiles.rscrates/openshell-cli/src/output.rscrates/openshell-router/src/config.rscrates/openshell-prover/src/*.rscrates/openshell-driver-kubernetes/,crates/openshell-server/serde_yaml.Technical Investigation
Architecture Overview
openshell-policyis the canonical policy YAML boundary. It parses YAML strings throughserde_yml::from_strand serializes throughserde_yml::to_string. The sandbox then loads policy YAML from/etc/openshell/policy.yamlor the legacy path, validates it, and falls back to restrictive defaults on invalid policy.The supervisor network policy path uses
regorusas the embedded Rego evaluator, but OpenShell preprocesses YAML itself.OpaEngine::from_filesandOpaEngine::from_stringsparse YAML policy data intoserde_json::Value, normalize and validate it, then callregorus::Engine::add_data_json. The proto runtime path skips YAML entirely by converting typed policy data to JSON before loadingregorus.Provider profiles, router config, CLI output, and prover inputs are separate YAML call sites. They mostly use
from_str,to_string,serde_yml::Error, andserde_yml::Value.Code References
Cargo.toml:73serde_yml = "0.0.12".Cargo.toml:111kube = "0.90"andkube-runtime = "0.90".crates/openshell-supervisor-network/Cargo.toml:28regorusfeature set["std", "arc", "glob", "yaml"];yamlbrings inserde_yaml.crates/openshell-policy/src/lib.rs:545serde_yml::from_str.crates/openshell-policy/src/lib.rs:557serde_yml::to_string.crates/openshell-supervisor-network/src/opa.rs:140crates/openshell-supervisor-network/src/opa.rs:194regoruswithadd_data_json.crates/openshell-supervisor-network/src/opa.rs:720serde_json::Valuebefore preprocessing.crates/openshell-providers/src/profiles.rs:39serde_yml::Error.crates/openshell-providers/src/profiles.rs:931crates/openshell-router/src/config.rs:88crates/openshell-prover/src/policy.rs:63serde_yml::Valuefor ignored policy sections.crates/openshell-cli/src/output.rs:71serde_yml::to_string.Current Dependency Paths
cargo tree -i serde_ymlonmainshows direct use by:openshell-cliopenshell-policyopenshell-proveropenshell-providersopenshell-routeropenshell-supervisor-networkcargo tree -i serde_yamlonmainshows deprecatedserde_yaml v0.9.34+deprecatedthrough:kube-client v0.90.0, viakubeandkube-runtimeregorus v0.9.1, via the enabledyamlfeatureunsafe-libyaml v0.2.11is only underserde_yaml.Feasibility
Replacing direct
serde_ymlis medium-low complexity if usingnoyalibin compatibility mode. A temporary compile check passed with this workspace dependency shape:The check covered the direct users:
mise exec -- cargo check -p openshell-policy -p openshell-prover -p openshell-providers -p openshell-router -p openshell-supervisor-network -p openshell-cliRemoving the
regorusyamlfeature is low complexity. No OpenShell Rego policy or Rust call site usesyaml.*builtins, and OpenShell already feeds JSON intoregorus. A temporary compile check ofopenshell-supervisor-networkpassed after removing only that feature.Using
serde-saphyras the direct replacement may be a better long-term outcome if kube 4 is adopted. That would avoid carrying bothserde-saphyrthrough kube andnoyalibfor OpenShell's own YAML parsing. It likely requires more code churn than thenoyalibcompatibility alias because OpenShell currently referencesserde_yml::Errorandserde_yml::Value, so it needs a behavior and API compatibility pass before selection.A temporary compatibility check showed that
serde-saphyrcan replace the directserde_ymlpackage with small source changes. The experiment used this workspace dependency alias:The required source changes were:
serde_yml::Valuefields withserde::de::IgnoredAnyforlandlockandprocess, avoiding a parser-specific dynamic value type.serde_yml::ser::Error, becauseserde-saphyrhas distinct deserialize and serialize error types.serde_yml::to_string(&profiles)becauseserde-saphyr::to_stringrequires a sized type parameter.Focused checks passed in the temp worktree:
After the alias,
cargo tree -i serde_ymlandcargo tree -i libymlno longer matched any packages, andcargo tree -i serde-saphyrshowed the existing direct YAML users now routed throughserde-saphyr. Deprecatedserde_yamlstill remained through kube-client 0.90 andregorus'syamlfeature, so those remain separate cleanup steps.Upgrading kube should be split into a separate issue. A temporary kube 4 check showed it removes the kube-client
serde_yamlpath and usesserde-saphyr, but requires coordinated dependency and source changes:k8s-openapimust move with kube, watcher event variants changed, and Kubernetes timestamp types now usejiff.Alternative Approaches Considered
noyalibBest near-term candidate. It is the migration path named by the deprecated
serde_ymlrelease, is pure Rust, and can be used with a Cargo package rename to keep currentserde_yml::call sites initially.serde-saphyrPure-Rust Serde YAML library and used by newer
kube-client. This is the strongest consolidation candidate if OpenShell upgrades to kube 4 because it avoids introducing another parser while kube already depends onserde-saphyr. A temporary compile and focused-test pass shows it is feasible with small code changes, but it still needs final review of YAML edge-case behavior, output formatting, and whether the temporary Cargo alias should become a directserde_saphyr::migration or an internal YAML adapter.saneyamlPure-Rust, Serde-compatible, and config-focused. It may fit the domain, but it is young and should be tested against OpenShell policy/profile fixtures before adoption.
yaml_serdeMaintained by the YAML Organization with a familiar API, but it depends on
libyaml-rs. This may address unmaintained-crate concerns, but not libyaml-style parser concerns.serde_norway,serde_yaml_ng,serde_yaml_neoNot preferred for this concern. They still rely on
unsafe-libyamlvariants.Proposed Approach
Treat direct YAML parser replacement and
regorusfeature cleanup as one dependency-modernization issue. Evaluate bothnoyalibcompatibility mode andserde-saphyragainst the existing YAML round-trip, provider profile, router config, prover, and supervisor policy tests. Preferserde-saphyrif kube 4 is accepted and behavior parity is good, because it would standardize OpenShell on the same maintained parser already used by kube-client; otherwise usenoyalibcompatibility mode to minimize code churn. Removeregorus's unusedyamlfeature in the same change if tests confirm no Rego YAML builtins are required.Handle the kube-client
serde_yamlpath in a separate kube upgrade issue because that migration changes kube APIs and Kubernetes type versions.Scope Assessment
serde_ymlreplacement andregorusfeature cleanup; medium for behavior parity until fixture tests are run.choreRisks & Open Questions
noyalib, or standardize onserde-saphyrif kube 4 brings it into the dependency graph?serde-saphyris chosen, should OpenShell rename call sites directly or introduce a small internal YAML adapter module to isolate parser APIs?Test Considerations
mise exec -- cargo check -p openshell-policy -p openshell-prover -p openshell-providers -p openshell-router -p openshell-supervisor-network -p openshell-clicrates/openshell-policy/src/lib.rs, especially YAML parse and round-trip coverage.crates/openshell-supervisor-network/src/opa.rs.crates/openshell-providers/src/profiles.rsand CLI provider integration tests that assert YAML output.crates/openshell-router/src/config.rs.Documentation Impact
Architecture docs describe policy YAML and runtime flow, but not parser implementation. A compatible dependency swap probably does not require architecture or published docs changes. Update
docs/reference/policy-schema.mdxonly if accepted YAML syntax or serialization semantics change.docs/reference/gateway-config.mdxis not affected unless a later kube upgrade changes gateway TOML fields, driver-specific config options, config defaults, or Helm rendering ofgateway.toml.LSM Impact
No Linux Security Module impact is expected for parser dependency changes. The existing OPA proto path includes
/proc/<pid>/rootsymlink resolution and remains LSM-sensitive, but this spike does not change that behavior.Created by spike investigation. Use
build-from-issueto plan and implement after human review.