feat(store): store Configs with CLI and SDK access#1417

merged29
configs/c1-redact-observations
configs/c2-config-store
openedOct 5, 2026byNNick
View on GitHubGitHub import enabledPR last imported 20d ago

Summary

Store named Configs as Environment-owned folders of text files. Services attach Configs by stable identity. CLI and SDK commands support file content, permissions, references, branches, sync, and discard.

mermaid
1flowchart LR
2 Environment --> Config
3 Config --> Files[Text, mode, UID, GID]
4 Service --> Mount[Config mount]
5 Mount --> Config

This layer stores Configs. C3 supplies runtime files, and C5 and C6 supply the dashboard workflow and user guide.

Evidence

C2 passes 71 SQLite semantic tests independently of later layers. The renamed-mount regression checks that discard restores both Working State and Saved State. The integrated core gate passes 2,251 tests, and the full PostgreSQL Store gate passes 252 tests after rebuilding a stale cached artifact without source changes.

Merge Danger

Door: One-way once Config state is stored. Old Store binaries cannot parse the new authored fields.

Blast Radius: Configuration.

New binaries still read old documents. Mount validation also checks Volume path aliases. A pre-existing Service named configs must be renamed before path-based settings work again.

Checks

23 successful
All checks passed
9185a95
Synced from GitHub

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

ReviewStatusCommitReview trigger
๐Ÿ“ Code Reviewโœ… Completed 2026-10-06T04:28:48.836219Z9bf5255New commits
โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with ๐Ÿ‘€ while any review is running, comments if it has suggestions, and reacts with ๐Ÿ‘ once all reviews finish with no findings.

Cchatgpt-codex-connectorBotreviewed ยท 1 inline threadOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01a5826a8a

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-core/src/value.rslines 582โ€“584Outdated ยท 01a5826Open thread
โ€ฆโ€ฆ@@ -569,6 +579,22 @@ validated_string_newtype!(
571581);
582+validated_string_newtype!(
583+ /// A Config's name, unique in its Environment: a lowercase DNS label.
584+ ConfigName,
Cchatgpt-codex-connectorBoton lines 582โ€“584Oct 5, 2026

P1 Badge Define Config in the domain glossary

These public types introduce Config as a new capitalized domain concept and assert that it is owned by an Environment, but core/CONTEXT.md currently defines Environment content only in terms of Services and Volumes and gives Config no meaning or lifecycle. Define Config and its relationship to Environment in the glossary before establishing these shared contract types, so later storage, CLI, and deployment work does not build on conflicting semantics.

AGENTS.md reference: AGENTS.md:L7-L7

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Fixed in 8e4efa167. core/CONTEXT.md now defines Config and Config Mount, and says how each relates to an Environment.

Cchatgpt-codex-connectorBotreviewed ยท 1 inline threadOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0280a17b69

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-core/src/config/environment.rslines 435โ€“437Outdated ยท 0280a17Open thread
โ€ฆโ€ฆ@@ -248,6 +415,46 @@ pub fn parse_environment_intent(value: Value) -> Result<SavedEnvironmentIntent,
434+ })?;
435+ let dir = attachment.mount_dir.as_str().trim_end_matches('/');
436+ for name in config.files.keys() {
437+ if !paths.insert(format!("{dir}/{name}")) {
Cchatgpt-codex-connectorBoton lines 435โ€“437Oct 5, 2026

P2 Badge Compare canonical effective mount targets

This collision check only canonicalizes trailing slashes and only collects Config file paths. Consequently, two Config mounts such as /etc and /etc/. containing the same filename are accepted even though they resolve to the same Linux target; likewise, a Volume mounted at /etc/app.yml is accepted alongside file app.yml from a Config mounted at /etc. This permits invalid Working State that will produce duplicate effective mount destinations, so build one canonical target set containing both expanded Config files and Volume mounts before admitting the document.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Fixed. /etc/.-style aliases are refused by 9e87cb8ab, which accepts only canonical mount directories. A Volume mounted at or under a Config file's path is refused by 9194c1316, which has tests in environment_intent.rs.

Cchatgpt-codex-connectorBotreviewed ยท 2 inline threadsOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5461d195f6

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-store/src/config_item/query.rslines 154โ€“159Outdated ยท 5461d19Open thread
โ€ฆโ€ฆ@@ -0,0 +1,194 @@
156+ .applied
157+ .configs
158+ .iter()
159+ .any(|applied| applied.resource_id == node.resource_id),
Cchatgpt-codex-connectorBoton lines 154โ€“159Oct 5, 2026

P1 Badge Advance Config nodes after successful deployments

After any successful deployment, this predicate still evaluates against an empty Config collection: deployments freeze only TargetNode::Service and TargetNode::Volume, and deployment::lifecycle::advance consequently inserts only Service and Volume rows into config_applied. Therefore Config listings remain deployed: false, reviews continue treating deployed Configs as new, and a staged deletion disappears from the listing instead of being compared with its deployed version. Configs need to be included in Applied State when the deployment that carries them advances.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Intended for this PR. Configs don't reach Applied State until they deploy, which is the next PR in the stack. Until then config ls shows every Config as a create. The PR body lists this under Scope.

core/crates/ployz-store/src/deployment/lowering.rsline 159Outdated ยท 5461d19Open thread
โ€ฆโ€ฆ@@ -156,7 +156,7 @@ pub(super) fn lower(
157157 },
158158 )),
159- CompiledNodeConfig::Volume(_) => None,
159+ CompiledNodeConfig::Volume(_) | CompiledNodeConfig::Config(_) => None,

