Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2b5dcc0
Give every process setting one home in config.json, applied with parsar
SaladDay Sep 25, 2026
55f3d11
Describe config.json, parsar and --convert in the install and operati…
SaladDay Sep 25, 2026
1b0c668
Keep parsar status and start usable when config.json is invalid
SaladDay Sep 25, 2026
8158ef0
Merge origin/main: phase 0 installer output, Docker defaults and docs
SaladDay Sep 25, 2026
bd3be53
Fix false hand-edit reports, native restarts and conversion edge cases
SaladDay Sep 25, 2026
e7339f0
Merge origin/main: Getting started checklist and node route authentic…
SaladDay Sep 25, 2026
a0413b9
Accept exactly the origins Core accepts, including bracketed IPv6
SaladDay Sep 25, 2026
903f99e
Merge origin/main: saved Agent model provider in the console
SaladDay Sep 25, 2026
43e20bd
Merge origin/main: one public URL and installation facts in Core (#138)
SaladDay Sep 25, 2026
e29c57a
Word first starts and repairs accurately in the installer output
SaladDay Sep 25, 2026
063f524
Converge on what runs instead of recorded apply state
SaladDay Sep 25, 2026
5957699
Merge origin/main: Standard sandbox sizes in one file (#139)
SaladDay Sep 25, 2026
648b08f
Say that an interrupted rotation was interrupted
SaladDay Sep 25, 2026
672cf57
Merge origin/main: deployment default model providers (#140)
SaladDay Sep 25, 2026
09084c4
Merge origin/main: parked self-hosted executors (#142)
SaladDay Sep 25, 2026
135fdc7
Merge origin/main: node artifacts from the console and harness defaul…
SaladDay Sep 25, 2026
3392eb5
Convert a local-only #138 install without a public URL, and check nat…
SaladDay Sep 26, 2026
019583e
Record rolled-back files and count bound nodes from Core's bindings
SaladDay Sep 26, 2026
de3dd6e
Match Go's loopback rule, require format 1 and note native Web restarts
SaladDay Sep 26, 2026
bc00081
Merge origin/main: self-hosted executor command in Web (#143, #146)
SaladDay Sep 26, 2026
7e2abc1
Tell a live installation without config.json to restore it, never to …
SaladDay Sep 26, 2026
e9fc748
Keep a hand edit visible after a rollback restores it
SaladDay Sep 26, 2026
563ceeb
Refuse hand-set Core addresses that would move Web, and map loopback …
SaladDay Sep 26, 2026
435388f
Merge origin/main: node enrollment IDs (#145, #147, #149)
SaladDay Sep 26, 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
34 changes: 24 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -1714,15 +1714,28 @@ across upgrades. This is the same managed Runtime, not user-managed enrollment.
#### Matched Core and console distribution

[Configuration](docs/configuration.md) is the canonical operator parameter reference.
Compose and native launchers load the same private `config/core.env`; the installer
creates it once, validates retained literal values and never replaces user edits.
Core logs the loaded path without values. The installation receipt records packaging
and identity, not provider overrides. Explicit local-node flags call the ordinary
administrator API once; PostgreSQL owns the resulting selection. Administrator-issued
enrollment approves capacity (default two active/eight retained); a node cannot
supply or overwrite those limits. Downloaded specification copies remain validated
against the existing database-owned resources/Runtime contract. Do not add a new
configuration format, loader precedence, hot reload or embedded Core node.
Every process setting has one home: the installation's private `config.json`,
described by `deploy/install/config.schema.json`. The operator edits only that
file; `parsar apply` validates it, derives `generated/` (Compose file, `core.env`,
native unit, Core key digest file, settings snapshot) and converges on what actually
runs: each service carries the digest of its inputs (Compose label
`io.parsar.inputs`, native `PARSAR_INPUTS`), and exactly the services whose running
inputs differ are recreated or restarted. Decide restarts from what runs, never
from recorded bookkeeping, so the next apply finishes any interrupted one. Installation flags only seed it, and
rerunning the installer rejects them. Runtime settings stay in PostgreSQL and
change through Web or `/core/v1`. Secrets live once each in `secrets/`; identity and
install facts live in tool-written `state.json`. Core still reads only its
environment and has no config loader; it serves the non-secret snapshot at
`GET /core/v1/installation`. Keep the schema, the subset validator
(`config_model.py`), the generator and the generated reference table in
`docs/configuration.md` (`scripts/config-reference.py`) in step. Do not add a
second operator configuration file, loader precedence, hot reload, compatibility
reading of retired names, or an embedded Core node. Explicit local-node flags call
the ordinary administrator API once; PostgreSQL owns the resulting selection.
Administrator-issued enrollment approves capacity (default two active/eight
retained); a node cannot supply or overwrite those limits. Downloaded specification
copies remain validated against the existing database-owned resources/Runtime
contract.

The installer packages Core and the Web console together,
with independent `--core-only` and `--web-only` modes. `site/` is the public static
Expand Down Expand Up @@ -1803,7 +1816,8 @@ allocates, wakes a sandbox or grants project resource access. It is an `/api/v1`
machine route that reaches Core directly, never through the console. Bounded
polling and reruns retain the original container and history; timeout is a
diagnostic failure, not permission to relaunch. The installation public URL
(`AGENTS_API_PUBLIC_URL`, from the installer's `--public-url`) is the one origin for
(`public_url` in the installation's `config.json`, seeded by `--public-url`, and
`AGENTS_API_PUBLIC_URL` for Core) is the one origin for
applications, nodes, sandbox guests and self-hosted executors, and also the console
origin. Core derives the daemon `wss` URL, the self-hosted `remote_url`, hosted
Runtime bootstrap and the deployment's read-only `core_url` from it; the deployment
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ check-distribution:
go test ./services/core-console -count=1
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s deploy/install -p 'test_*.py'
PYTHONDONTWRITEBYTECODE=1 python3 scripts/core-distribution-manifest.test.py
PYTHONDONTWRITEBYTECODE=1 python3 scripts/config-reference.py --check
bash -n deploy/install/install.sh scripts/build-core-console.sh scripts/build-core-distribution.sh scripts/prepare-release-runtimes.sh
./scripts/build-core-console.sh

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ retain their node across disconnects and resume.
for obtaining/building a matching bundle and the host/network prerequisites.
2. **Sign in to Web.** Open the console address printed by the installer and
sign in with the [Core key](docs/getting-started/operations.md#core-key) from
`~/.parsar/core/admin/core.key`. Keep it private. The console connects to Core
`~/.parsar/core/secrets/core.key`. Keep it private. The console connects to Core
automatically. Create a Project on the **Projects and keys** page, then
issue a key within it for your application. Save the one-time plaintext
response privately; Core stores its digest. Rotate by issuing another key in the
Expand Down Expand Up @@ -68,7 +68,9 @@ run an optional API example with your own model credentials.

Run these from an extracted distribution. The plain command uses loopback for
local console/API access. For node enrollment, use the reachable origin described
above; the installer does not change an existing installation's public URL.
above. Flags only seed the installation's `config.json`; later changes go there
and take effect with `~/.parsar/core/parsar apply`, and `parsar status`, `start`
and `stop` replace `install.sh --status` and `--stop`.
Installing a local provider is optional, needs that HTTPS `--public-url`, and is not
required for adding nodes in Web.

Expand Down
255 changes: 255 additions & 0 deletions deploy/install/config.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Parsar Core installation configuration",
"description": "Process settings of one installation. Edit config.json, then run parsar apply.",
"type": "object",
"additionalProperties": false,
"required": ["format", "mode"],
"properties": {
"$schema": {
"type": "string",
"description": "Editor hint that points at the installed copy of this schema. Ignored.",
"x-parsar": {"setting": false}
},
"format": {
"const": 1,
"description": "Configuration format. Only an upgrade changes it.",
"x-parsar": {"setting": false, "changeable": false}
},
"mode": {
"enum": ["all", "core-only", "web-only"],
"default": "all",
"description": "Which services this installation runs.",
"x-parsar": {"changeable": false, "install_flag": "--core-only or --web-only"}
},
"native_core": {
"type": "boolean",
"default": false,
"description": "Run Core as a systemd user service instead of a container.",
"x-parsar": {"changeable": false, "modes": ["all", "core-only"], "install_flag": "--native-core"}
},
"public_url": {
"type": ["string", "null"],
"default": null,
"description": "Public origin of Core and Web behind your TLS reverse proxy, such as https://core.example. Nodes, sandboxes and self-hosted executors use it. null means local access only through http://127.0.0.1.",
"x-parsar": {
"check": "origin",
"restarts": ["core", "web"],
"derives": ["AGENTS_API_PUBLIC_URL", "CORE_CONSOLE_ORIGIN"],
"install_flag": "--public-url"
}
},
"ports": {
"type": "object",
"additionalProperties": false,
"properties": {
"core": {
"type": "integer",
"minimum": 1024,
"maximum": 65535,
"default": 8091,
"description": "Loopback port of the Core API. With native Core, Web follows it.",
"x-parsar": {
"modes": ["all", "core-only"],
"restarts": ["core"],
"native_restarts": ["core", "web"],
"derives": ["Core port mapping or AGENTS_API_ADDR"],
"install_flag": "--core-port"
}
},
"web": {
"type": "integer",
"minimum": 1024,
"maximum": 65535,
"default": 8080,
"description": "Loopback port of Web.",
"x-parsar": {
"modes": ["all", "web-only"],
"restarts": ["web"],
"derives": ["Web port mapping or CORE_CONSOLE_ADDR"],
"install_flag": "--web-port"
}
},
"database": {
"type": "integer",
"minimum": 1024,
"maximum": 65535,
"description": "Loopback port of PostgreSQL. Present exactly when native_core is true; the installer picks a free port.",
"x-parsar": {
"modes": ["all", "core-only"],
"restarts": ["database", "core"],
"derives": ["database port mapping", "AGENTS_API_DATABASE_URL"]
}
}
}
},
"web": {
"type": "object",
"additionalProperties": false,
"x-parsar": {"modes": ["web-only"]},
"properties": {
"core_url": {
"type": "string",
"description": "Origin of the Core that this Web connects to: HTTPS, or HTTP on a loopback host.",
"x-parsar": {
"check": "origin",
"restarts": ["web"],
"derives": ["CORE_CONSOLE_UPSTREAM"],
"install_flag": "--core-url"
}
}
}
},
"log": {
"type": "object",
"additionalProperties": false,
"properties": {
"level": {
"enum": ["debug", "info", "warn", "error"],
"default": "info",
"description": "Minimum log level of Core and Web.",
"x-parsar": {"restarts": ["core", "web"], "derives": ["PARSAR_LOG_LEVEL"]}
},
"format": {
"enum": ["auto", "text", "json"],
"default": "auto",
"description": "Log format. auto writes text to a terminal and JSON otherwise.",
"x-parsar": {"restarts": ["core", "web"], "derives": ["PARSAR_LOG_FORMAT"]}
},
"add_source": {
"type": "boolean",
"default": false,
"description": "Add the source file and line to each log record.",
"x-parsar": {"restarts": ["core", "web"], "derives": ["PARSAR_LOG_ADD_SOURCE"]}
}
}
},
"core": {
"type": "object",
"additionalProperties": false,
"x-parsar": {"modes": ["all", "core-only"]},
"properties": {
"execution_concurrency": {
"type": "integer",
"minimum": 1,
"maximum": 1024,
"default": 4,
"description": "Concurrent execution work units in Core. Unrelated to node sandbox capacity.",
"x-parsar": {"restarts": ["core"], "derives": ["AGENTS_API_EXECUTION_CONCURRENCY"]}
},
"harnesses": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {"enum": ["claude_sdk", "codex", "mcode"]},
"default": ["claude_sdk", "codex", "mcode"],
"description": "Harnesses that Sessions may select.",
"x-parsar": {"restarts": ["core"], "derives": ["AGENTS_API_HARNESSES"]}
},
"default_harness": {
"enum": ["claude_sdk", "codex", "mcode"],
"default": "codex",
"description": "Harness used when a Session names none. It must be listed in core.harnesses.",
"x-parsar": {"restarts": ["core"], "derives": ["AGENTS_API_ENGINE"]}
},
"write_audit_retention": {
"type": "string",
"default": "2160h",
"description": "How long non-creation write history is kept, as a Go duration of at least 1h.",
"x-parsar": {"check": "go_duration_min_1h", "restarts": ["core"], "derives": ["AGENTS_API_WRITE_AUDIT_RETENTION"]}
},
"oauth_trusted_origins": {
"type": "array",
"uniqueItems": true,
"items": {"type": "string", "x-parsar": {"check": "https_origin"}},
"default": [],
"description": "Extra HTTPS origins trusted as private OAuth issuers.",
"x-parsar": {"restarts": ["core"], "derives": ["AGENTS_API_OAUTH_TRUSTED_ORIGINS"]}
},
"database_pool": {
"type": "object",
"additionalProperties": false,
"properties": {
"max_conns": {
"type": ["integer", "null"],
"minimum": 1,
"default": null,
"description": "Maximum database connections. null keeps the driver default, max(4, CPU count).",
"x-parsar": {"restarts": ["core"], "derives": ["pool_max_conns in AGENTS_API_DATABASE_URL"]}
},
"min_conns": {
"type": ["integer", "null"],
"minimum": 0,
"default": null,
"description": "Minimum idle database connections. null keeps the driver default, 0.",
"x-parsar": {"restarts": ["core"], "derives": ["pool_min_conns in AGENTS_API_DATABASE_URL"]}
},
"max_conn_lifetime": {
"type": ["string", "null"],
"default": null,
"description": "Go duration. null keeps the driver default, 1h.",
"x-parsar": {"check": "go_duration", "restarts": ["core"], "derives": ["pool_max_conn_lifetime in AGENTS_API_DATABASE_URL"]}
},
"max_conn_idle_time": {
"type": ["string", "null"],
"default": null,
"description": "Go duration. null keeps the driver default, 30m.",
"x-parsar": {"check": "go_duration", "restarts": ["core"], "derives": ["pool_max_conn_idle_time in AGENTS_API_DATABASE_URL"]}
},
"health_check_period": {
"type": ["string", "null"],
"default": null,
"description": "Go duration. null keeps the driver default, 1m.",
"x-parsar": {"check": "go_duration", "restarts": ["core"], "derives": ["pool_health_check_period in AGENTS_API_DATABASE_URL"]}
}
}
},
"runtime_history": {
"type": ["object", "null"],
"default": null,
"additionalProperties": false,
"description": "Runtime history collection and OTLP export. null keeps local collection with Core's defaults. Core checks the values at startup.",
"x-parsar": {"restarts": ["core"], "derives": ["generated/runtime-history.json", "AGENTS_API_RUNTIME_HISTORY_FILE"]},
"properties": {
"transport": {
"type": "string",
"description": "OTLP export transport.",
"x-parsar": {"restarts": ["core"]}
},
"endpoint": {
"type": "string",
"description": "OTLP collector endpoint. Omit it to keep history local.",
"x-parsar": {"restarts": ["core"]}
},
"insecure": {
"type": "boolean",
"description": "Export without TLS.",
"x-parsar": {"restarts": ["core"]}
},
"headers": {
"type": "object",
"additionalProperties": {"type": "string"},
"description": "Headers sent with each export, such as credentials. Never shown by parsar or Core.",
"x-parsar": {"sensitive": true, "restarts": ["core"]}
},
"queue_capacity": {
"type": "integer",
"description": "Export queue capacity.",
"x-parsar": {"restarts": ["core"]}
},
"timeout_seconds": {
"type": "integer",
"description": "Export and query timeout in seconds.",
"x-parsar": {"restarts": ["core"]}
},
"sample_interval_seconds": {
"type": "integer",
"description": "Periodic sampling interval in seconds.",
"x-parsar": {"restarts": ["core"]}
}
}
}
}
}
}
}
Loading
Loading