Repository navigation
Give every installation setting one home in config.json, applied with parsar - #151
Merged
Merged
Conversation
A new installation writes <install>/config.json, the only file an operator edits, with every setting that applies to its mode. The installer flags only seed it. deploy/install/config.schema.json describes it; config_model.py validates the small keyword subset the schema uses and a test keeps the schema inside that subset. The installation directory now holds secrets/ (one copy of each secret), state.json (identity and install facts) and generated/, which parsar apply derives from config.json: compose.json and core.env without secrets, the Core key digest file, the native unit, the settings snapshot Core serves at GET /core/v1/installation and runtime-history.json when set. Core reads the new AGENTS_API_PUBLIC_URL, AGENTS_API_DATABASE_PASSWORD_FILE and AGENTS_API_SETTINGS_FILE; the retired AGENTS_API_DAEMON_WS_URL and AGENTS_API_CONFIG_FILE are no longer generated. The parsar zipapp in the installation directory runs status, start, stop, apply and rotate-core-key without the bundle. apply refuses hand-edited generated files, changed fixed fields and changed secrets, confirms a public URL change that strands bound nodes, recreates only the services whose inputs changed (a Compose label carries each service's input digest) and restores the previous files when Core rejects the new ones. rotate-core-key cuts over at once. install.sh reruns read config.json, reject flags and repair; --status and --stop name their replacements. install.sh --convert moves an installation made before config.json to this layout and release. Its preflight changes nothing and stops on anything it can't convert; secrets are renamed, not copied; an interrupted conversion resumes. An AGENTS_API_EXECUTION_OPTIONS_FILE stays as it is until the model settings import, which hooks into parsar apply. scripts/config-reference.py renders the settings table in docs/configuration.md from the schema; make check-distribution checks it.
…ons docs CONTRIBUTING now names config.json as the single operator file instead of forbidding a new configuration format. The install guide covers changing settings after installation, repair reruns and converting an earlier installation; the operations guide uses parsar status, start, stop and rotate-core-key. Key paths move from admin/ to secrets/.
status and start report why config.json can't be used and fall back to the last applied settings, which the running services use. A missing retained execution options file names itself instead of failing generically.
Phase 0's installer output moves into the new summary: the public Console and API base lines, local-only labels, Core's loopback /v1 when the public URL is Web's loopback port, the core-only /core/v1 line and the Projects and keys and Nodes wording, now pointing at secrets/core.key and config.json. The local Docker node keeps its 2 CPU / 2048 MiB default, and the docs keep phase 0's page names.
Review round 1 on 8158ef0. - apply recomputes the files to write after stamping the snapshot, so settings.json is written whenever its digest is recorded. Rotation and applies that restore a file no longer report settings.json as edited. - An apply records the digests it is about to write before writing, then commits state.json. A run interrupted in between is finished by the next apply instead of being reported as hand edits; status says so. - Native Core restarts only when its inputs changed, including on the install.sh repair path where enable --now leaves an active unit alone. - A failed first start leaves the apply unfinished, and --convert finishes a converted installation whose first start failed. - Web-only conversion before its Core notes the older Core instead of failing; the next apply records the pairing. - The conversion preflight refuses secret files that are links or readable by others, starts an old native unit by path, lowercases an old public URL when case is the only problem and records no core.key digest. - A public URL change needs confirmation when the applied value can't be read; state.json records the applied public URL. - Rotation restores the old key only before the new apply is recorded; status reports a Core that rejects secrets/core.key and a mismatched digest file, and config.json fields that disagree with state.json. - The origin check refuses IPv6 literals, underscores, trailing dots, non-ASCII hosts and leading-zero ports. - rotate-core-key reads state under the lock, the Compose marker escapes $, and unused imports are gone.
The public URL and web.core_url check now uses the rule of PR 1a's installer valid_core_origin, which matches Core's ValidateSandboxCoreURL: bracketed IP literals are accepted like Core accepts them, while underscores, a trailing dot, non-ASCII hosts and leading-zero ports stay refused. After 1a merges, config_model calls that one shared function.
The installer keeps the config.json structure. 1a's generator edits are resolved into it: the new Core env names were already generated, the local node no longer sends core_url, and valid_core_origin in configuration.py is the one origin rule. config.json's public_url and web.core_url check calls it, and --public-url and --core-url normalize case and a trailing slash before applying it. 1a's rule tests run against both. The docs keep 1a's public URL semantics and point upgrades at install.sh --convert.
Found in the mx1 real check. A first apply says "Services to start" rather than restart, and only a new installation says it has no execution node; a repaired or converted installation keeps its nodes.
Review rounds 1 and 2 both found P1s in interruption bookkeeping, so apply no longer decides restarts from state.json. Every Compose service carries the digest of its inputs in the io.parsar.inputs label, and the native unit carries it as PARSAR_INPUTS, which apply reads back from the Core process. apply renders config.json, compares with the running services and recreates or restarts exactly those that differ, Core first. A Core that rejects secrets/core.key is restarted, then Web. An interrupted apply, rotation or rollback is finished by the next apply, and status reports services that run with other inputs than config.json renders. state.json keeps identity and install facts and the last digests written for each generated file, used only to detect hand edits: a file is edited only if it matches none of them and differs from the current render; a missing file is written again. Rollback happens only when every service ran with the previous files; rotation never rolls back. The applied and pending records are gone. --convert now accepts installs made by the #138 installer (public URL, password file, no retired variables) as well as earlier ones; retired variables may be missing and a hand-set AGENTS_API_PUBLIC_URL becomes public_url. The test fixtures come from both earlier generators, kept verbatim in deploy/install/testdata/. Also: rotation stops Web inside its error handling and a stale core.key.new is removed; failed first starts name the right rerun command, and a local node requested at install is enrolled by the repair; parsar refuses a symlinked or non-private installation, secrets/ or generated/ directory; a resumed conversion checks its bundle and may repeat --public-url; the old Runtime history file is removed once config.json holds it; the public URL confirmation lists only nodes on the old address; and a Web-only dry run sends no key to a changed web.core_url.
Found in the mx1 recovery check: the message had an empty reason.
Phase 3 retires AGENTS_API_EXECUTION_OPTIONS_FILE at Core startup, so --convert no longer carries it into the generated core.env: it reports the variable, points to the deployment model providers and leaves the old file in place, since it may hold model keys. The generator's pass-through and the parsar apply import hook are gone. model_provider_sessions.py also finds an installation's generated/compose.json and no longer names install.sh --status. The bundle file list keeps both branches' additions.
…ive hosts first Review round 3. At #138 the sandbox deployment's core_url is derived from AGENTS_API_PUBLIC_URL, including the loopback fallback, so conversion no longer adopts it when core.env names the address: the #138 value is used as is and the loopback fallback becomes public_url null, keeping Web's origin on its own port. Both --convert branches run the native preflight before anything changes. A fresh install writes state.json before config.json, and a directory left with only state.json gets a clear message.
Review round 3. A rollback records the digests of the files it restores, so repeated rejected applies no longer end in a false hand-edit report. The public URL confirmation counts bound nodes from /core/v1/installation's address_bindings, so a failed node list read still requires confirmation. The running-state read skips one-off Compose containers, parsar stop checks the directories, a failed command names its program, and a rollback that could not start the previous files says so.
Review round 3. valid_core_origin treats an IPv4-mapped loopback address as loopback, as Go does. format must be the integer 1, not true or 1.0. ports.core restarts Web too with native Core; the schema's native_restarts annotation carries that into the settings snapshot and the generated table.
…start over Focused review. Only a directory that never applied anything (no recorded generated files, empty generated/) counts as an interrupted fresh install, including one stopped before state.json was written; an installation that lost config.json is told to restore it from a backup, with generated/settings.json for the last applied values, since deleting it would destroy the credential key and orphan the database. An unreadable installation.json gets conversion's own message.
Focused review. A rollback records only restored files that parsar wrote and that were not edited by hand, so a hand edit it puts back is still reported.
…to none Focused review. A hand-set loopback AGENTS_API_PUBLIC_URL other than Core's own fallback is reported, and in all mode a hand-set address that differs from installation.json's public_url stops conversion until --public-url names the one to keep, instead of silently moving Web's origin. A pre-#138 deployment core_url equal to Core's loopback address means no public URL. The core.env value is not canonicalized twice, and its message points at config/core.env. The docs say the deployment is read only for pre-#138 installs. Tests: the deployment-read assertion now watches HTTP requests, the native core-only ports case is back, and the hand-set loopback and resume preflight paths are covered.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 1b of the new-user install work: every process setting of an installation gets one visible home,
<install>/config.json, applied with aparsarmanagement command.config.jsonis the only file an operator edits.deploy/install/config.schema.json), and the settings table indocs/configuration.mdis generated from it.modeandnative_coreare fixed after install.secrets/holds one copy each of the Core key, the credential key and the database password (PostgresPOSTGRES_PASSWORD_FILE, CoreAGENTS_API_DATABASE_PASSWORD_FILE). The Core key digest file is derived from the key.state.jsonholds install facts that only tools write.generated/holdscompose.json,core.env, the systemd unit, the digest file and a non-secret settings snapshot (AGENTS_API_SETTINGS_FILE, served byGET /core/v1/installation).--discard-edits.parsaris copied into the install directory and works without the bundle:status,start,stop,apply [--dry-run]androtate-core-key.applydecides what to restart by comparing the inputs rendered fromconfig.jsonwith what is actually running (Compose service labels, or native Core's running environment). It restarts only what differs, and never recreates the database unless its own inputs changed.apply.install.shreadsconfig.json, rejects flags other than the documented resume cases, and repairs.install.sh --convertturns installs made by the two previous installers into this layout: before Derive every Core address from one public URL and report installation facts #138 and Derive every Core address from one public URL and report installation facts #138 itself, in all, core-only, web-only and native modes.AGENTS_API_EXECUTION_OPTIONS_FILEis dropped with a report, pointing to the deployment default model providers from Give deployment default model providers one home and close the self-hosted model gap #140. The old file is left in place, because it may hold keys.valid_core_origin).config.jsonas the single operator file. The install, configuration and operations guides are updated.The Web key hint moves to
secrets/core.keyin the paired frontend PR #150, which merges right after this one.Review
applycompares against what is actually running.3392eb50,019583ecandde3dd6ec.config.jsonwas told to delete its directory) and its P3s were fixed in7e2abc13,e9fc7481and563ceeb7. These were verified by tests and the gate, under the small-fix rule.Verification
435388f5(the exact head, which includes main31d91123): passed. Playwright browser cases were skipped on the server (Chrome is unavailable there; the skip is approved).parsarwithout the bundle;Upgrading
install.sh --convertfrom the new bundle, and Core is upgraded as part of that.AGENTS_API_EXECUTION_OPTIONS_FILEset deployment default models in Web (System) afterwards.Need help on this PR? Tag
@codesmithwith what you need. Autofix is disabled.