P1 Badge Preserve Config snapshots during deployment lowering

When a Service mounts a Config, this arm discards the only snapshot containing the Config's files. The resulting ServiceConfig mount metadata is also ignored by lower_deployment, which destructures only settings and mounts and builds only a volume graph, so the Deploy Intent contains neither ConfigSpecs nor Config mounts. Such a deployment can report success while the container receives none of the configured files; convert the Config snapshots and Service attachment metadata into the runtime config graph instead of dropping them.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Intended for this PR. Configs are stored here but don't deploy yet. history::as_deployed strips Config mounts from what a Deployment runs, so Applied State never points at a Config it doesn't carry. Lowering Configs into the runtime config graph is the next PR in the stack.

Cchatgpt-codex-connectorBotreviewed ยท 3 inline threadsOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 634507ee1f

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-core/tests/environment_intent.rslines 149โ€“152Outdated ยท 634507eOpen thread
โ€ฆโ€ฆ@@ -90,3 +90,98 @@ fn authored_documents_reject_derived_service_fields() {
149+ assert_eq!(
150+ config["config"]["files"]["config.yml"]["content"],
151+ "host: ${{ web.HOST }}"
152+ );
Cchatgpt-codex-connectorBoton lines 149โ€“152Oct 5, 2026

P1 Badge Align the Config snapshot assertion with its wire shape

The new configs_compile_with_references_written_by_service_name test fails unconditionally: the serialized value is a ValuePart array, while this assertion expects rendered text. ConfigNodeConfig and the generated SDK contract both currently declare each file as SavedConfigFile, whose content is Array<ValuePart>. Update the assertion to validate that structured representation, or change the implementation and generated contract consistently if compiled snapshots are intended to contain text, so this test does not leave the suite red.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Stale. The test was rewritten as configs_compile_with_references_kept_by_service_lineage, which asserts the ValuePart array, and it passes.

core/crates/ployz-store/src/settings/path.rslines 185โ€“190Outdated ยท 634507eOpen thread
โ€ฆโ€ฆ@@ -131,21 +163,33 @@ pub(crate) enum Target {
187+ return Err(error::invalid(
188+ "A Config has no Settings: address it as configs.CONFIG",
189+ json!({ "example": "configs.sentry" }),
190+ ));
Cchatgpt-codex-connectorBoton lines 185โ€“190Oct 5, 2026

P2 Badge Allow restoring individual Config changes

When a Config name or file is changed alongside another file, the review builds paths such as configs.NAME.name and configs.NAME.files.PATH, then uses this parser to determine can_restore. Rejecting every Config path containing another dot marks all those rows non-restorable, so ployz discard can only reset the whole Config and unintentionally discard unrelated edits. Parse the Config's name/file subpaths and route them to field-level restoration.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Not in this PR. Every Config is a create until Configs deploy, so a file or name row has no baseline to restore, and configs.NAME is the only meaningful discard. Per-file restore needs Applied Configs, so it ships with deployment in the next PR.

core/crates/ployz/src/handlers/config_item.rslines 27โ€“29Outdated ยท 634507eOpen thread
โ€ฆโ€ฆ@@ -0,0 +1,355 @@
26+ .arg_required_else_help(true)
27+ .subcommand(
28+ store::scoped(Command::new("create").about("Create a Config; staged until you deploy"))
29+ .arg(positional("name", true).help("Config name, unique in the Environment"))
Cchatgpt-codex-connectorBoton lines 27โ€“29Oct 5, 2026

P1 Badge Add user documentation for the Config commands

This introduces the user-facing ployz config command family, including creation, file management, mounting, inspection, and deletion, but the commit changes no file under docs/user/; the current user documentation therefore does not explain the new workflow, file constraints, reference syntax, or mount semantics. Add the shipped behavior to the relevant guide and documentation navigation.

AGENTS.md reference: AGENTS.md:L6-L6

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Deferred by plan. User docs for Configs (docs/user/services/configs.md) ship in the stack PR that makes Configs deploy and appear in the dashboard. Documenting a command whose files never reach a container would describe behavior users can't use yet.

Cchatgpt-codex-connectorBotreviewed ยท 1 inline threadOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45ced1465e

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-store/src/settings/path.rsline 185Outdated ยท 45ced14Open thread
โ€ฆโ€ฆ@@ -131,21 +163,33 @@ pub(crate) enum Target {
182+ if path == "configs" {
183+ return Err(name_a_config());
184+ }
185+ if let Some(config) = path.strip_prefix("configs.") {

P1 Badge Keep legacy configs Services addressable

On an upgraded store containing a Service named configsโ€”valid before this commit because only volumes was reservedโ€”every path such as configs.replicas is now captured as the Config namespace, while the bare configs path is rejected above. Settings queries and edits, discard, and other path-based operations can therefore no longer address that persisted Service; preserve a legacy/escaped resolution or migrate these names before reserving the namespace.

AGENTS.md reference: AGENTS.md:L5-L5

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Accepted tradeoff, the same one taken when volumes was reserved. A Service named configs can still be renamed with ployz service rename, because RenameService takes a Service name, not a setting path. After that, path addressing works again.

Cchatgpt-codex-connectorBotreviewed ยท 1 inline threadOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0804616736

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-store/src/catalog.rslines 129โ€“130Outdated ยท 0804616Open thread
โ€ฆโ€ฆ@@ -126,6 +126,20 @@ fn service() -> Value {
127127 }),
128128 );
129+ properties.insert(
130+ "configs".to_owned(),
Cchatgpt-codex-connectorBoton lines 129โ€“130Oct 5, 2026

P2 Badge Bump the catalog version for the new Config setting

Adding the published SERVICE.configs.CONFIG path changes the catalog schema, but CATALOG_VERSION remains 1 despite this file's requirement to bump it whenever a published path, type, or meaning changes. Consumers using x-ployz-version therefore cannot distinguish the old catalog from one containing Config mounts; increment the version alongside this schema addition.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Not bumping. The version marks a change to an existing published path, type or meaning, and adding a path changes none of them. Commit 98a0e6a42 added the healthcheck paths and kept version 1 for the same reason. Existing consumers read the catalog exactly as before.

Cchatgpt-codex-connectorBotreviewed ยท 2 inline threadsOct 5, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9194c1316b

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-store/src/catalog.rsline 133Outdated ยท 9194c13Open thread
โ€ฆโ€ฆ@@ -126,6 +126,20 @@ fn service() -> Value {
130+ "configs".to_owned(),
131+ json!({
132+ "title": "Config mounts",
133+ "description": "Where the Service mounts each Config, by Config name, each at SERVICE.configs.CONFIG. Create a Config with `ployz config create`. Unset unmounts it: the Config and its files stay.",

P2 Badge Point catalog users to config add

This catalog text tells users to run ployz config create, but the command tree introduced by this commit exposes ployz config add and has no create subcommand. Anyone following schema or explain output reaches an unknown command, so the description should use the actual CLI spelling.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Fixed in 6c332a4a1. The catalog now says ployz config add.

core/crates/ployz-store/src/catalog.rsline 171Outdated ยท 9194c13Open thread
โ€ฆโ€ฆ@@ -149,9 +163,24 @@ fn mount() -> Value {
168+ "title": "Mount directory",
169+ "description": "The absolute directory the Config's files appear in, read-only, in the Service's containers. Unset unmounts it: the Config and its files stay.",
170+ "type": "string",
171+ "pattern": "^/",

P2 Badge Match the Config mount schema to server validation

The published schema accepts any string beginning with /, including /, /etc/app/, /etc//app, and /etc/./app, while ConfigAttachment::is_canonical_dir rejects all of these. Schema-driven forms and SDK clients can therefore approve values that the same Config Store later rejects with invalid_argument; encode the canonical-directory constraints in this schema.

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Fixed in 6c332a4a1. The schema pattern now refuses /, empty segments, . and .. segments, and a trailing /. A catalog test checks the pattern against ConfigAttachment::is_canonical_dir over the same sample set.

Cchatgpt-codex-connectorBotreviewed ยท 1 inline threadOct 6, 2026
Synced from GitHub

๐Ÿ’ก Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c332a4a11

โ„น๏ธ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with ๐Ÿ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

core/crates/ployz-core/src/config/environment.rslines 446โ€“449Outdated ยท 6c332a4Open thread
โ€ฆโ€ฆ@@ -230,6 +417,43 @@ pub fn parse_environment_intent(value: Value) -> Result<SavedEnvironmentIntent,
446+ if service.config_attachments.iter().any(|config| {
447+ service.volume_attachments.iter().any(|volume| {
448+ volume.mount_path.as_str().trim_end_matches('/') == config.mount_dir.as_str()
449+ })
Cchatgpt-codex-connectorBoton lines 446โ€“449Oct 6, 2026

P2 Badge Canonicalize Volume targets before collision checks

Fresh evidence after the canonical-Config fix: volume::attach still accepts any absolute ContainerPath, but this comparison normalizes only trailing slashes. A Service can therefore mount a Volume at /etc//sentry or /etc/./sentry and a Config at /etc/sentry; both this check and mounted_files accept the document even though the paths resolve to the same container location, violating the Config Mount invariant that each directory holds one mount. Canonicalize both target types before comparing them, including the Volume-versus-Config-file checks.

AGENTS.md reference: AGENTS.md:L7-L7

Useful? React with ๐Ÿ‘ย / ๐Ÿ‘Ž.

Fixed in 9bf5255aa. Both Volume checks now compare the Volume path after resolving //, . and .., the way the runtime resolves a mount target. The test covers /etc//a, /etc/./a, /etc/x/../a and //etc/..

Added 27 commits

  • feat(core): add ConfigName and ConfigFileName1728d03
  • test: Config store, branch copy, sync rows and old-state parse1c5d784
  • sdk: regenerate payloads284d4aa
  • wip(store): Config file refs in rules problems, catalog configs property2f8ed54
  • style: rustfmt the Volume target check305100b
  • perf(core): find a template's last }} once3e99b44
  • fix(store): an exact Config file name picks only that file4637900
  • wip(store): config_item module, commands, queries and match-arm sweep4687391
  • docs+sdk: Config and Config Mount in CONTEXT, regenerated SDK, node_configs smoke485dba2
  • fix(store): a Deployment runs Services without Config Mounts until Configs deploy538a14a
  • feat(cli): ployz config; fix review of compiled configs family5d16359
  • fix(core): compare Config files without indexing6193f95
  • fix(cli): config output goes through ui tables, fields and stream6c05c69
  • fix(cli): config errors use the usage exit Failure now carries7547b37
  • refactor: trim Config store code and comments758bc55
  • fix: canonical Config mount directories; a Config can't hold a file and a folder of one name7d855d7
  • fix: ployz config verbs match the house CLI; dashboard refreshes Config views8a8c344
  • fix(cli): binary stdin gets the binary-file error90fbc34
  • fix(store): catalog names config add and matches canonical mount dirsa269f4c
  • wip(store): config mounts in review, discard and settingsa5bb5be
  • wip(store): Config refs count live, Own Copy brings Configs, land introduces thema669528
  • fix(core): a Volume can't mount where a Config file sitsac332dd
  • wip(core): thread Config through environment intent and syncbc7a29b
  • fix(store): discarding a renamed Config's mount restores it by IDddeef6c
  • fix: clippy clean under -D warningse109976
  • fix(core): compare Volume targets with Config mounts after resolving . and ..fa04f93
  • fix(store): discard renamed mounts by one resolved resource identity04322ea
Synced from GitHub

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Synced from GitHub

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Added 2 commits

  • fix(store): select Config queries and discards by exact identity373666d
  • fix(store): resolve Config mounts by exact identity9185a95
Synced from GitHub

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.