fix(grid-agent): move setup into YAML preferences (#135)
This commit is contained in:
@@ -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 32–512. |
|
||||
| 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user