fix(grid-agent): move setup into YAML preferences (#135)
Some checks failed
CI / rust-skia (Rust only) (push) Successful in 2m46s
CI / required (push) Failing after 54s

This commit is contained in:
2026-08-18 21:34:14 +02:00
parent b56b28043f
commit db25a977b7
27 changed files with 1242 additions and 482 deletions

View File

@@ -29,7 +29,7 @@ transport failures, visual fallback, and bounded-load cases.
The evidence stages are `configuration_and_bounds`, `headless_startup`,
`control_conformance`, `chat_scheduling`, `clean_shutdown`, `maintenance_reconnect`, and
`policy_redaction_and_protocol_audit`. A successful run has six ordered
`policy_redaction_and_protocol_audit`. A successful run has seven ordered
`passed` records and leaves zero service tasks, grid sessions, or loopback
sockets. Headless startup never creates a TUI. Integrated and split UI clients
exercise the same versioned control protocol, commands, event model, and
@@ -38,9 +38,9 @@ graceful-shutdown target in their conformance tests.
Reference deterministic run on 2026-08-18: all 137 library scenarios, 37
integration scenarios, and one compile-fail documentation case passed with all
features; the seven-stage evidence command passed with zero leaked tasks,
sessions, or sockets. The completed live-grid release binary was 28,857,688
sessions, or sockets. The completed live-grid release binary was 29,487,544
bytes with SHA-256
`b2c93a6acd6fa856669fdb4e08110d700218255f57ec2f7908977eeabea7c470`,
`1ccbd11baf69372d76767c419b59319447ecf088bc0ae83ce1640709f6507ad0`,
under the 40-MiB budget. The record identifies the package, pinned Rust toolchain,
source revision when supplied through `METACRATE_SOURCE_COMMIT`, exact generic
command, fake/live profile, grid type, endpoint capability profile, timestamp,
@@ -62,7 +62,7 @@ but small enough to expose deadlocks and unbounded ownership:
| Ordered shutdown | 5,000 ms | Allows journal flush and task joins while remaining service-manager friendly. |
| Steady/peak memory | 128/256 MiB | Includes bounded queues, conversations, observations, and one visual frame. |
| Any queue | 8,192 items | Matches the hard configuration ceiling; defaults are 32512. |
| Release binary | 40 MiB | The completed live-grid release is 28,857,688 bytes. |
| Release binary | 40 MiB | The completed live-grid release is 29,487,544 bytes. |
| Journal retention | 1 GiB | Operator-configured segment and total limits remain mandatory. |
Startup, control, reconnect, and shutdown are measured by monotonic time.
@@ -76,29 +76,21 @@ because allocator and OS accounting are not comparable across platforms.
## Live-grid matrix
Live validation is optional and requires a dedicated avatar, controlled land,
and an operator-supplied OpenAI-compatible endpoint. Credentials alone grant no
consent. Inspect exact confirmations without contacting either service:
Live validation uses a dedicated avatar, controlled land, and an
operator-supplied OpenAI-compatible endpoint configured in the private
platform `config.yml`. There are no action-specific environment switches.
Authorization follows the production policy model:
```sh
metacrate-grid-agent --check-live-opt-ins
```
Each capability has a separate exact-value environment opt-in:
| Capability | Variable and required literal |
| Origin | Available behavior |
| --- | --- |
| Login/relogin | `METACRATE_AGENT_LIVE_LOGIN=LOGIN` |
| Public mention and authorized/unprivileged IM | `METACRATE_AGENT_LIVE_CHAT_IM=CHAT-IM` |
| Controlled LSL delivery | `METACRATE_AGENT_LIVE_SCRIPT=SCRIPT` |
| Landmark offer, teleport, and bounded roaming | `METACRATE_AGENT_LIVE_LANDMARKS=LANDMARKS` |
| Reversible prim build and cleanup | `METACRATE_AGENT_LIVE_BUILD=BUILD-CLEANUP` |
| Synthetic visual capture and visual question | `METACRATE_AGENT_LIVE_VISUAL=VISUAL` |
| Everyone/public chat | Informational responses and explicitly public-safe tools. |
| Ordinary IM | Private conversation without privileged mutation. |
| UUID listed in `authorized_avatar_uuids` | Policy-gated privileged tools, with approval where required. |
| Local operator/control role | Pause, approve/cancel, reconnect, configuration, and shutdown controls. |
Any action opt-in without the login opt-in fails closed. A misspelled value is
false. Store the grid password and endpoint key in restrictive `_FILE` inputs
described by [the operations guide](grid-agent-operations.md), never in these
variables or a command line.
Landmarks, scripts, builds, cleanup, and visual questions are capabilities,
not configuration modes. They remain governed by tool origin, authenticated
UUID, land/ownership checks, bounded resources, and approval policy.
For an authorized live run, start split mode so another terminal can reconnect
the TUI without affecting the agent session. Record UTC start/end, commit,
@@ -113,15 +105,15 @@ hash. Do not record vendor presets or identifiers. Exercise, in order:
exact rollover, facing/attention event, and perception queries.
3. Pause, cancel, approve, resume, and force reconnect from the control client;
disconnect/reconnect the TUI and verify the service remains headless-safe.
4. With the script opt-in, deliver only to the controlled recipient and record
4. As a privileged user, deliver a script only to the controlled recipient and record
the returned inventory ID and permissions. Advanced mutation requires an
explicit approval and conservative script size/runtime limits.
5. With the build opt-in, build only on controlled land, record transaction and
5. As a privileged user, build only on controlled land, record transaction and
object recovery IDs locally, verify no currency operation exists, and delete
every created prim through the ownership-checked cleanup path.
6. With the landmark opt-in, accept a controlled offer, use a short bounded
folder schedule, teleport, then disable the schedule. With visual opt-in,
capture the synthetic scene and ask one visual question. Record the endpoint
6. Accept a controlled landmark offer, use a short bounded folder schedule,
teleport, then disable the schedule. Capture the synthetic scene and ask
one visual question. Record the endpoint
capability fallback if image input is rejected.
7. Gracefully stop. Confirm no pending approvals, scheduled jobs, inventory
offers, owned test prims, tasks, sockets, or sessions. List any unavoidable
@@ -147,8 +139,7 @@ needs `x86_64-w64-mingw32-gcc` for the existing AWS-LC build.
## Remaining limitations
No public CI runner performs live actions, holds credentials, measures portable
RSS, or proves a particular provider's image capability. Live evidence is only
credible when an operator supplies all exact opt-ins and completes the matrix
on a dedicated account. The deterministic gate is therefore the required CI
RSS, or proves a particular endpoint's image capability. Live evidence is only
credible when an operator completes the matrix on a dedicated account. The deterministic gate is therefore the required CI
acceptance record; a live report supplements it and must state any skipped
capability, endpoint fallback, or manually recoverable artifact explicitly.

View File

@@ -70,7 +70,7 @@ cleanup, fencing old events and late LLM/tool results. See
## Trust boundaries
- JSON configuration and environment text are untrusted. Unknown fields,
- YAML configuration and legacy migration input are untrusted. Unknown fields,
oversized files, invalid booleans, conflicting modes, non-HTTP(S) URLs,
URL fragments, malformed/noncanonical/nil UUIDs, wildcard authorization,
multiline secrets, and unsafe limits fail before startup.

View File

@@ -13,11 +13,11 @@ grid or LLM:
```sh
cargo build --locked --release -p metacrate-grid-agent
target/release/metacrate-grid-agent --config config/grid-agent.example.json --check-config
target/release/metacrate-grid-agent --config config/grid-agent.example.json --run-once
target/release/metacrate-grid-agent --config config/grid-agent.example.yml --check-config
target/release/metacrate-grid-agent --config config/grid-agent.example.yml --run-once
```
`--check-config` parses files, resolves secret files/environment, validates all
`--check-config` parses YAML and any legacy referenced secret files, validates all
bounds and TLS files, then exits before constructing the grid or LLM clients.
The example is fake/offline mode (`integrated=false`, `split=false`) and all
credential-looking values are placeholders.
@@ -26,16 +26,16 @@ For live foreground or an embedded TUI, build intentionally:
```sh
cargo build --locked --release -p metacrate-grid-agent --features live-grid
target/release/metacrate-grid-agent --config /path/to/grid-agent.json
target/release/metacrate-grid-agent --config /path/to/grid-agent.json --tui
target/release/metacrate-grid-agent --config /path/to/config.yml
target/release/metacrate-grid-agent --config /path/to/config.yml --tui
```
Set `integrated=true` for the foreground and embedded-TUI commands. Set
`split=true` for a headless service, then run the same binary as the TUI client:
```sh
metacrate-grid-agent --config /path/to/grid-agent.json
metacrate-grid-agent --config /path/to/grid-agent.json --tui-client
metacrate-grid-agent --config /path/to/config.yml
metacrate-grid-agent --config /path/to/config.yml --tui-client
```
The TUI client and service must use the same control address and operator token.
@@ -46,12 +46,11 @@ the default file is used only when it already exists:
| Platform | Configuration | Non-secret state |
|---|---|---|
| Linux/Unix | `$XDG_CONFIG_HOME/metacrate/grid-agent.json`, otherwise `$HOME/.config/metacrate/grid-agent.json` | `$XDG_DATA_HOME/metacrate/grid-agent`, otherwise `$HOME/.local/share/metacrate/grid-agent` |
| macOS | `$HOME/Library/Application Support/MetaCrate/grid-agent.json` | `$HOME/Library/Application Support/MetaCrate/grid-agent` |
| Windows | `%APPDATA%\MetaCrate\grid-agent.json` | `%LOCALAPPDATA%\MetaCrate\grid-agent` |
| Linux/Unix | `$XDG_CONFIG_HOME/metacrate/config.yml`, otherwise `$HOME/.config/metacrate/config.yml` | `$XDG_DATA_HOME/metacrate/grid-agent`, otherwise `$HOME/.local/share/metacrate/grid-agent` |
| macOS | `$HOME/Library/Application Support/MetaCrate/config.yml` | `$HOME/Library/Application Support/MetaCrate/grid-agent` |
| Windows | `%APPDATA%\MetaCrate\config.yml` | `%LOCALAPPDATA%\MetaCrate/grid-agent` |
`METACRATE_AGENT_STORAGE_PATH` overrides the state directory. Relative paths in
a JSON document resolve relative to that document where specified; service
Relative paths in a YAML document resolve relative to that document where specified; service
deployments should use absolute paths.
## Endpoint, grid identity, and authority
@@ -59,11 +58,11 @@ deployments should use absolute paths.
`llm.endpoint_url` is the exact OpenAI-compatible chat-completions URL. It is
not a base URL: MetaCrate does not append a path, discover models, select a
provider, or rewrite query parameters. `llm.api_key` is sent as the bearer key
only to that exact origin; redirects are refused. Prefer
`METACRATE_AGENT_LLM_API_KEY_FILE` over inline JSON or direct environment text.
only to that exact origin; redirects are refused. Set `llm.model` when the
endpoint requires a model name. These values live in the private `config.yml`.
Live modes require `grid.login_url`, `grid.avatar_name`, and a password from
`METACRATE_AGENT_GRID_PASSWORD_FILE`. `authorized_avatar_uuids` contains exact
Live modes require `grid.login_url`, `grid.avatar_name`, and `grid.password`.
`authorized_avatar_uuids` contains exact
grid UUIDs, never display names. Text claiming an authorized identity grants no
authority. Public chat can request bounded informational work and safe public
LSL delivery; movement, teleport, building, roaming changes, and administration
@@ -72,19 +71,22 @@ or a narrowly bound scheduler grant as documented in the policy matrix.
## Configuration contract and migration
The JSON root uses `"schema_version": 1`. Omitting it is accepted as legacy
The YAML root uses `schema_version: 1`. Omitting it is accepted as legacy
version 1. Any other value fails before startup with a migration message. There
is no automatic in-place migration: copy the file, update the copy using the
release notes/example, validate the copy, then atomically select it. Unknown
fields fail closed. Installers never overwrite or migrate operator files.
Precedence, lowest to highest, is:
Resolution order is:
1. bounded built-in defaults;
2. JSON configuration;
3. secret files named by JSON;
4. environment-referenced secret files;
5. direct environment values.
2. the platform `config.yml` or explicit `--config` document;
3. legacy secret files referenced by that document.
Process environment variables do not override connection, mode, or authority.
Migrate an existing `.env` once with `metacrate-grid-agent --import-env
/path/to/.env`, inspect it using `--preferences`, then remove the old file under
the operator's retention policy. JSON remains readable only for migration.
Mode, endpoints, credentials, authorization UUIDs, TLS, storage, queue/resource
limits, reconnect policy, behavior, and interaction settings are restart-only.
@@ -120,9 +122,8 @@ Rust's portable metadata API cannot prove arbitrary Windows ACL semantics, and
FAT/network filesystems may not enforce them. Treat an unverifiable filesystem
as unsuitable for unattended secrets.
Rotate one credential at a time: write a new restricted file beside the old
one, atomically replace or repoint the `_FILE` setting, run `--check-config`,
then restart. Revoke the old credential only after readiness. Control observer
Rotate one credential at a time in `--preferences` (or an ACL-restricted copy
of `config.yml`), save, run `--check-config`, then restart. Revoke the old credential only after readiness. Control observer
and operator tokens must differ from each other and from grid/LLM credentials.
## Linux systemd
@@ -130,16 +131,15 @@ and operator tokens must differ from each other and from grid/LLM credentials.
The hardened example is
[`../packaging/metacrate-grid-agent/systemd/metacrate-grid-agent.service`](../packaging/metacrate-grid-agent/systemd/metacrate-grid-agent.service).
Create the unprivileged `metacrate-agent` identity, copy (do not overwrite) an
operator configuration to `/etc/metacrate/grid-agent.json`, create
`/var/lib/metacrate/grid-agent`, and copy the environment-file example. The
environment file contains only secret *paths*. Put the actual secret files in
`/etc/metacrate/secrets`, owned by the service identity with mode `0600`.
operator configuration to `/etc/metacrate/config.yml` and create
`/var/lib/metacrate/grid-agent`. Keep the configuration owned by the service
identity with mode `0600`; do not put credentials in the unit environment.
Install the unit, inspect the sandbox, validate, then start:
```sh
sudo systemd-analyze security packaging/metacrate-grid-agent/systemd/metacrate-grid-agent.service
sudo -u metacrate-agent /usr/local/bin/metacrate-grid-agent --config /etc/metacrate/grid-agent.json --check-config
sudo -u metacrate-agent /usr/local/bin/metacrate-grid-agent --config /etc/metacrate/config.yml --check-config
sudo systemctl daemon-reload
sudo systemctl enable --now metacrate-grid-agent.service
```
@@ -150,19 +150,18 @@ writes only under the state directory.
## Windows service operation
Run the same `.exe`, JSON, control TCP protocol, and shutdown path. Portable
Run the same `.exe`, YAML, control TCP protocol, and shutdown path. Portable
foreground operation in PowerShell is the baseline:
```powershell
& 'C:\Program Files\MetaCrate\metacrate-grid-agent.exe' --config 'C:\ProgramData\MetaCrate\grid-agent.json' --check-config
& 'C:\Program Files\MetaCrate\metacrate-grid-agent.exe' --config 'C:\ProgramData\MetaCrate\grid-agent.json'
& 'C:\Program Files\MetaCrate\metacrate-grid-agent.exe' --config 'C:\ProgramData\MetaCrate\config.yml' --check-config
& 'C:\Program Files\MetaCrate\metacrate-grid-agent.exe' --config 'C:\ProgramData\MetaCrate\config.yml'
```
For unattended use, configure a maintained Windows service wrapper (for
example WinSW) to launch exactly that command as a dedicated low-privilege
account and to translate SCM Stop into Ctrl-C/console control before its timeout.
Keep secrets in separate ACL-restricted files and expose only `_FILE` paths in
the wrapper environment. Configure restart-on-failure, not unconditional rapid
Keep `config.yml` ACL-restricted to the service identity. Configure restart-on-failure, not unconditional rapid
restart. Validate as the service identity before registration. The wrapper must
not capture environment values or command output into a world-readable log.
The supplied PowerShell installer replaces only the executable and never state.

View File

@@ -8,21 +8,29 @@ Run an embedded UI with a live-grid integrated configuration:
```sh
cargo run --locked -p metacrate-grid-agent --features live-grid -- \
--config config/grid-agent.json --tui
--config config/grid-agent.integrated.example.yml --tui
```
Run a separate UI against a loopback split service:
```sh
cargo run --locked -p metacrate-grid-agent -- \
--config config/grid-agent.json --tui-client
--config config/grid-agent.split.example.yml --tui-client
```
The split client reads its operator capability through the normal secret-file
or environment configuration. It never displays or copies that value. Remote
The split client reads its operator capability from the ACL-restricted
`config.yml`. It never displays or copies that value. Remote
TLS clients must embed `TuiTransport` with their explicitly configured trust
roots; the command-line client intentionally accepts loopback TCP only.
Open preferences before a service is configured with
`metacrate-grid-agent --preferences`; the same panel is available from the live
TUI. It edits the endpoint, optional model, grid identity, hidden credentials,
mode, and privileged UUID list while preserving advanced YAML settings. Use
Up/Down to select, Enter to edit/commit, `w` to save, and Esc to cancel. Changes
to connection or authorization settings apply after restart. A one-time
`--import-env /path/to/.env` migrates legacy `GRID_*` and `OPENAPI_*` values.
Use Tab/arrow keys to change panels and scroll. `r` refreshes; `p`/`u` pause or
resume; `f` reconnects the grid; `a`/`d` approve or deny the first pending
approval; `x` cancels the newest visible action; `e` expires the first visible