From 1331d0956f106654f2d97297db0f0a020f0a154b Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 06:46:32 +0000 Subject: [PATCH 1/8] docs: make the executor credential contract own the installation grant Add the installation grant, its machine routes and claim rules, the break-glass command, the park-after-rejection behavior and compute-owner cleanup. Fix the stale connection-confirmation text (the native installer polls once a second for 45 seconds and points to connect.log) and the ambiguous 409 for a second key on a bound Environment. --- .../environment-executor-credentials.md | 262 ++++++++++-------- 1 file changed, 154 insertions(+), 108 deletions(-) diff --git a/contracts/agents-api/environment-executor-credentials.md b/contracts/agents-api/environment-executor-credentials.md index e9f8a185f..682a25f79 100644 --- a/contracts/agents-api/environment-executor-credentials.md +++ b/contracts/agents-api/environment-executor-credentials.md @@ -1,24 +1,79 @@ # Environment executor credentials -An executor credential lets a daemon enroll and connect for one `self_hosted` -Environment. An application creates the Session with its Project API key and -receives an Environment-scoped installation command. The installer claims its -connect-only key and connects without requiring Web or a Core key. Operators -retain the explicit Core-key issuance, rotation and revocation routes below. -The command's short-lived authorization and the daemon's long-term credential -are separate; their lifecycle is defined in [native installation](../../docs/self-hosted-native.md). +An executor credential lets `oac-daemon` enroll and connect for one `self_hosted` +Environment. It authorizes only the private daemon transport +(`/api/v1/agent-daemon/*`) for that Environment, never `/v1`, `/core/v1`, +sandbox-node enrollment or Project resources. The Project's principal is its +execution principal. Core stores only a digest of the secret. + +A credential comes from one of two places: + +- **The installation grant.** A `self_hosted` Session returns an install command. + The installer uses the command's short-lived grant to claim one credential; it + needs neither Web nor the Core key. The + [self-hosted guide](../../docs/getting-started/self-hosted.md) shows the steps. +- **The Core-key routes.** An operator issues, rotates and revokes credentials + through Web or `/core/v1`. + +Core never creates, stops or reclaims the machine. Disconnecting, revoking a +credential or deleting the Session does not prove that every native process has +stopped; the machine's owner stops and cleans up its own compute. + +## Installation grant + +Session create, retrieve and update responses of a `self_hosted` Session carry +`x_agents_core.installation`; Session lists do not. Web reads the same object with +the Core key at +`GET /core/v1/projects/{project_id}/environments/{environment_id}/installation` +and shows its commands without changing them. + +| Field | Meaning | +| --- | --- | +| `status` | `available`, or `unavailable` when this Core has no matching native installers; `message` then says so | +| `version` | The Core build the commands install | +| `expires_at` | Unix time when the grant expires, 30 minutes after the response | +| `commands.posix`, `commands.powershell` | The install command for Linux/macOS and for Windows PowerShell | + +The grant is bound to the Environment, the Session creator's principal and the +Core build. It stops working when it expires, when the Session is deleted, when the +Project is archived or when Core runs a different build. Reading the Session again +returns a fresh grant. Treat the command as a temporary secret: it can claim the +credential, but it cannot run work or read files. + +The installer generates the secret and saves it privately as +`daemon/executor-credential.json` in the installation directory before it claims +the key. Core stores the digest under the key ID equal to the Environment ID. A +lost response is safe to retry: the retry must present the same secret. A grant +never replaces or restores a credential. If the Environment already has a +different, rotated or revoked credential, the claim fails with 409 +`executor_credential_exists`. + +The installer calls these machine routes on Core: + +| Route | Authorization | Purpose | +| --- | --- | --- | +| `GET /api/v1/agent-daemon/install/{version}/bootstrap.sh`, `bootstrap.ps1` | None | Platform bootstrap scripts | +| `GET /api/v1/agent-daemon/install/{version}/{os}-{arch}.sha256`, `{os}-{arch}.tar.gz` | None | Installer checksum and archive. Core serves a local copy, or redirects (307) to the versioned release URL in its catalog | +| `POST /api/v1/agent-daemon/installation` | Grant | The frozen binding: `version`, `protocol_version`, `environment_id`, `remote_url`, `workspace_directory`, `harness` | +| `POST /api/v1/agent-daemon/installation/claim` | Grant | `{"executor_token":"SECRET"}`; 204 | + +An invalid or expired grant returns 401 `installation_authorization_invalid`. +Without matching installers the grant routes return 503 `installation_unavailable`. +A malformed secret returns 400. Artifact routes carry no credential, and the grant +is sent only to Core, never to an artifact host. -## Routes +## Core-key routes All routes are under `/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` and require the Core key. They apply only to a `self_hosted` Environment of that Project whose Session exists (is not deleted); any other Project, Environment -type, missing Environment or deleted Session returns 404. +type, missing Environment or deleted Session returns 404. Project API keys cannot +use them. | Operation | Request | Result | | --- | --- | --- | -| List | `GET …/executor-credentials` | Credential metadata in `data`, plus required `connection` observation | +| List | `GET …/executor-credentials` | Credential metadata in `data`, plus the required `connection` object | | Issue or rotate | `POST …/executor-credentials` with `{"key_id":"UUID","rotate":false}` | 201 credential file, returned once | | Revoke | `DELETE …/executor-credentials/{key_id}` | 204 | @@ -26,29 +81,6 @@ The list holds metadata only, oldest first, for the credentials restricted to th Environment; `revoked_at` is null while a credential is active. It never contains a secret. -The required `connection` object contains `status` (`never_enrolled`, `connected`, -or `disconnected`), `bound_key_id`, `enrolled_at`, and `last_seen_at`. All three -binding fields are null before enrollment. Once enrolled, the bound key and -enrollment time describe the existing device; a null `last_seen_at` means no -authenticated heartbeat has been recorded. Issuing another key does not change -the binding. Rotation/revocation can make the binding disconnected while its -history remains visible. Expired Environments remain readable under the existing -list rules but cannot have current executor authority. - -Connected means the Environment is connected, its device and executor key still -have current Core authority, and the process-local gateway has an open peer -that authenticated with that current key. Core rechecks authority after observing -the peer. A former key's live socket, a device timestamp, or a ready-looking -Environment alone is insufficient; without a gateway, Core never returns -connected. These facts are an observation, not a reservation of connectivity or -native/model readiness. `last_seen_at` may lag by a heartbeat interval. - -List metadata and binding facts use one read-only database snapshot. That snapshot -ends before the live authority checks, so a committed rotation/revocation is not -hidden by snapshot isolation. Known authority loss projects as disconnected; -observation/storage failures remain errors. Device IDs and credential digests are -internal and never serialized. The public `/v1` Environment shape is unchanged. - `key_id` is a canonical nonzero UUID chosen and retained before the request. `rotate` is optional and defaults to false. The 201 response is the daemon credential-file format: @@ -59,10 +91,6 @@ credential-file format: Responses use `Cache-Control: no-store`. Save the response directly to an owned mode-0600 file; never place it in shell arguments, logs, a workspace or source. -Only its digest is persisted in Core. The Project's principal is the credential's -execution principal. The secret authorizes daemon enrollment and connection -(`/api/v1/agent-daemon/*`) for this exact Environment only, never `/v1`, `/core/v1`, -sandbox-node enrollment or project resource operations. Writes have two conflicts, both 409. `executor_credential_exists`: an issuance whose `key_id` already exists and does not set `rotate:true`, even after @@ -71,16 +99,15 @@ rotated credential; listing and revocation remain available there, because revoking must always work. An issuance or rotation is checked in this order, and the first failure is -returned: the request body (400); the target Environment, which must be a -`self_hosted` Environment of this Project whose Session exists (404); an archived +returned: the request body (400); the target Environment (404); an archived Project (409 `project_archived`); then the key itself (409 `executor_credential_exists` without `rotate`, or 404 when rotating a `key_id` that was never issued). + Rotation replaces the secret of an existing key restricted to this Environment, -keeps that Environment, invalidates the previous secret and restores a revoked -key; rotating an unknown `key_id` returns 404. Revocation is idempotent and -returns 204 each time. It denies further enrollment and connection; it does not -stop executor-owned compute or prove that an existing process has stopped. +keeps that Environment, invalidates the previous secret at once and restores a +revoked key. Revocation is idempotent and returns 204 each time. It denies further +enrollment and connection. After an uncertain result, such as a timeout, do not retry automatically. List the credentials, then either rotate the same `key_id` (it was issued but its @@ -89,73 +116,92 @@ secret was lost) or issue it again (it was not issued). Issue, rotate and revoke each record an administrator audit entry (`resource_type:"executor_credential"`, the key ID as `resource_id`, action `issue`, `rotate` or `revoke`) in the same transaction as the write. The audit -never contains the secret. Credentials issued by the operator CLI -(`oac-core-environment-key`) without an Environment restriction cannot be -managed through these routes. +never contains the secret. + +### Break-glass command + +`oac-core-environment-key` issues, rotates or revokes a credential directly in the +database when the Core API is unavailable. It needs the private database +configuration and the Project's execution principal: `--tenant` (the Project's +tenant UUID in the `projects` table), `--organization core`, +`--project proj_`, `--subject-kind service_account`, +`--subject-id project:` and `--key-id`. `--environment` restricts a +new credential to one Environment. The command bypasses the Core API: it skips the +archived-Project check and writes no audit entry, so use the Core-key routes +whenever Core is running. A credential issued without an Environment restriction +cannot be managed through the routes above. + +## Connection status + +The list's required `connection` object contains `status` (`never_enrolled`, +`connected` or `disconnected`), `bound_key_id`, `enrolled_at` and `last_seen_at`. +All three binding fields are null before enrollment. Once enrolled, the bound key +and enrollment time describe the existing device; a null `last_seen_at` means no +authenticated heartbeat has been recorded. Issuing another key does not change the +binding. Rotation or revocation can make the binding disconnected while its +history remains visible. Expired Environments remain readable under the existing +list rules but cannot have current executor authority. -## Model provider +Connected means the Environment is connected, its device and executor key still +have current Core authority, and the process-local gateway has an open peer that +authenticated with that current key. Core rechecks authority after observing the +peer. A former key's live socket, a device timestamp or a ready-looking +Environment alone is insufficient; without a gateway, Core never returns +connected. These facts are an observation, not a reservation of connectivity or +of native or model readiness. `last_seen_at` may lag by a heartbeat interval. -The Session carries its own model provider: `x_agents_core.model_provider` at -creation or a saved Agent that has one. Deployment default model providers do not -apply to `self_hosted` Sessions, and creation without a provider fails with 400 -`model_provider_required`. Core freezes the bundle in the Session's encrypted -snapshot and sends it only over the connection of the executor enrolled for this -Environment with a current credential of the Session creator's principal. The -executor keeps it in the Runtime's native harness home. Public Files remains -scoped to the authorized workspace, but native tools and the host owner can read -whatever the starting account can access. The daemon provides no same-user -credential isolation. Revocation does not erase a -bundle already delivered. A saved Agent's provider key is delivered to the -executor of every `self_hosted` Session created with that Agent in the Project, so -anyone who can create `self_hosted` Sessions in the Project and run an executor -can read it. - -## Executor host - -The [native installer](../../docs/self-hosted-native.md) uses the same daemon and -pinned adapters on every supported platform. Session responses provide commands -in `x_agents_core.installation`; Web displays them without reconstructing them. -The bootstrap only downloads and extracts a qualified distribution, then invokes -the common installer to select Harnesses, install, start and verify connection. -The Session's Environment identity and workspace are fixed inputs. Installation -never creates or reclaims the user's machine, workspace or native history. - -Explicit distribution installation with `--credential-file` remains available -for operator-managed credentials. Readiness and connection checks do not validate -model access. Runtime preparation and execution use the existing common protocol. - -### Revoked or rotated credential - -When Core permanently rejects enrollment or the WebSocket, the daemon reports the -reason and parks without retrying until stopped. A protocol mismatch requires the -matching current distribution; it does not trigger a migration. Transient transport -failures retain the existing reconnect behavior and never replay execution. - -Rotate the same `key_id`, stop the daemon, replace the configured credential JSON -file, and start it again. Issuing a new key for an enrolled Environment fails with -409 because enrollment retains its original key binding. Revocation prevents the -old token from reconnecting. Rotation does not reinstall Harnesses, change the -workspace or replace native history. - -## Private connection confirmation - -`GET /api/v1/agent-daemon/connection?environment_id=UUID` uses the existing -executor bearer, sent directly to Core (the reverse proxy routes `/api/v1` to -Core; the console does not serve it). It is part of the private -daemon transport, not the public Agents API. It reads existing authorization and -binding only; it never enrolls a device, starts execution or changes resources. -The no-store response contains only the requested `environment_id` and `status` -(`connected` or `disconnected`). Connected requires the existing Environment -observation, its exact Session/device binding, current executor authority and a -live gateway socket authenticated with that same credential. A stale observation -or a socket carrying the former rotated key cannot confirm connection. +List metadata and binding facts use one read-only database snapshot. That snapshot +ends before the live authority checks, so a committed rotation or revocation is +not hidden by snapshot isolation. Known authority loss projects as disconnected; +observation and storage failures remain errors. Device IDs and credential digests +are internal and never serialized. The public `/v1` Environment shape is unchanged. + +### Private connection confirmation + +`GET /api/v1/agent-daemon/connection?environment_id=UUID` uses the executor bearer, +sent directly to Core (the reverse proxy routes `/api/v1` to Core; the console does +not serve it). It is part of the private daemon transport, not the public Agents +API. It reads existing authorization and binding only; it never enrolls a device, +starts execution or changes resources. The no-store response contains only the +requested `environment_id` and `status` (`connected` or `disconnected`). Connected +requires the Environment observation, its exact Session and device binding, +current executor authority and a live gateway socket authenticated with that same +credential. A stale observation or a socket carrying a rotated key cannot confirm +connection. Invalid, revoked, foreign or deleted-Session authority returns 401; a different key for an already bound Environment returns 409. Responses do not expose the -actual binding or database diagnostics. The installer derives this HTTPS route -from the validated returned `remote_url`, rejects redirects, retries transient -read failures within its deadline and polls at two-second intervals. Permanent -rejections fail immediately. On timeout or rejection it retains the container, -volumes, credential and receipts, and prints bounded Docker log inspection and -same-command retry guidance. This confirms authenticated connectivity, not model -credentials, harness capabilities or completed execution. +actual binding or database diagnostics. + +The installer derives this route from the returned `remote_url` and does not +follow redirects. After starting the daemon it polls once a second for up to 45 seconds. +A 401 or 409 fails at once. On timeout it prints the path of the daemon's +`connect.log` and asks you to rerun the same command; the daemon keeps +reconnecting, and the installation, credential and history stay in place. A +confirmed connection proves authentication only, not model access, Harness +capability or completed execution. + +## Revoked or rotated credential + +When Core permanently rejects the daemon (enrollment 401 or 409, a permanent +WebSocket rejection, or a daemon version from another Core distribution), the +daemon prints the reason once and makes no further requests until it is stopped; +it then exits successfully, so a supervisor that restarts on exit does not loop. +When started again, it tries enrollment once and parks again. Transient +failures keep the normal reconnect behavior and never replay execution. + +To reconnect, rotate the same `key_id` (**Rotate**, or **Restore** for a revoked +credential, in the Session's **Executor credentials**), stop the daemon, replace +the credential file at its configured path, and start the daemon again. A new +`key_id` cannot reconnect an Environment that is already bound: issuing it +succeeds, but enrollment with it returns 409. Rotation does not reinstall +Harnesses, change the workspace or replace native history; never create a new +Session history to recover a credential. + +## Model provider + +A `self_hosted` Session carries its own model provider; deployment defaults never +apply. [Model execution](model-execution.md) owns the delivery rules. A saved +Agent's provider key is delivered to the executor of every `self_hosted` Session +created with that Agent in the Project, so anyone who can create `self_hosted` +Sessions in the Project and run an executor can read it. From 87cd53c08dd557a88d2c314952c6b535b08661fe Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 06:46:33 +0000 Subject: [PATCH 2/8] docs: merge the native self-hosted guide into the self-hosted guide docs/getting-started/self-hosted.md now covers platforms, connecting, automation options, operating oac-daemon, adding Harnesses, rotation and manual installation. The Windows npm/npx launcher rule moves to the Environment contract. Remove docs/self-hosted-native.md and its site page, and point the Web install panel and every inbound link at the merged guide. --- apps/docs/content/docs/meta.json | 1 - apps/docs/content/docs/self-hosted-native.mdx | 177 -------------- apps/docs/scripts/guides.json | 6 - apps/web/e2e/monitoring.spec.ts | 2 +- .../sessions/ExecutorInstallPanel.tsx | 2 +- contracts/agents-api/environments.md | 6 +- contracts/agents-api/harness-onboarding.md | 2 +- docs/api/README.md | 4 +- docs/getting-started/README.md | 1 - docs/getting-started/self-hosted.md | 228 ++++++++++++------ docs/self-hosted-native.md | 172 ------------- docs/user-guide.md | 2 +- docs/web/protocol-coverage.md | 2 +- 13 files changed, 164 insertions(+), 441 deletions(-) delete mode 100644 apps/docs/content/docs/self-hosted-native.mdx delete mode 100644 docs/self-hosted-native.md diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json index 7ff7f2f37..286c8d94a 100644 --- a/apps/docs/content/docs/meta.json +++ b/apps/docs/content/docs/meta.json @@ -20,7 +20,6 @@ "environments-and-files", "hosted-providers", "self-hosted-execution", - "self-hosted-native", "---Administration---", "console", "admin-api", diff --git a/apps/docs/content/docs/self-hosted-native.mdx b/apps/docs/content/docs/self-hosted-native.mdx deleted file mode 100644 index 5b3e93889..000000000 --- a/apps/docs/content/docs/self-hosted-native.mdx +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: "Native daemon installation" -description: "Install and operate a self-hosted daemon on Linux, macOS or Windows." ---- - -Use the same `oac-daemon` on a user-managed Linux, macOS or Windows machine. -Physical machines, VMs and user-owned sandboxes use the same installer. Core does -not create or reclaim these machines. Core-managed Docker, E2B and microsandbox -Providers remain Linux-only and receive prebuilt Runtime images or templates. - -**The daemon is not a sandbox.** Tools can access whatever its account can. Use an -outer container or VM when you need isolation; see -[Runtime and outer isolation](/concepts#runtime-and-outer-isolation). -`disabled` and `restricted` network modes need an outer layer that enforces them. - -## Platforms and prerequisites - -| Platform | Codex | Claude Code | MiniMax Code | -| --- | --- | --- | --- | -| Linux | Supported | Supported | Supported | -| macOS | Supported | Supported | Supported | -| Windows | Supported | Supported | Unsupported by the current adapter | - -Use a distribution built for the machine's OS and architecture. Windows support -is validated on a native CI runner; manual Windows machine acceptance is not yet -recorded. Native Linux/macOS runs and native CI qualify the corresponding bundles. -Supported does not mean every model provider or optional native feature works in -every combination. Session capabilities are checked by the existing Harness contract. - -The distribution contains Node 22.22.0/npm and the selected release's components: -Codex 0.153.4, Claude Agent SDK 0.3.269 with native Claude Code 2.1.269 and the -project's adapter, and the patched MiniMax Code 0.4.12 companion on Unix. Arbitrary -official CLI installations are not adopted. They remain untouched while the -installer creates its private, verified copy. Compatible components from this -same installation are reused after checking their version, contents and startup. - -Claude on Windows requires Git Bash. Bash is also needed for Runtime setup and -MiniMax tools. Node/npm and MiniMax's ripgrep are included; Python/pip, when needed -by a Session's capability dependencies, must be available. Missing system -components are reported. Install those through the host's normal administration -process; the daemon never runs apt, sudo or an elevation command. - -## Install and connect - -Create a Session using the public Agents API with `environment.type: "self_hosted"`. -The response retains the official Environment `id` and `remote_url`, and adds -`x_agents_core.installation` with `commands.posix`, `commands.powershell` and -`expires_at`. Execute the command for your target platform. Core Web shows the -same commands in the **Self-hosted** Session's connection section; Web is not a -prerequisite for API callers. - -The command downloads the distribution matched to this Core, verifies its archive, -asks which Harnesses to install and where, installs them, starts the daemon and -checks its authenticated connection. The Session's required Harness must remain -selected. Its workspace is frozen at Session creation; the installer creates that -directory if necessary using your existing permissions. To choose a different -workspace, create a Session with that path. No administrator privileges or Docker -are required. - -For automation, append `--non-interactive --harness codex` and optionally -`--install-dir ABS` to the command. Multiple Harnesses use a comma-separated value, -for example `--harness codex,claude`. Missing required input fails without prompting. -Interactive installation defaults to a separate directory for each Environment: -`~/.oac/environments/` (or beneath `OAC_RUNTIME_HOME`). - -The command carries a 30-minute authorization restricted to this Environment and -Core build. Treat it as a temporary credential. Refresh the Session detail or -copy a fresh Web command after expiry. It cannot execute tasks or read files. -The installer generates a private connect-only credential file before claiming -its key, so a lost response can be retried without losing the credential. The -long-term secret never appears in the command or terminal. A different machine -cannot use the command to replace an already claimed key. Session deletion, -Project archival, expiry or a different Core build invalidates the authorization; -new commands never revive revoked credentials. - -Installation reports three separate results: **Installation**, **Daemon -connection**, and **Model configuration**. This workflow does not configure or -validate model access. If connection is not confirmed, inspect the reported local -log and the Session's connection status. Rerun with the same installation directory -to resume; completed components and credentials are retained and an existing -daemon is reused. After authentication failures, check the Environment credential -in Core. Do not remove the workspace or Session history to retry. - -Qualified releases publish separate Linux amd64, macOS arm64 and Windows amd64 -installers. Default Core installation carries only their version and checksum -catalog. The one-command bootstrap downloads only the current platform from the -fixed Release URL and verifies its checksum before extraction. It requires access -to that public download host; installation credentials stay on Core. Unsupported -platforms fail explicitly, and download failure never selects a different version. - -The explicit offline Core archive carries one copy of each installer outside the -Core image. Installing it retains the files in the installation's private -`native-installers` directory and makes them available through Core's same public -artifact endpoint. No external download is needed for these archives. Standalone -Core operators can set `OAC_NATIVE_INSTALLER_DIR` to the matched catalog directory, -with optional locally supplied archives. A corrupt local archive refuses startup; -missing catalog metadata disables one-command installation. Model access and -capability dependencies may still require networking. - -## Manual distribution installation - -The same installer also accepts an already-extracted distribution and an explicitly -supplied private executor credential, without the bootstrap command: - -```sh -./oac-daemon install --non-interactive --harness codex \ - --install-dir "$HOME/.oac/my-runtime" \ - --remote 'wss://core.example/api/v1/agent-daemon/ws' \ - --environment-id '11111111-2222-4333-8444-555555555555' \ - --workspace "$HOME/workspace" \ - --credential-file "$HOME/executor-credential.json" -"$HOME/.oac/my-runtime/bin/oac-daemon" start -``` - -Use `.\oac-daemon.exe` and native absolute paths in PowerShell. This manual mode -requires an existing workspace and starts only when `start` is invoked. Optional -`--capability-directory ABS` selects snapshot storage; `--tool-env-file ABS` -supplies tool/MCP variables. They do not introduce another installation workflow. -Build distributions on their target OS with `scripts/build-native-installer.mjs`; -`bundle.json` describes release content and checksums, not installation options. - -## Add Harnesses and operate the installation - -Run the original distribution's install command again with identical connection -options and the Harnesses to add. The installer retains already selected Harnesses, -checks compatible existing contents and adds only missing components. No default -deletion, replacement or upgrade occurs. All installation writes use one lock. -A component is published only after its copy passes checksum verification; -interrupted additions can reuse complete components on the next run. Installation -settings are committed only after all selected Harnesses pass readiness checks. - -The installed `bin/oac-daemon` locates its own installation. Use that executable -for `start`, `status`, `logs -n 100`, `logs -f` and `stop`. Direct `connect` is -rejected for an installed Runtime; `start` validates its components and selection. An explicit -`OAC_RUNTIME_HOME` overrides this location; keep it consistent if set. If adding a -Harness while the daemon is running, restart it to refresh Harness discovery. -`status` reports local profile/PID-file information only. Check **Host connection** -in Core or the Environment connection API for authenticated connection state. - -A connected Environment proves machine authentication, not model availability: -send a Turn to verify execution. To rotate or revoke the credential, see -[Rotate or revoke](/self-hosted-execution#rotate-or-revoke). - -An incompatible daemon version, component version or modified installation is -an explicit error. Use a separate installation directory; there is no old-version -upgrade, migration or automatic repair. Preserve previous files and history. -Stopping a daemon, cancelling a Turn or deleting a Session never removes the -user's machine, workspace, native history or capability snapshot. - -## Common preparation and execution - -After authenticated connection every Runtime follows the same flow: Harness -availability, workspace and capability preparation, fixed `installed.json`, then -execution, cancellation and recovery. Provider image contents and self-hosted -`capability_directories` enter the same parser and installation result. Adapters -receive Skill paths, Plugin results and MCP declarations from that snapshot. -Reconnect reuses it; new Sessions capture new configuration. Preparation or -recovery errors never trigger silent reinstall, replay or replacement native Sessions. - -Runtime initialization/package directories default to `initialization` and -`packages` under the installation. Managed images use their own storage layout -through `OAC_RUNTIME_INITIALIZATION_DIRECTORY` and `OAC_RUNTIME_PACKAGE_DIRECTORY`; -this does not change the preparation protocol or account permissions. npm and -Python dependencies use user-writable prefix/target directories. Setup uses Bash, -including Git Bash on Windows. Supplying `packages.system` in configuration is -rejected even when empty or null; managed images/templates must include system -dependencies before launch. Official API read responses retain the required -`system: []` field, which does not imply support for installing system packages. - -On Windows, stdio MCP commands named npm or npx (including explicit .cmd -paths) run through the selected installation's JavaScript entrypoint with Node. -Other batch wrappers require an explicit cmd.exe command and its arguments. - -For precedence and snapshot behavior of `--tool-env-file` with Session -preparation, see the [Environment contract](/environments-and-files#explicit-local-tool-environment). - -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/self-hosted-native.md) diff --git a/apps/docs/scripts/guides.json b/apps/docs/scripts/guides.json index 5ef23e97a..165a78af6 100644 --- a/apps/docs/scripts/guides.json +++ b/apps/docs/scripts/guides.json @@ -89,12 +89,6 @@ "title": "Self-hosted executors", "description": "Connect a user-owned Runtime to its Environment with a restricted executor credential." }, - { - "slug": "self-hosted-native", - "source": "docs/self-hosted-native.md", - "title": "Native daemon installation", - "description": "Install and operate a self-hosted daemon on Linux, macOS or Windows." - }, { "slug": "console", "source": "docs/web/README.md", diff --git a/apps/web/e2e/monitoring.spec.ts b/apps/web/e2e/monitoring.spec.ts index b158037ac..c48b1beea 100644 --- a/apps/web/e2e/monitoring.spec.ts +++ b/apps/web/e2e/monitoring.spec.ts @@ -81,7 +81,7 @@ test("shows a self-hosted Session's install command, issues its credential once, // Web displays Core-provided commands with short-lived installation authority. const install = section.getByRole("region", { name: "Connect a host" }); await expect(install.getByLabel("Executor install command").locator("pre")).toHaveText("bash fixture-bootstrap --authorization fixture-short-lived"); - await expect(install.getByRole("link")).toHaveAttribute("href", /docs\/self-hosted-native.md$/); + await expect(install.getByRole("link")).toHaveAttribute("href", /docs\/getting-started\/self-hosted.md$/); await install.getByRole("combobox", { name: "Host platform" }).click(); await page.getByRole("option", { name: "Windows · PowerShell" }).click(); await expect(install.locator("pre")).toContainText("fixture-bootstrap.ps1 -Authorization fixture-short-lived"); diff --git a/apps/web/src/features/sessions/ExecutorInstallPanel.tsx b/apps/web/src/features/sessions/ExecutorInstallPanel.tsx index 23d4ac62a..94e537edb 100644 --- a/apps/web/src/features/sessions/ExecutorInstallPanel.tsx +++ b/apps/web/src/features/sessions/ExecutorInstallPanel.tsx @@ -38,7 +38,7 @@ export function ExecutorInstallPanel({ install, archived, connected = false }: {

{t(archived ? "executor.install.archived" : "executor.install.steps")}

- {t("executor.install.guide")} + {t("executor.install.guide")} {install.kind === "ready" ? <> { if (value === "posix" || value === "powershell") setShell(value); }} /> diff --git a/contracts/agents-api/environments.md b/contracts/agents-api/environments.md index 8ae8ec6b6..1f1ebfaea 100644 --- a/contracts/agents-api/environments.md +++ b/contracts/agents-api/environments.md @@ -523,9 +523,13 @@ tools, files or network access. Outer Environments own managed isolation, and unsupported network restrictions reject instead of silently running unrestricted. Native installation and validation limits are in the -[native guide](../../docs/self-hosted-native.md). Historical acceptance evidence +[self-hosted guide](../../docs/getting-started/self-hosted.md#platforms). Historical acceptance evidence stays limited to its recorded binaries and inputs. +On Windows, npm package installation and stdio MCP commands named `npm` or `npx` +(including their `.cmd` shims) run through the resolved npm installation's +JavaScript entrypoint with Node, without an extra shell. + ### Environment initialization and compute wake Environment initialization has pending, running, complete and failed states. diff --git a/contracts/agents-api/harness-onboarding.md b/contracts/agents-api/harness-onboarding.md index 20f047237..aeb7dae9b 100644 --- a/contracts/agents-api/harness-onboarding.md +++ b/contracts/agents-api/harness-onboarding.md @@ -43,7 +43,7 @@ same contract. Operating-system support belongs in the implementation and its qualification. The native daemon supports Linux, macOS and Windows; each adapter declares its qualified platform scope. Managed Providers remain Linux-only. A platform-neutral interface alone does not qualify a harness on another platform. -See [native Runtime validation](../../docs/self-hosted-native.md) for the current +See [self-hosted platforms](../../docs/getting-started/self-hosted.md#platforms) for the current acceptance limits. Runtime connection, installed capability snapshot, Session Executor and Turn each have their own lifetime; see [Executor and Turn lifetimes](../../docs/runtime-protocol.md#executor-and-turn-lifetimes). diff --git a/docs/api/README.md b/docs/api/README.md index 41f9a47f1..d200afdc4 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -55,7 +55,7 @@ core() { # core METHOD PATH [JSON body] | Archive a Project (revokes all keys) | `core POST /projects/$PROJECT_ID/archive` | | See harnesses and their default models | `core GET /harnesses` | | Set Codex's default model | `core PUT /harnesses/codex/model-configuration '{"model": "your-model-id", "model_provider": {"protocol": "responses", "base_url": "https://provider.example/v1", "api_key": "sk-..."}}'` | -| Issue an executor credential | See [self-hosted execution](../getting-started/self-hosted.md#operator-credential-management) | +| Issue an executor credential | See [executor credentials](../../contracts/agents-api/environment-executor-credentials.md#core-key-routes) | | Installation facts, including the API base URL | `core GET /installation` | Errors use the [Core error envelope](../../contracts/agents-api/core-errors.md). @@ -126,7 +126,7 @@ Creating or reading a `self_hosted` Session returns short-lived install commands Machine installers use `POST /api/v1/agent-daemon/installation` and its `/claim` subroute with the installation Bearer authorization. Qualified artifacts under `/api/v1/agent-daemon/install/{version}/` are public, immutable release content. See -the [native Runtime guide](../self-hosted-native.md) for expiry, retry, credential +the [self-hosted guide](../getting-started/self-hosted.md) for expiry, retry, credential ownership and platform rules. The console-local `GET`/`POST /console/installation/domain` surface uses the signed-in diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 86410c65d..1714424a0 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -17,7 +17,6 @@ API. Pick the path that matches your role. New here? Read the | [Configuration](../configuration.md) | `config.json`, default models, sandbox deployment | | [Nodes](nodes.md) | Adding, checking and removing managed nodes | | [Self-hosted execution](self-hosted.md) | Connecting your own machine to a Session | -| [Native Runtime](../self-hosted-native.md) | Platforms, installing and operating `oac-daemon` | | [Operations](operations.md) | Services, backups, keys, repair and troubleshooting | | [Web console](../web/README.md) | What the console shows and manages | diff --git a/docs/getting-started/self-hosted.md b/docs/getting-started/self-hosted.md index 6dddae568..085749597 100644 --- a/docs/getting-started/self-hosted.md +++ b/docs/getting-started/self-hosted.md @@ -1,24 +1,50 @@ # Self-hosted executors -A `self_hosted` Session runs on a machine the application owns. The application -creates the Session through `/v1` and receives a command that installs and connects -`oac-daemon`. Web displays the same command in the Session; it is optional. -Linux, macOS and Windows use the same Runtime protocol. Core-managed Providers -remain Linux-only. - -**The daemon is not a sandbox:** tools run with its launching user's permissions. -Use a container or VM if you need isolation; see +A `self_hosted` Session runs on a machine your application owns: a workstation, a +VM or a sandbox you manage. The application creates the Session through `/v1` and +receives a command that installs `oac-daemon`, starts it and connects it to Core. +Web shows the same command on the Session's page; it is optional. Core never +creates, stops or reclaims the machine. + +**The daemon is not a sandbox.** Tools run with the permissions of the account +that starts it and can reach whatever that account can. Use a container or VM when +you need isolation; see [Runtime and outer isolation](../design-principles.md#runtime-and-outer-isolation). -Platforms, prerequisites and commands are in the -[native Runtime guide](../self-hosted-native.md). +The daemon does not restrict network access, so a Template that requires a +network policy is rejected for a self-hosted Session. -An executor credential works for one Environment only, and for nothing else. The -Session must bring its own model provider; the installation default never applies -([why](../user-guide.md#which-model-provider-a-session-uses)). +The Session brings its own model provider; the installation default never applies +([why](../user-guide.md#which-model-provider-a-session-uses)). The machine gets an +executor credential that works for this one Environment and nothing else. -## Connect a host +## Platforms -1. Choose an absolute workspace path on the target host. Create a Session with +| Platform | Codex | Claude Code | MiniMax Code | +| --- | --- | --- | --- | +| Linux amd64 | Supported | Supported | Supported | +| macOS arm64 | Supported | Supported | Supported | +| Windows amd64 | Supported | Supported | Not supported | + +The installer brings its own pinned Node.js and Harness versions (listed in +[`scripts/build-native-installer.mjs`](../../scripts/build-native-installer.mjs)) +and leaves other installations of those tools untouched. On a platform without a +matching installer, the command fails. + +The machine needs: + +- HTTPS access to Core (plain HTTP only on loopback), and to the release download + host unless Core carries an offline copy of the installers; +- Bash for environment setup and MiniMax Code tools; on Windows, Git Bash, which + Claude Code also requires; +- Python and pip when the Session's packages need them; +- any system packages your setup needs. The daemon never runs apt, sudo or another + elevation command, so install them through the host's normal administration. + +No administrator privileges or Docker are needed. + +## Connect a machine + +1. Choose an absolute workspace path on the target machine. Create a Session with that path and the application's Project API key: ```python @@ -41,22 +67,52 @@ Session must bring its own model provider; the installation default never applie }, ) installation = session.model_dump()["x_agents_core"]["installation"] - print(installation["commands"]["posix"]) # use "powershell" for Windows + print(installation["commands"]["posix"]) # use "powershell" on Windows ``` -2. Run the returned command on the target machine. Select the Harnesses and - installation directory when prompted. Installation creates the workspace if - needed, starts the daemon and checks its connection. For automation, append - `--non-interactive --harness codex` and optionally `--install-dir ABS`. -3. Send a Turn. A connected Environment proves only the machine connection; the - first Turn checks the harness and model. +2. Run the command on the target machine with the account that should run the + tools. It downloads the installer matched to this Core, verifies its checksum, + asks which Harnesses to install and where, installs them, creates the workspace + if needed, starts the daemon and checks its connection. +3. Send a Turn. A connected machine proves only authentication; the first Turn + checks the Harness and the model. + +In Web, open the Session and copy the command under **Connect a host**. + +The command expires after 30 minutes. Read the Session again, or reload its page +in Web, for a fresh one. Treat the command as a temporary secret: it can claim the +machine's credential but cannot run work or read files. The +[credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) +describes what invalidates it. + +The installer reports three results: + +| Result | Meaning | +| --- | --- | +| **Installation** | The selected Harnesses passed their readiness checks | +| **Daemon connection** | Core confirmed the daemon's authenticated connection | +| **Model configuration** | Not checked; the first Turn uses the Session's model provider | + +If the connection is not confirmed within 45 seconds, the installer prints the +path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the +same command again with the same installation directory: completed components and +the credential are kept and a running daemon is reused. Do not remove the +workspace or the Session to retry. + +### Options for automation -In Web, open the **Self-hosted** Session and copy the command under **Connect a -host**. A command expires after 30 minutes; fetch the Session again for a fresh -one. The [native guide](../self-hosted-native.md#install-and-connect) covers retry, -platform prerequisites and credential storage. Core must be reachable from the -host with TLS outside loopback. Native installation does not require Docker. +Append these to the command: +| Option | Effect | +| --- | --- | +| `--non-interactive` | Never prompt; missing input fails | +| `--harness codex,claude,minimax` | Harnesses to install, comma-separated. Must include the Session's Harness | +| `--install-dir ABS` | Installation directory. Default: `environments/` under `~/.oac`, or under `OAC_RUNTIME_HOME` when set | +| `--capability-directory ABS` | Where [capability snapshots](#local-capability-directories) are stored. Default: `capabilities` in the installation directory | +| `--tool-env-file ABS` | A JSON file of string variables for tools and MCP servers; see [explicit local tool environment](../../contracts/agents-api/environments.md#explicit-local-tool-environment) | + +The workspace is fixed when the Session is created. For a different workspace, +create another Session. ## Local capability directories @@ -70,12 +126,13 @@ environment = { } ``` -Core accepts absolute Unix, Windows drive and UNC source paths without checking -its own filesystem. Runtime validates them using the executor host's path syntax. -Populate these directories before connecting the Runtime. They are ordinary paths visible -to that process; naming a directory does not mount it or create a sandbox. -Use `x_agents_core.environment` for the same project-owned Skills, Plugin archives, -files, dependencies, setup commands or Template used by a managed Session: +Paths are absolute in the machine's own syntax (Unix, Windows drive or UNC); the +daemon checks them, not Core. Fill these directories before the daemon connects. +They are ordinary paths visible to the daemon; naming one does not mount it or +create a sandbox. + +Use `x_agents_core.environment` for the same Project-owned Skills, Plugin archives, +files, packages, setup commands or Template used by a managed Session: ```python session = client.beta.agents.sessions.create( @@ -89,56 +146,75 @@ session = client.beta.agents.sessions.create( ``` The same extension works with `environment={"type": "openai_hosted"}`. Do not -repeat a field in both `environment` and the extension. The +repeat a field in both `environment` and the extension. Setup runs with the +daemon's account permissions. Deployment model keys are never sent to your +machine. + +Before the first Turn the daemon copies these sources into a snapshot. Reconnecting +reuses the snapshot even after you edit the sources; a new Session takes a new +snapshot. The [preparation contract](../../contracts/agents-api/environments.md#runtime-capability-preparation) -lists fields, merge rules and failure semantics. Your machine needs the selected -Harness and any required system dependencies; setup uses your account permissions. -Deployment model keys are never sent automatically to a user-managed machine. - -Runtime snapshots sources before native execution and uses the same parser and -`installed.json` format as managed bundles to supply Skills and Plugin MCP. -The native installer defaults the snapshot destination to `capabilities` under -`OAC_RUNTIME_HOME`; `--capability-directory` selects another local destination. -It is an operator setting, not a public API write destination. Reconnect reuses -installed contents even after source edits; a new Session captures its own -configuration. A missing or inconsistent snapshot fails preparation rather than -silently reinstalling. Local discovery does not create entries in the public -API-managed installation arrays. Snapshot file modes do not isolate the snapshot -from tools running as the same user. - -Closing an executor, cancelling a Turn or losing its connection preserves the -snapshot and workspace. The compute owner remains responsible for explicit -cleanup. [Historical qualification](../../contracts/agents-api/user-managed-runtime-v1.md) -records only its stated inputs and binaries; it does not qualify the current native -platforms or local capability preparation. +lists fields, merge rules, snapshot behavior and failures. -## Rotate or revoke +## Operate the installation + +The installation's `bin/oac-daemon` finds its own installation. Use it for: -| Action in Web | Effect | +| Command | Effect | | --- | --- | -| **Rotate** | The credential gets a new secret; the old secret stops working at once. Rotating a revoked credential restores it | -| **Revoke** | The credential stops working at once | +| `oac-daemon start` | Validate the installed Harnesses and start the daemon in the background | +| `oac-daemon status` | Show the local profile and process; not the connection | +| `oac-daemon logs -n 100`, `oac-daemon logs -f` | Print or follow the daemon log | +| `oac-daemon stop` | Stop the daemon | -To reconnect a native installation, stop it with `oac-daemon stop`, rotate the same -credential, replace the JSON at its configured credential-file path, and run -`oac-daemon start`. Keep the same `OAC_RUNTIME_HOME` for every command. Issuing a -new credential does not reconnect an Environment already bound to its first -credential; rotate that credential instead. Do not run `install` again over the -existing installation. +If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the +connection under **Host connection** on the Session's page in Web, or with the +[connection status](../../contracts/agents-api/environment-executor-credentials.md#connection-status). -Stopping the daemon keeps its workspace and native history. Deleting a Session -does not remove host files. In an archived Project, credentials cannot be issued -or rotated; revocation remains available. +To add a Harness, run the original install command again with the same connection +options and the Harness to add. The installer checks the existing contents, adds +only missing components and keeps the Harnesses already installed. Restart a +running daemon afterwards so it discovers the new Harness. -## Operator credential management +Stopping the daemon, cancelling a Turn or deleting the Session never removes the +machine's workspace, native history or capability snapshot. An installation from +another daemon version, or one whose files were changed, is refused. The installer +never upgrades, repairs or migrates it; install into a separate directory. -Operators can still issue, rotate or revoke executor credentials using a Core key -through Core's loopback port. This is not required for one-command onboarding. -See the [credential contract](../../contracts/agents-api/environment-executor-credentials.md) -for those routes and uncertain-response handling. +## Rotate or revoke + +In Web, the Session's **Executor credentials** list the machine's credential: + +| Action | Effect | +| --- | --- | +| **Rotate** | The credential gets a new secret; the old secret stops working at once. On a revoked credential the action is **Restore** | +| **Revoke** | The credential stops working at once | -## Installation scope +To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the +JSON at its configured credential-file path with the new credential, and run +`oac-daemon start`. Do not run `install` again over the existing installation, and +do not issue a second credential: the Environment stays bound to the credential it +first connected with. + +In an archived Project, credentials cannot be issued or rotated; revocation remains +available. Operators can manage credentials with the Core key; see the +[credential contract](../../contracts/agents-api/environment-executor-credentials.md#core-key-routes). + +## Install from an extracted distribution + +The same installer accepts an already extracted distribution and a credential +file issued by an operator, without the install command: + +```sh +./oac-daemon install --non-interactive --harness codex \ + --install-dir "$HOME/.oac/my-runtime" \ + --remote 'wss://core.example/api/v1/agent-daemon/ws' \ + --environment-id '11111111-2222-4333-8444-555555555555' \ + --workspace "$HOME/workspace" \ + --credential-file "$HOME/executor-credential.json" +"$HOME/.oac/my-runtime/bin/oac-daemon" start +``` -The [native installation guide](../self-hosted-native.md#add-harnesses-and-operate-the-installation) -owns supported installation operations and component validation. Stopping a Runtime -or deleting a Session never removes the user's files or native history. +Use the Session's `remote_url` and Environment ID. In PowerShell, run +`.\oac-daemon.exe` with native absolute paths. This mode needs an existing +workspace and does not start the daemon until you run `start`. diff --git a/docs/self-hosted-native.md b/docs/self-hosted-native.md deleted file mode 100644 index 647088810..000000000 --- a/docs/self-hosted-native.md +++ /dev/null @@ -1,172 +0,0 @@ -# Native self-hosted Runtime - -Use the same `oac-daemon` on a user-managed Linux, macOS or Windows machine. -Physical machines, VMs and user-owned sandboxes use the same installer. Core does -not create or reclaim these machines. Core-managed Docker, E2B and microsandbox -Providers remain Linux-only and receive prebuilt Runtime images or templates. - -**The daemon is not a sandbox.** Tools can access whatever its account can. Use an -outer container or VM when you need isolation; see -[Runtime and outer isolation](design-principles.md#runtime-and-outer-isolation). -`disabled` and `restricted` network modes need an outer layer that enforces them. - -## Platforms and prerequisites - -| Platform | Codex | Claude Code | MiniMax Code | -| --- | --- | --- | --- | -| Linux | Supported | Supported | Supported | -| macOS | Supported | Supported | Supported | -| Windows | Supported | Supported | Unsupported by the current adapter | - -Use a distribution built for the machine's OS and architecture. Windows support -is validated on a native CI runner; manual Windows machine acceptance is not yet -recorded. Native Linux/macOS runs and native CI qualify the corresponding bundles. -Supported does not mean every model provider or optional native feature works in -every combination. Session capabilities are checked by the existing Harness contract. - -The distribution contains Node 22.22.0/npm and the selected release's components: -Codex 0.153.4, Claude Agent SDK 0.3.269 with native Claude Code 2.1.269 and the -project's adapter, and the patched MiniMax Code 0.4.12 companion on Unix. Arbitrary -official CLI installations are not adopted. They remain untouched while the -installer creates its private, verified copy. Compatible components from this -same installation are reused after checking their version, contents and startup. - -Claude on Windows requires Git Bash. Bash is also needed for Runtime setup and -MiniMax tools. Node/npm and MiniMax's ripgrep are included; Python/pip, when needed -by a Session's capability dependencies, must be available. Missing system -components are reported. Install those through the host's normal administration -process; the daemon never runs apt, sudo or an elevation command. - -## Install and connect - -Create a Session using the public Agents API with `environment.type: "self_hosted"`. -The response retains the official Environment `id` and `remote_url`, and adds -`x_agents_core.installation` with `commands.posix`, `commands.powershell` and -`expires_at`. Execute the command for your target platform. Core Web shows the -same commands in the **Self-hosted** Session's connection section; Web is not a -prerequisite for API callers. - -The command downloads the distribution matched to this Core, verifies its archive, -asks which Harnesses to install and where, installs them, starts the daemon and -checks its authenticated connection. The Session's required Harness must remain -selected. Its workspace is frozen at Session creation; the installer creates that -directory if necessary using your existing permissions. To choose a different -workspace, create a Session with that path. No administrator privileges or Docker -are required. - -For automation, append `--non-interactive --harness codex` and optionally -`--install-dir ABS` to the command. Multiple Harnesses use a comma-separated value, -for example `--harness codex,claude`. Missing required input fails without prompting. -Interactive installation defaults to a separate directory for each Environment: -`~/.oac/environments/` (or beneath `OAC_RUNTIME_HOME`). - -The command carries a 30-minute authorization restricted to this Environment and -Core build. Treat it as a temporary credential. Refresh the Session detail or -copy a fresh Web command after expiry. It cannot execute tasks or read files. -The installer generates a private connect-only credential file before claiming -its key, so a lost response can be retried without losing the credential. The -long-term secret never appears in the command or terminal. A different machine -cannot use the command to replace an already claimed key. Session deletion, -Project archival, expiry or a different Core build invalidates the authorization; -new commands never revive revoked credentials. - -Installation reports three separate results: **Installation**, **Daemon -connection**, and **Model configuration**. This workflow does not configure or -validate model access. If connection is not confirmed, inspect the reported local -log and the Session's connection status. Rerun with the same installation directory -to resume; completed components and credentials are retained and an existing -daemon is reused. After authentication failures, check the Environment credential -in Core. Do not remove the workspace or Session history to retry. - -Qualified releases publish separate Linux amd64, macOS arm64 and Windows amd64 -installers. Default Core installation carries only their version and checksum -catalog. The one-command bootstrap downloads only the current platform from the -fixed Release URL and verifies its checksum before extraction. It requires access -to that public download host; installation credentials stay on Core. Unsupported -platforms fail explicitly, and download failure never selects a different version. - -The explicit offline Core archive carries one copy of each installer outside the -Core image. Installing it retains the files in the installation's private -`native-installers` directory and makes them available through Core's same public -artifact endpoint. No external download is needed for these archives. Standalone -Core operators can set `OAC_NATIVE_INSTALLER_DIR` to the matched catalog directory, -with optional locally supplied archives. A corrupt local archive refuses startup; -missing catalog metadata disables one-command installation. Model access and -capability dependencies may still require networking. - -## Manual distribution installation - -The same installer also accepts an already-extracted distribution and an explicitly -supplied private executor credential, without the bootstrap command: - -```sh -./oac-daemon install --non-interactive --harness codex \ - --install-dir "$HOME/.oac/my-runtime" \ - --remote 'wss://core.example/api/v1/agent-daemon/ws' \ - --environment-id '11111111-2222-4333-8444-555555555555' \ - --workspace "$HOME/workspace" \ - --credential-file "$HOME/executor-credential.json" -"$HOME/.oac/my-runtime/bin/oac-daemon" start -``` - -Use `.\oac-daemon.exe` and native absolute paths in PowerShell. This manual mode -requires an existing workspace and starts only when `start` is invoked. Optional -`--capability-directory ABS` selects snapshot storage; `--tool-env-file ABS` -supplies tool/MCP variables. They do not introduce another installation workflow. -Build distributions on their target OS with `scripts/build-native-installer.mjs`; -`bundle.json` describes release content and checksums, not installation options. - -## Add Harnesses and operate the installation - -Run the original distribution's install command again with identical connection -options and the Harnesses to add. The installer retains already selected Harnesses, -checks compatible existing contents and adds only missing components. No default -deletion, replacement or upgrade occurs. All installation writes use one lock. -A component is published only after its copy passes checksum verification; -interrupted additions can reuse complete components on the next run. Installation -settings are committed only after all selected Harnesses pass readiness checks. - -The installed `bin/oac-daemon` locates its own installation. Use that executable -for `start`, `status`, `logs -n 100`, `logs -f` and `stop`. Direct `connect` is -rejected for an installed Runtime; `start` validates its components and selection. An explicit -`OAC_RUNTIME_HOME` overrides this location; keep it consistent if set. If adding a -Harness while the daemon is running, restart it to refresh Harness discovery. -`status` reports local profile/PID-file information only. Check **Host connection** -in Core or the Environment connection API for authenticated connection state. - -A connected Environment proves machine authentication, not model availability: -send a Turn to verify execution. To rotate or revoke the credential, see -[Rotate or revoke](getting-started/self-hosted.md#rotate-or-revoke). - -An incompatible daemon version, component version or modified installation is -an explicit error. Use a separate installation directory; there is no old-version -upgrade, migration or automatic repair. Preserve previous files and history. -Stopping a daemon, cancelling a Turn or deleting a Session never removes the -user's machine, workspace, native history or capability snapshot. - -## Common preparation and execution - -After authenticated connection every Runtime follows the same flow: Harness -availability, workspace and capability preparation, fixed `installed.json`, then -execution, cancellation and recovery. Provider image contents and self-hosted -`capability_directories` enter the same parser and installation result. Adapters -receive Skill paths, Plugin results and MCP declarations from that snapshot. -Reconnect reuses it; new Sessions capture new configuration. Preparation or -recovery errors never trigger silent reinstall, replay or replacement native Sessions. - -Runtime initialization/package directories default to `initialization` and -`packages` under the installation. Managed images use their own storage layout -through `OAC_RUNTIME_INITIALIZATION_DIRECTORY` and `OAC_RUNTIME_PACKAGE_DIRECTORY`; -this does not change the preparation protocol or account permissions. npm and -Python dependencies use user-writable prefix/target directories. Setup uses Bash, -including Git Bash on Windows. Supplying `packages.system` in configuration is -rejected even when empty or null; managed images/templates must include system -dependencies before launch. Official API read responses retain the required -`system: []` field, which does not imply support for installing system packages. - -On Windows, stdio MCP commands named npm or npx (including explicit .cmd -paths) run through the selected installation's JavaScript entrypoint with Node. -Other batch wrappers require an explicit cmd.exe command and its arguments. - -For precedence and snapshot behavior of `--tool-env-file` with Session -preparation, see the [Environment contract](../contracts/agents-api/environments.md#explicit-local-tool-environment). diff --git a/docs/user-guide.md b/docs/user-guide.md index 3cfb3d832..0f3351213 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -142,7 +142,7 @@ After a lost response or connection: 3. Never resend without a key; you may run the work twice. On a self-hosted machine, restart the same installation to keep its workspace and -history; see [operating the installation](self-hosted-native.md#add-harnesses-and-operate-the-installation). +history; see [operating the installation](getting-started/self-hosted.md#operate-the-installation). ## Diagnose a failure diff --git a/docs/web/protocol-coverage.md b/docs/web/protocol-coverage.md index 87554a2dc..1f4d15698 100644 --- a/docs/web/protocol-coverage.md +++ b/docs/web/protocol-coverage.md @@ -83,7 +83,7 @@ Resource-specific boundaries: The credential operations below also serve the native daemon on Linux, macOS and Windows. The existing **Connect a host** download command is the Linux container -installer; use the [native installation guide](../self-hosted-native.md) for +installer; use the [self-hosted guide](../getting-started/self-hosted.md) for `oac-daemon install` and lifecycle commands. Native credential rotation replaces the configured credential file and restarts the daemon, without rerunning install. From 946edd5974b94dbabb540633433b1b69f77f155d Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 06:46:33 +0000 Subject: [PATCH 3/8] docs: move installer design rules to deploy/install/README.md Configuration and apply, versions and the lock, install-time sandbox selection, accounts, managed HTTPS, output, the node installer, the download contract, image identity and the native daemon installer, without the historical notes. --- CONTRIBUTING.md | 2 +- deploy/install/README.md | 337 ++++++++++++++++++++++++++ docs/api/web-management.md | 2 +- docs/web/architecture.md | 2 +- packages/claude-sdk-adapter/README.md | 2 +- 5 files changed, 341 insertions(+), 4 deletions(-) create mode 100644 deploy/install/README.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ceca6ab0..258f02624 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -388,7 +388,7 @@ settings remain unchanged. No old label is accepted as a fallback. Historical Runtime and project-version upgrades are not supported. Do not ship retired installer conversion implementations; preserve rejection guards under the -[installer lifecycle contract](docs/maintainers.md#distribution-and-installer-rules). Preserve +[installer lifecycle contract](deploy/install/README.md#versions-and-the-lock). Preserve older installations, Runtime files, provider resources and Session history; install the current release separately. Startup never verifies and rebinds historical allocations or accepts node deployments without a valid specification. Keep the diff --git a/deploy/install/README.md b/deploy/install/README.md new file mode 100644 index 000000000..f01d2da74 --- /dev/null +++ b/deploy/install/README.md @@ -0,0 +1,337 @@ +# Installer design rules + +This directory holds the Core/Web installer, the `oac` command and the node +installer. The self-hosted daemon installer (`oac-daemon install`) lives in +`apps/parsar-daemon/internal/cli`; its rules are in +[Native daemon installer](#native-daemon-installer). These are the rules to keep +when you change them. Operator usage is in the +[installation guide](../../docs/getting-started/install.md), the +[node guide](../../docs/getting-started/nodes.md) and the +[self-hosted guide](../../docs/getting-started/self-hosted.md); the version policy +is in [Operations](../../docs/getting-started/operations.md#installation-version-policy). +Building and publishing distributions is in the +[maintainer guide](../../docs/maintainers.md). `make check-distribution` runs this +directory's tests. + +| Module | Role | +| --- | --- | +| `install.sh`, `install.py` | Core/Web installer: host checks, fresh installation and same-bundle repair | +| `oac_cli.py` | The `oac` command (`status`, `start`, `stop`, `apply`, `domain`, `rotate-core-key`), packaged as `oac.pyz` | +| `config.schema.json`, `config_model.py` | The `config.json` schema, its defaults and the subset validator | +| `configuration.py` | Everything under `generated/` and the service input digests | +| `ingress.py`, `ingress_config.py` | Managed HTTPS gateway and the domain operation | +| `native_service.py` | Native Core as a systemd user service | +| `native_installers.py` | Retains the native daemon installer catalog and offline archives | +| `sandbox_setup.py` | The install-time sandbox selection through Core's administrator API | +| `distribution.py` | Shared manifest verification, verified downloads and image identity | +| `node_install.py`, `node_spec.py`, `node_generations.py` | Node installer and generation helper, packaged as `node-install.pyz` | +| `install_display.py`, `install_output.py`, `node_output.py` | Shared terminal formatting and completion guidance | +| `model_provider_sessions.py` | Read-only count of Sessions without a frozen model provider | +| `installer_fakes.py`, `acceptance.py`, `test_*.py` | Test fakes, opt-in real-model acceptance and tests | + +## Scope + +- Installers prepare hosts and services. Core alone owns Session allocation, + initialization, cancellation, snapshots and cleanup. No installer creates an + execution Session or supplies a model credential. +- The Core/Web installer never adds its own host as a node, imports no Runtime + image and gives Core neither the Docker socket nor host devices. Nodes are added + afterwards from Web, this host included. +- Ingress is an installation concern, independent of Runtime and Sandbox Provider + selection. +- A repeated installation or repair never changes provider identity, the backend + namespace or native history. The database, Projects and their keys, provider + identity and the credential encryption key survive repair. +- Recovery never deletes data and never prunes containers, volumes or images. +- Do not add another launcher, scheduler, supervisor or recovery path. + +## Configuration and apply + +- Every process setting has one home: the installation's private `config.json`, + described by `config.schema.json`. Keep the schema, `config_model.py`, the + generator and the reference tables that `scripts/config-reference.py` renders + into [configuration](../../docs/configuration.md) and + [installation options](../../docs/getting-started/install-options.md) in step. +- Installation flags only seed `config.json`. A rerun of the installer accepts only + `--install-dir` and repairs. +- `oac apply` validates `config.json`, derives `generated/` and converges on what + actually runs. Each service carries the digest of its inputs (Compose label + `io.oac.inputs`, native `OAC_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 an interrupted one. Apply + contacts running services at the last applied address before changing listeners. +- Health checks, setup, apply and generated service files derive addresses from + the same `config.json`. `host` selects the gateway listener for managed ingress + and the Core and Web listeners for external ingress. A non-loopback external bind + requires an HTTPS public origin. +- Runtime settings stay in PostgreSQL and change through Web or `/core/v1`. + Secrets live once each in `secrets/`; identity and installation facts live in + the tool-written `state.json` (format 2, with an `oac-` Compose project). + `config.json` has its own format, 1. +- Core reads only its environment and has no configuration loader; it serves the + non-secret snapshot at `GET /core/v1/installation`. Do not add a second operator + configuration file, loader precedence, hot reload, fallback to earlier setting + names or an embedded Core node. +- Names: Core settings use `OAC_*`, Web settings `OAC_WEB_*`, shared Go logging + `OAC_LOG_*`. A renamed setting fails startup even when empty or when the new name + is also set; report every matching name without its value. Executables are + `oac-core`, `oac-core-migrate`, `oac-core-device`, `oac-core-environment-key`, + `oac-node`, `oac-web`, and the Core-host E2B helper `oac-e2b-provider` under + `/opt/oac/e2b` in the image. The default installation directory is + `~/.oac/core`; generated files carry `x-oac` annotations. + +## Versions and the lock + +- Install only into an empty directory, or repair the same source revision. + Refuse older formats and different revisions before changing anything; keep + their data and direct the operator to install separately. Distributions carry + only current installation code: no conversion, migration or binary replacement. + Keep the refusal checks and their tests. +- The packaged `oac.pyz` embeds its build revision and refuses a `state.json` + whose `source_commit` differs. +- The installer and every mutating `oac` command share `.oac.lock`. The installer + holds it across creation, payload, native service and launcher repair, and apply, + calling the already-locked apply implementation without locking again. Never + unlink or replace the lock file, even after an interrupted fresh installation; + its inode must stay stable. + +## Install-time sandbox selection + +`--sandbox docker|microsandbox|e2b|none` (default `microsandbox`; only `none` with +`--web-only`) is a one-time action. After the services are healthy, the installer +posts `/core/v1/sandbox/deployment` once, as Web's setup would, and never on a +repair. The choice is not written to `config.json`; PostgreSQL owns it, and an +existing database selection is never overwritten. + +- Docker and microsandbox use Web's Standard size from + `apps/web/src/features/sandbox/standard-sizes.json`, which the distribution + build copies into the bundle. Keep no other copy of those values. +- `docker` prints its weaker isolation and needs a y/N confirmation or + `--accept-docker-risks` before anything is created. +- `e2b` needs a non-loopback HTTPS `public_url`, `--e2b-api-key-file` and + `--e2b-template`; otherwise the installer refuses before installing anything. +- A Docker or microsandbox selection with a loopback `public_url` is saved, but no + node can serve it until `public_url` is guest-reachable HTTPS. +- `--sandbox-provider` and `--provider` fail with a message naming `--sandbox`. + +## Accounts and permissions + +- The Core/Web installer runs as the launching account, root included, in a + writable installation directory. It never invokes sudo, switches accounts or + changes Docker permissions. Check the actual platform, Docker and directory + prerequisites; root alone is no reason to refuse. +- `--native-core` runs Core as a systemd user service of that account, with + lingering, and keeps PostgreSQL and Web in Compose with a private loopback + database port. Native Core needs no KVM or node assets. +- Installation state and secrets are private under `~/.oac/`. No credential enters + build arguments, image layers, browser bundles or diagnostic output. The Compose + file is confidential. +- The distribution build uses umask 022 so non-root service users can read the + payload; installation credentials and state keep their private modes. + +## Managed HTTPS + +A default combined Docker installation adds two Compose services from one pinned +image: `gateway` runs Caddy, and `installation` runs the packaged +`oac domain-server`. The latter runs with the installing account's UID and its +Docker socket access and calls the same locked apply implementation. Its only +request surface is the private `ingress/api/api.sock`, with Core-key +authentication and one typed domain action. Core and Web get no Docker socket, host +process authority or writable installation configuration. Web gets only the +private API socket directory, never Caddy's admin socket. + +- The gateway owns ports 80 and 443 and the initial Web port. Caddy issues and + renews certificates and keeps its private data in `ingress/data`. + `generated/Caddyfile` is derived from `config.json`, and apply reloads it through + the private Caddy socket even when container inputs already match. +- A domain change keeps the old entry point while it verifies a trusted + certificate and an installation-specific response over HTTPS. Only then does it + set `public_url` and call the common apply path. Failure restores the previous + configuration and reports incomplete recovery; failed retries restore through + the common apply even after a partial change. +- The operation record keeps the last successfully applied public address. Apply + and start update it after gateway verification and service health checks; + generated files alone never prove that a new address is active. A successful + apply reconciles the domain operation status after verifying the running + services. +- The domain operation refuses unrelated pending `config.json` edits and shares + `.oac.lock` with the CLI. Its status file is bookkeeping and the recovery + receipt; `config.json` stays the source of desired settings. An interrupted + operation keeps its desired files and a visible failure and retry state; it + never creates another service project or deletes execution data. +- A Web restart ends console sessions, so the UI gives the new HTTPS sign-in + address instead of treating a dropped request as success. +- Split and native installations use external ingress and report that automatic + Web domain setup is unavailable. + +## Output + +- Progress describes the operation about to run. Do not imply fresh health checks + on a no-change repair. +- Terminal styling is optional: honor `NO_COLOR` and keep redirected logs plain. +- Summaries show credential file locations, never their values. +- `install_display.py` owns shared terminal formatting; `install_output.py` and + `node_output.py` own the completion guidance. Ship and checksum the display + modules in both the node bootstrap and its retained helper. +- A node summary reports success only after Core connection and provider readiness + are confirmed. +- Output from the service account stays plain and passes through the + terminal-control sanitizer. + +## Node installer + +The node installer runs as root and prepares the host for one node per +installation. + +- It creates or adopts the `oac-node` system user, adds it to the `docker` or + `kvm` group (no other group), and installs one root-owned system service per + installation that runs the node program as `User=oac-node`. Nodes on a host + share that account, so a host serves one Core. +- Docker group membership makes that user, and so the node, root-equivalent on the + host; that is inherent to Docker sandboxes. microsandbox needs only `kvm`, user + KVM access and the Linux runtime libraries. +- Node configuration and identity live under `~/.oac/nodes//` in + the node account's home (`/var/lib/oac-node`); microsandbox uses a separate short + private Runtime home. +- The node service owns its provider processes outside the Core container. + `KillMode=process` keeps resident microVM and helper processes across a service + restart. The service restarts after failures with no start limit, so a node + outlasts a Core outage, and stops restarting when the node program exits 78 + because Core answered 401 to its credential (a removed node). +- It never installs Docker, KVM or packages and never changes device permissions. + It refuses SELinux-enforcing hosts and changes nothing when a check fails. The + enrollment token comes only on standard input, never in arguments or the + environment. +- Files the service account owns are read, written and deleted only with that + account's credentials, never by root. That work runs in a child that starts its + own session with `/dev/null` as input, joins a new session keyring and dies with + its parent; root shows its output only as plain text (terminal controls become + `?`). SIGINT, SIGHUP and SIGTERM stop that child and what it started. +- Root never runs a file the service account can write, opens a URL it wrote, or + follows a link in its home. Capture the trusted bootstrap bytes before dropping + to the service account and pass them through the fork; the service account + writes its own retained generation helper. Never open the caller's private + download directory to it or let root write into service-owned state. +- The generated bootstrap passes only the six standard HTTP/HTTPS proxy and bypass + variables through sudo and gives both spellings the lowercase value when present, + even if empty, so curl, urllib and the Go registration command follow the same + rules. The installation child keeps just those names beside its fixed + environment. Proxy values stay out of arguments, saved configuration, service + units and diagnostics; never use broad sudo environment inheritance. This covers + installation downloads only, not the node service. +- `--uninstall` removes a node only after Core rejects its credential. It never + touches sandboxes, volumes or images (the Runtime image and the microsandbox + store stay), deletes the account only when the installer created it and no node + remains, and otherwise removes only the groups it added. +- Refuse resources of an older product name for the same installation ID; never + adopt them or remove another installation's resources. + +## Download contract + +The distribution manifest is the one download contract for the Core, node and +self-hosted installers: flat versioned file names, and the compressed and +unpacked size and SHA-256 of the Runtime. + +- The default installation downloads the Core, Web and PostgreSQL payloads, never + the Runtime image or node execution artifacts. Core's image never acquires + execution-only payloads. The offline archive stays an explicit option. +- A node obtains bootstrap metadata from the console that generated its command, + or from a local offline bundle. Web serves artifacts it has locally and + redirects missing declared execution artifacts to the versioned HTTPS release + base in the verified manifest. Web never downloads or caches those bytes. +- Only artifact requests may follow HTTPS redirects, and only without credentials + or cookies. Metadata and enrollment requests stay on the configured console. + The console publishes only fixed non-secret files and declared artifact names. +- Download into private temporary files, verify size and SHA-256 before an + atomic rename, resume interrupted transfers, and reuse only verified cache + entries or exact image identities. Never select a release other than the + pinned one. +- Python zipapps bundle the shared resolver with each remote bootstrap. The node + asset includes the `oac-node` binary. +- Release downloads are anonymous. Never add repository credentials to installed + node or Runtime configuration. +- Manual builds use the `build-` release tag and tag builds the `v*` tag. + The manifest's download base must match the release tag; artifact file names and + source provenance keep the full source SHA. + +## Image identity + +The manifest's `images` records each exported image's config digest, and +`image_manifest_digests` its OCI manifest or index digest. Derive and verify both +from the same archive, including its referenced config and layer bytes, and +require the build host's selected image ID to match one of them. + +Docker's classic image store identifies images by config digest, and its +containerd store by the OCI descriptor. The build therefore takes the digest the +local store resolves from BuildKit's build metadata, never the `--iidfile` config +digest alone, and disables provenance attestations so each image and archive holds +one platform manifest in both stores. For the same reason the default PostgreSQL +image is pinned by its linux/amd64 platform manifest digest: a pulled +multi-platform tag keeps its whole index in the containerd store, and its export +holds every platform. + +The Core, node and self-hosted installers share one resolver for these identities. +It confirms Linux amd64 and the returned immutable local ID, and service and +provider configuration and Runtime launches use that ID. Tags never replace +identity verification. The microsandbox `runtime_ref` is independent of Docker's +local store identity. + +## Native daemon installer + +`oac-daemon install` installs the daemon and selected Harnesses on a self-hosted +Linux, macOS or Windows machine; the +[credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) +covers the grant it claims. + +- Interactive selection and CLI-only installation share one options and + validation path. There is no installation-options file. The saved installation + state and explicitly supplied credential and tool-variable files serve runtime + operation, not a second configuration language. +- Each release bundles pinned Node.js and npm, the native Harnesses and their + adapter assets. Registration lives in the CLI, and native activation and + readiness in each adapter's optional `agent.Installation` descriptor. Core never + selects native paths or OS-specific steps. +- Bootstrap scripts only download and extract the current platform's archive, + after verifying the checksum Core provides. Installation, startup, connection + verification and execution stay common. Native bundles must match Core's source + revision and Runtime wire version. +- Neither Core installation nor repair downloads native payloads. Core serves the + local offline archives or redirects to the catalog URL without proxying or + caching; it verifies local archives before serving, and a corrupt local archive + fails closed. +- Every mutation holds the installation directory lock. Publish complete, + checksum-verified components from staging, then commit the configuration after + native readiness passes. A rerun with the same connection settings adds the + selected Harnesses and validates existing contents. Never overwrite, upgrade, + repair or migrate installed components; missing, modified, wrong-platform or + incompatible content is an explicit error. A partial addition keeps the old + configuration and reusable complete components and removes nothing. +- Serialize background PID inspection and publication so concurrent starts cannot + create two daemons. An installed daemon registers only the adapter kinds its + verified installation manifest names; other Harness executables on `PATH` + cannot extend it. Direct `connect` refuses an installed Runtime and points to + `start`. +- The installer runs as the current user in writable directories and never + elevates. Subprocess diagnostics never expose sensitive parameters or + environment values. Readiness checks take the installer's cancellation context + and reap their processes before returning. +- Report installation, authenticated connection and model configuration as + separate results. Starting execution never downloads or installs Harnesses. + Stop and reconnect keep capability snapshots and native Session state. +- `scripts/build-native-installer.mjs` validates pins and startup, hashes every + component file, accepts only contained regular files and rejects escaping links. + For the native bundle, Claude's frozen `pnpm deploy` export is reinstalled with + the hoisted linker before contained links are flattened; the Runtime image's + Claude archive is unchanged. The `native-check` workflow builds and tests + installation, addition and reuse, missing arguments and the unsupported Windows + MiniMax case on Linux, macOS and Windows. + +## Validation + +`make check-distribution` covers the production proxy, the installation rules, +release metadata and native catalog assembly, including bundle manifests larger +than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). A real +bundle check covers default and provider selection, component modes, connecting to +an existing Web, public native execution and restart retention. Diagnostics report observed service health, never +fabricated model or environment readiness. Runtime observations belong to Core; do +not add monitoring or lifecycle tracking to the installer or the landing site. diff --git a/docs/api/web-management.md b/docs/api/web-management.md index fb8952e4d..a0ec241d0 100644 --- a/docs/api/web-management.md +++ b/docs/api/web-management.md @@ -56,7 +56,7 @@ failures retain the sign-in error shape above. Poll the same-origin GET while preparing. Applying the change restarts Web and ends its sign-in sessions; provide a link to the submitted HTTPS origin for a fresh login. A dropped request or cross-origin browser probe does not prove -success. The [installer contract](../maintainers.md#managed-https-ownership) owns +success. The [installer contract](../../deploy/install/README.md#managed-https) owns certificate verification, locking, retry and rollback. ## Console to Core diff --git a/docs/web/architecture.md b/docs/web/architecture.md index b2a601604..ca076951e 100644 --- a/docs/web/architecture.md +++ b/docs/web/architecture.md @@ -44,7 +44,7 @@ exception: the console authenticates the same browser session and origin, then calls the installer's private Unix socket with its server-held Core key. This is not a Core `/core/v1` route and does not use `AdminClient`. It can configure only the managed domain; it cannot submit shell commands or arbitrary process settings. -The [installer rules](../maintainers.md#managed-https-ownership) own application, +The [installer rules](../../deploy/install/README.md#managed-https) own application, certificates and recovery. Before a domain is configured, the console accepts same-origin HTTP requests at literal IP addresses; after apply, only the configured HTTPS origin is accepted. diff --git a/packages/claude-sdk-adapter/README.md b/packages/claude-sdk-adapter/README.md index 0b32bcd37..8fe0891f2 100644 --- a/packages/claude-sdk-adapter/README.md +++ b/packages/claude-sdk-adapter/README.md @@ -240,7 +240,7 @@ failed readiness reports an unavailable descriptor with a rejecting factory. Runtime checks establish local readiness, not provider authentication. Installed daemons use `start` and their verified installation manifest for adapter selection and activation; ambient activation variables cannot extend that selection. See -[the native installation contract](../../docs/maintainers.md#native-daemon-and-harness-installation). +[the native installation contract](../../deploy/install/README.md#native-daemon-installer). SDK state lives under `paths.ProfileDir(profile)/runtime/claude-sdk`, independently of the replaceable runtime bundle. Both the entrypoint and managed state root must From d348de9a738fcb54dd749bcdd649fe779f9bf73c Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 06:46:41 +0000 Subject: [PATCH 4/8] docs: limit the maintainer guide to build and release docs/maintainers.md now covers distribution builds, native installers, Runtime image and helper builds (moved from the deploy and tool READMEs, with the msb archive checksum), standalone Core builds, publication, candidates, CI and running Core without the installer, which absorbs the standalone container guide. Fix the stale five-command count and the release job ordering. Trim the standalone archive README to its own steps and fix its link targets. Release promotion rules move to the promote-qualified-release.py docstring and the E2B template umask rule to the template builder's README. --- docs/maintainers.md | 973 ++++++------------ scripts/name-allowlist.json | 20 - scripts/promote-qualified-release.py | 64 +- services/agents-api/CONTAINER.md | 111 -- services/agents-api/README.md | 6 +- services/agents-api/RELEASE.md | 183 +--- services/agents-api/deploy/claude/README.md | 18 +- services/agents-api/deploy/codex/README.md | 13 +- services/agents-api/deploy/e2b/README.md | 5 +- services/agents-api/deploy/mcode/README.md | 16 +- .../agents-api/deploy/microsandbox/README.md | 41 +- .../agents-api/tools/e2b-provider/README.md | 24 +- .../tools/microsandbox-provider/README.md | 15 +- 13 files changed, 442 insertions(+), 1047 deletions(-) delete mode 100644 services/agents-api/CONTAINER.md diff --git a/docs/maintainers.md b/docs/maintainers.md index 3ebada442..ce8c05e64 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -1,164 +1,258 @@ -# Maintainers and advanced deployments +# Build and release OpenAgentCore -This page is for people who build and publish OpenAgentCore, or run Core without the -installer. To install Core and Web, use the -[installation guide](getting-started/install.md) instead. +This guide is for maintainers who build and publish OpenAgentCore. To install Core +and Web, use the [installation guide](getting-started/install.md). The rules the +installer code follows are in [Installer design rules](../deploy/install/README.md); +required checks are in [CONTRIBUTING](../CONTRIBUTING.md#required-checks). ## Build a distribution -Release builders need the repository's full Linux toolchain and Docker. Build from -clean, committed source, with the pinned Codex platform package and a matching MiniMax -companion prepared through the existing Runtime build instructions: +A distribution is the matched set of Linux amd64 release assets built from one +commit: the control archive (the installer, the `oac` command, and the Core, Web, +gateway and PostgreSQL images), the Runtime image and node artifacts as separate +files, and the native installers. + +Build on Linux x86_64 with a glibc compatible with Debian 12, Docker, the Go +version in `go.mod`, Node, pnpm, Python 3.9 or newer, curl and tar. The source +must be clean and committed. First prepare the pinned Codex package and MiniMax +Code companion, then build: ```sh -export AGENTS_RUNTIME_CODEX_PACKAGE=/absolute/path/to/codex-linux-package -export MCODE_HARNESS_BUILD_DIR=/absolute/path/to/mcode-harness-artifact -export CORE_DISTRIBUTION_RELEASE_BASE_URL=https://downloads.example/releases/COMMIT +bash scripts/prepare-release-runtimes.sh +inputs="$HOME/.oac/build/release-inputs/inputs.json" +export AGENTS_RUNTIME_CODEX_PACKAGE="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["codex"])' "$inputs")" +export MCODE_HARNESS_BUILD_DIR="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["mcode"])' "$inputs")" +export CORE_DISTRIBUTION_RELEASE_BASE_URL=https://github.com/MiniMax-AI/parsar-core/releases/download/v1.2.3 make build-core-distribution ``` -The builder reuses the existing Core, Runtime, SDK and Web build scripts. It records -the commit, immutable image identities, microsandbox binary hashes and the actual -Runtime OCI manifest digest. Output goes to `~/.oac/build/core-distribution/`; it is -not published anywhere automatically. Qualify the exact bundle before distributing it. -See the [contributor guide](../CONTRIBUTING.md). - -The release base must serve the generated asset file names over HTTPS. Set -`CORE_DISTRIBUTION_OFFLINE=1` to also produce the offline bundle; a build without a -release base must select offline mode. Nodes obtain bootstrap metadata from the -console that generated their command. The console serves local artifacts or redirects -missing ones to the pinned HTTPS release URL. Published assets download anonymously. - -Native daemon/Harness installers are separate `oac-native--.tar.gz` -Release assets with checksum files. The default Core archive and image carry only -`native-installers/catalog.json`; Session bootstrap downloads the machine's platform -on demand. The explicit offline archive includes these installers once, outside the -Core image. Core installation retains them locally for the same bootstrap endpoint. -The tag workflow assembles the catalog from matching native CI outputs. For a local -build with native onboarding, first run `scripts/build-native-catalog.mjs INPUT OUTPUT` -and set `OAC_NATIVE_INSTALLER_BUILD_DIR=OUTPUT`; missing or foreign native assets -prevent release publication. No Release or registry download occurs when Core starts. - -A bundle carries a fixed set of docs (the build lists them). Links between them stay -relative; every other relative link is rewritten to the same file on GitHub at the -bundle's commit, and the build fails if a link or anchor does not resolve. - -### Independent Core build artifacts - -`make build-agents-api` produces `oac-core`, `oac-core-migrate`, -`oac-core-device` and `oac-core-environment-key` under `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core`. -`OAC_DEV_CORE_BUILD_DIR` may select another absolute output directory. The build -uses only the explicit source set in `scripts/build-agents-api.sh`: the execution -service, its Go contracts and required shared daemon/logging packages, plus the -root Go module manifests. Product server/frontend, other applications and their -migrations/assets are absent from the temporary build context. Keep this boundary -explicit when introducing shared dependencies; do not copy the whole repository -to make an accidental product dependency compile. - -The build uses Go directly with workspace discovery and CGO disabled, read-only -module manifests and trimmed paths. It requires no Node, Docker or product setup. -`make check-agents-api` runs this build before its tests, so the full `make check` -and the dedicated CI workflow enforce the same boundary. CI exercises the built -migration command and uses the built server for official-client HTTP checks. -`make docker-build-agents-api` reuses that build for Linux amd64 and sends only -its executables, the E2B helper and `services/agents-api/Dockerfile` to Docker. The -digest-pinned Debian slim runtime runs without root, product assets or an embedded -harness. Keep runtime credentials outside the image and migrations explicit. -`make check-agents-api-container` runs the existing official-client suite against -the image with a read-only root filesystem; it requires Linux Docker, a non-root -host user, the pinned SDK and a dedicated execution test database. Dedicated CI -runs this after binary validation. Changes to the image/build path require this -check in addition to `make check`; do not make ordinary Go builds require Docker. -Registry publication, additional runtime architectures, daemon packaging and -product cutover remain separate work. - -`make build-agents-api-release` reuses the isolated build for a Linux amd64 archive -under `~/.oac/`, with its four commands, license, operator guide, source/tree and -protocol manifest, and file/archive checksums. It requires clean committed source -and Python 3.9+, stages output privately, and packages fixed artifacts deterministically. -Keep runtime configuration, credentials, product sources and separately installed -daemons/harnesses out of the archive. Archive changes require content/hash and -fresh-extraction operator checks plus `make check`; execution acceptance uses the -packaged operators and public protocol, not private Store provisioning. Preserve -the database and native history when replacing the API package. This target does -not publish a release or provide an installer/supervisor. - -The archive stays Docker-free. Its former Docker-hosted variant, selected by a -retired release Runtime image variable, is gone and the builder refuses that variable: -an image ID alone is not the complete Runtime release a Docker deployment needs. -Docker-hosted deployments use the matched Core distribution below. The archive and -the standalone container are advanced paths for running Core alone; keep them off the -newcomer installation path and document them under -[Maintainers and advanced deployments](maintainers.md). +`prepare-release-runtimes.sh` refuses an existing `~/.oac/build/release-inputs`; +use a fresh build host or directory. + +| Variable | Effect | +| --- | --- | +| `CORE_DISTRIBUTION_RELEASE_BASE_URL` | Versioned HTTPS directory that will serve the generated asset file names (never `latest`). Required unless `CORE_DISTRIBUTION_OFFLINE=1` | +| `CORE_DISTRIBUTION_OFFLINE` | `1` also builds the offline archive | +| `AGENTS_RUNTIME_CODEX_PACKAGE`, `MCODE_HARNESS_BUILD_DIR` | Pinned Runtime inputs from `prepare-release-runtimes.sh` | +| `CORE_DISTRIBUTION_CODEX_IMAGE`, `CORE_DISTRIBUTION_CLAUDE_IMAGE`, `CORE_DISTRIBUTION_MCODE_IMAGE` | Use existing Harness images instead of building them; set all three or none. Each must contain the daemon built from this commit | +| `OAC_NATIVE_INSTALLER_BUILD_DIR` | Native installer catalog directory; see [Native installers](#native-installers) | +| `CORE_DISTRIBUTION_BUILD_DIR` | Output directory under `~/.oac`. Default: `~/.oac/build/core-distribution` | +| `CORE_DISTRIBUTION_BUILD_NETWORK` | Docker build network: `default`, `host` or `none` | +| `CORE_DISTRIBUTION_MICROSANDBOX_ARCHIVE` | Cached microsandbox release archive. Default: `~/.oac/cache/microsandbox-v0.7.2-linux-x86_64.tar.gz`, downloaded when missing | +| `CORE_DISTRIBUTION_DATABASE_IMAGE` | PostgreSQL 16 image; the default is pinned by its linux/amd64 manifest digest | + +The build reuses the Core, Web, Runtime, SDK and helper builders. The manifest +records the commit and source tree, image config and OCI manifest digests, the +Runtime OCI manifest digest, the microsandbox runtime and firmware hashes, and the +size and hash of every downloadable artifact. Output is the control archive and its +`.sha256`, the optional offline archive, and the versioned Runtime, node and native +installer assets. Nothing is published. Rebuilding into a directory that already +holds this commit's distribution is refused. + +The control archive carries no Runtime image or node execution artifacts. Nodes +fetch them from the Web that generated their command, which serves a local copy or +redirects to the release base; the offline archive carries them instead. The +[download contract](../deploy/install/README.md#download-contract) owns these rules. + +A distribution carries the docs listed in `BUNDLED_DOCS` in +`scripts/core-distribution-manifest.py`. Links between bundled docs stay relative; +every other relative link is rewritten to the same file on GitHub at the bundle's +commit. The build fails when a link or anchor does not resolve, and +`make check-distribution` runs the same check on the repository. Update the list +when you add or move a doc that the installer or its output refers to. + +### Native installers + +Self-hosted machines install `oac-daemon` from per-platform native installers: +Linux amd64, macOS arm64 and Windows amd64. Each is built on its own OS by the +`native-check` workflow (`scripts/build-native-installer.mjs`, whose `pins` object +fixes the Node.js and Harness versions) and uploaded as +`oac-native-installer--.tar.gz`. For a local distribution, download the +three artifacts from the native run for the same commit, then assemble the catalog +from that checkout: + +```sh +node scripts/build-native-catalog.mjs INPUT_DIR OUTPUT_DIR +export OAC_NATIVE_INSTALLER_BUILD_DIR=OUTPUT_DIR +``` + +The catalog records the commit, the Runtime protocol version and each archive's +checksum. The control archive and Core image carry only `native-installers/catalog.json`; +the archives become separate `oac-native--.tar.gz` release +assets, and the offline archive holds one copy of each outside the Core image. +Without a catalog, Sessions report the install command as unavailable, and the +release workflow refuses to publish. + +### Runtime images and helpers + +`make build-core-distribution` builds all of these. Build one on its own to test a +Harness image or a helper. Run every command from the repository root; default +outputs go under `${OAC_DEV_HOME:-$HOME/.oac}/build`. + +**Codex Runtime image.** Extract the official npm package +`@openai/codex@0.153.4-linux-x64` under `~/.oac` (for example with +`npm pack --ignore-scripts` and `tar -xzf`), then: + +```sh +export AGENTS_RUNTIME_CODEX_PACKAGE=/absolute/path/to/package +make build-agents-runtime +docker build --platform linux/amd64 -t oac-runtime:codex "${OAC_DEV_HOME:-$HOME/.oac}/build/agents-runtime" +``` + +The script checks the package version, builds `oac-daemon` for Linux amd64 and +prepares a context with only the daemon, the unmodified native executable, its +resources and `services/agents-api/deploy/codex/Dockerfile`. + +**Claude Code Runtime image.** Node 20 or newer and pnpm are required. + +```sh +make build-claude-sdk-runtime +make build-claude-runtime +docker build --platform linux/amd64 -t oac-runtime:claude "${OAC_DEV_HOME:-$HOME/.oac}/build/claude-runtime" +``` + +The first step exports the adapter with the pinned Claude Agent SDK +(`packages/claude-sdk-adapter/package.json`) as a checksummed archive; the second +verifies it and adds the daemon. Keep the exported archive unchanged. + +**MiniMax Code Runtime image.** Build the companion from a checkout of the revision +pinned in `packages/mcode-harness/source.json`, with the `@minimax-ai/code` npm +package of the same version for native dependencies. It builds on Linux x86_64 or +macOS arm64 into a new directory: + +```sh +MCODE_NATIVE_SOURCE=/absolute/minimax-code \ +MCODE_CLI_DIR=/absolute/node_modules/@minimax-ai/code \ +MCODE_HARNESS_BUILD_DIR=/absolute/mcode-harness bash scripts/build-mcode-harness.sh +MCODE_HARNESS_BUILD_DIR=/absolute/mcode-harness bash scripts/build-mcode-runtime.sh +docker build --platform linux/amd64 -t oac-runtime:mcode "${OAC_DEV_HOME:-$HOME/.oac}/build/mcode-runtime" +``` + +`scripts/prepare-release-runtimes.sh` runs the companion build from the pins. + +The distribution combines the three Harness images into one Runtime image +(`deploy/distribution/Runtime.Dockerfile`) that contains the daemon, the shared +helpers and the three native Harness packages. It verifies that each image +carries the daemon built from the same commit. + +**E2B helper.** + +```sh +make build-e2b-provider +``` + +Docker builds the Linux amd64 helper with the pinned CPython and Debian 12 image. +The Python dependency closure, including PyInstaller, is hash-locked in +`services/agents-api/tools/e2b-provider/requirements.lock`; no E2B account key is +needed. Set `E2B_PROVIDER_BUILD_DIR` for another output directory and +`E2B_SOURCE_REVISION` when building from an exported source tree. The output is +`oac-e2b-provider-linux-amd64.tar.gz` with its `.sha256`; it extracts to +`oac-e2b-provider/` with the executable, `_internal/`, `licenses/`, +`requirements.lock` and `manifest.json`. The Core image and native Core use the +same tree; the host needs a compatible glibc and CA certificates, not Python. + +**microsandbox helper.** Linux only, with a C compiler: + +```sh +make build-microsandbox-provider +make check-microsandbox-provider +``` + +The helper is written to +`~/.oac/build/microsandbox-provider/oac-microsandbox-provider`. Its separate Go +module pins the microsandbox Go SDK v0.7.2 and embeds the matching FFI library; +never build production with the SDK's `microsandbox_ffi_path` tag. Core itself +stays a CGO-disabled build. The helper needs glibc and runs only on nodes. + +**microsandbox runtime.** The distribution uses the official +[v0.7.2 release](https://github.com/superradcompany/microsandbox/releases/tag/v0.7.2) +archive `microsandbox-linux-x86_64.tar.gz`, SHA256 +`47c223e3ef5298abf05f47ed9f87981106e400d99bb3f1d042d4d6881346b18b` +(`RUNTIME_ARCHIVE_SHA256` in `scripts/core-distribution-manifest.py`). The build +verifies the checksum before extracting `msb` and `libkrunfw.so.5.6.1` and records +both files' hashes. The helper checks those hashes on every call and never +installs or upgrades them. + +### Standalone Core builds + +`make build-agents-api` builds `oac-core`, `oac-core-migrate`, `oac-core-device`, +`oac-core-environment-key` and `oac-node` into +`${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core` (`OAC_DEV_CORE_BUILD_DIR` selects +another absolute directory). The build copies only the source set listed in +`scripts/build-agents-api.sh` (the Core service, its contracts, the shared +packages it needs and the root Go module files) into a temporary context and +builds with CGO disabled, read-only modules and trimmed paths. It needs no Node, +Docker or other application. When Core gains a shared dependency, add that package +to the list; never copy the whole repository to make it compile. + +`make docker-build-agents-api` builds the image `oac-core:dev` (`OAC_DEV_CORE_IMAGE` +selects another name) from those five commands and the E2B helper. The base is the +digest-pinned `debian:bookworm-slim` with CA certificates and the glibc runtime the +helper needs; the default user is UID/GID 65532 and Core listens on `:8091`. The +image is Linux amd64 only and is not pushed to a registry. Changes to the image or +its build need `make check-agents-api-container` in addition to `make check`: it +runs the official-client suite against the image with a read-only root filesystem +and needs Linux Docker, a non-root user, `OAC_TEST_OFFICIAL_SDK_PYTHON` and a +dedicated `OAC_TEST_DATABASE_URL`. + +`make build-agents-api-release` packages the same five commands into +`oac-core--linux-amd64.tar.gz` and its `.sha256` under +`~/.oac/build/oac-core-release` (`OAC_DEV_RELEASE_DIR`). The archive holds the +[archive README](../services/agents-api/RELEASE.md), the license, `manifest.json` +(commit, tree, platform, Go version, upstream protocol and binary hashes) and +`SHA256SUMS`. The build needs clean committed source and Python 3.9 or newer, and +packages deterministically. It carries no configuration, credentials, Web or +Runtime. Test archive changes by extracting a fresh copy and running its commands. ## Publish a version -Push a version tag on the reviewed commit to run `core-release`: +Push a version tag on the reviewed commit to run the `core-release` workflow: ```sh git tag -a v1.2.3 FULL_REVIEWED_COMMIT_SHA -m "OpenAgentCore v1.2.3" git push origin v1.2.3 ``` -Tags use `vMAJOR.MINOR.PATCH`, optionally with a prerelease suffix such as -`-rc.1` and build metadata such as `+build.1`. A prerelease suffix creates a -GitHub prerelease. Tag creation is the maintainer's release decision. - -The workflow checks that exact source with the shared `make check` workflow -while building the Linux amd64 Core, Web, Runtime and database images, installation -archives and versioned Runtime assets. Tag builds include the offline archive. -It uploads the matched files as an Actions artifact and automatically publishes -them in the same tag's GitHub Release. Downloads in the manifest refer to that -tag. Each Release also includes the standalone `install.sh` bootstrap and its -checksum. It defaults to the latest stable release and accepts `--version`; -the [installation guide](getting-started/install.md#install) owns its usage. -Images are shipped as archives; this workflow does not push an image registry. -Publishing a Release does not change the repository's visibility. - -Checks and builds run concurrently, and publication requires both jobs to succeed. -Both check out the same full commit SHA (the tag event's commit or the explicit -manual input). A failed check never permits publication, even if its build succeeds. -The build may consume runner time before another job fails; this trades some failed-run -cost for shorter successful releases. - -Both jobs use the same Go module and compiler-cache directories under -`~/.oac/cache/`. Cache keys include runner OS/architecture, all Go module manifests -and checksums (including the toolchain version), and the checked-out commit. -A dependency-matched older cache is only a compiler/download seed: Go resolves -inputs again, and all checks still run with their existing assertions and timeouts. -No test result, installation state or release archive is accepted from this cache. -Main-branch checks can populate the default-branch cache for later release runs; -GitHub's branch/tag cache visibility rules still apply. New keys are saved only -after successful jobs; concurrent writers for the same key may retain either -job's valid cache. Missing or evicted entries affect speed, not correctness. - -Build and check jobs have read-only repository permissions. Only the publication -job receives `contents: write`. Before publication it verifies archive checksums -and confirms that the remote lightweight or annotated tag still resolves to the -built commit after uploading the draft assets. Publication sends one request for -that fixed Release ID. An upload failure cannot expose an incomplete public Release; -an ambiguous publication response leaves the Release intact for inspection. -The publisher resolves GitHub's current repository name before any writes, so -renaming a repository does not leave asset uploads using an old Actions context. -Uploads remain on `uploads.github.com` and bound to the created Release ID; -publication does not replay failed uploads or follow arbitrary upload hosts. - -Do not move release tags or overwrite published assets. A rerun refuses an -existing Release, including a partial draft, rather than replacing files. If the -publication job fails, inspect the Release first: it may have completed despite -a lost response. Leave a complete published Release intact. For an incomplete -draft, reconcile or remove only that draft before rerunning the failed publication -job, which reuses the original Actions artifact. Do not rerun the successful build -or recreate the tag to recover a failed upload. - -Automated checks establish build and test results, not real-model qualification. -Keep live execution evidence separate and assess it before pushing the version -tag. No model credentials or private certificate authorities belong in CI inputs. +Tags use `vMAJOR.MINOR.PATCH`, optionally with a prerelease suffix such as `-rc.1` +and build metadata such as `+build.1`. A prerelease suffix creates a GitHub +prerelease. Pushing the tag is the release decision. Automated checks establish +build and test results, not real-model qualification: assess live execution +evidence before you push the tag. Model credentials and private certificate +authorities never enter CI or release inputs, including acceptance images that +contain them. + +The workflow runs three jobs on the tagged commit: `check` (the full `make check` +workflow), `native` (the `native-check` matrix) and `build`, which starts after +`native` succeeds. `build` prepares the pinned Runtime inputs, assembles the native +catalog and builds the distribution with the offline archive, and adds +`deploy/install-release.sh` as `install.sh` with its checksum. The `release` job +runs only after `check` and `build` succeed. It is the only job with +`contents: write`. It verifies the archive checksums and the native installer +checksums against the catalog, refuses an existing Release or draft for the tag, +uploads everything to a new draft, confirms the tag still points at the built +commit, and publishes that draft by its ID. Images ship as archives; no registry is +pushed. Downloads are anonymous. + +`install.sh` resolves the latest stable release once, or the release named by +`--version`, verifies the control archive and runs that bundle's installer; the +[installation guide](getting-started/install.md#install) covers its use. + +The jobs share Go module and build caches under `~/.oac/cache/`, keyed by runner +OS and architecture, the Go module files and the commit. An older cache only seeds +downloads and compilation; every check still runs. New keys are saved only after a +successful job. + +Never move a release tag or overwrite published assets. If the `release` job +fails, inspect the Release first: publication may have completed despite a lost +response. Leave a complete published Release as it is. For an incomplete draft, +fix or delete only that draft, then rerun the failed `release` job, which reuses +the original Actions artifact. Do not rerun the build or recreate the tag to +recover a failed upload. ### Build a candidate without publishing -Manual runs accept a full source commit SHA. They run the same checks and build -steps, default to offline output, and never publish automatically: +A manual run takes a full commit SHA, runs the same checks and builds, defaults to +the offline archive, and never publishes: ```sh revision=$(git rev-parse HEAD) @@ -166,545 +260,74 @@ gh workflow run core-release --repo MiniMax-AI/parsar-core --ref main \ -f ref="$revision" -f offline=true -f draft_release=true ``` -With `draft_release=true`, the result is an unpublished `build-` -Release. With `draft_release=false`, files remain in the Actions artifact only. -Use the exact matched asset set; do not mix builds or resolve components through -`latest`. The historical batch qualification tools are not part of tag publication. +With `draft_release=true` the result is an unpublished `build-` draft +Release; with `draft_release=false` the files stay in the Actions artifact. Use the +exact matched asset set; never mix builds or resolve components through `latest`. -### Continuous integration coverage +`scripts/promote-qualified-release.py` qualifies such a draft on a supervised host +and publishes it; its module docstring states the rules. -CI coverage has three owners: `core-check` runs the complete `make check` gate; -`api-acceptance.yml` adds the pinned official-client, migration-command and container -acceptance without repeating the full service test suite; `native.yml` -builds and tests the daemon, process lifecycle, Harness protocols and installer -bundle together on Linux, macOS and Windows. Native tests use the packaged -Harnesses and share one daemon build per platform. Changes to native sources, -shared dependencies or packaging inputs trigger that matrix; documentation-only -and unrelated Web changes do not. Manual native validation remains available. -Superseded native runs on the same ref are cancelled. Workflow syntax validation -and release qualification remain separate checks. +## Continuous integration -## Run Core without the installer +| Workflow | Runs on | Covers | +| --- | --- | --- | +| `core-check` (`check.yml`) | Pushes to `main`, every pull request, releases | `make check` with a PostgreSQL service and the Playwright browser, then a daemon build | +| `api-acceptance` | Pushes to `main` and pull requests that touch Core, its contracts, clients, shared Go code or build scripts | Standalone commands and migration, the pinned official client over HTTP, and the standalone container | +| `native-check` (`native.yml`) | Pull requests that touch native sources, shared dependencies or packaging inputs; manual runs; releases | Daemon, process lifecycle, Harness protocols and the installer bundle on Linux, macOS and Windows; uploads the native installers | +| `actionlint` | Changes to workflows | Workflow syntax | +| `core-release` | Version tags and manual runs | See [Publish a version](#publish-a-version) | -These paths give you Core alone, without Web, the `oac` command or `config.json`. -They are for development, testing and operators who manage Core's process themselves. -They are not an installation path for new users. +Documentation-only and unrelated Web changes do not start `native-check`. A newer +`core-check`, `api-acceptance` or `native-check` run on the same branch or pull +request cancels the older one. -- [Standalone Core archive](../services/agents-api/RELEASE.md) - (`make build-agents-api-release`): Core, its migrator and operator commands for your - own PostgreSQL. -- [Standalone container](../services/agents-api/CONTAINER.md) - (`make docker-build-agents-api`): the same in a Linux container image. -- [Service guide](../services/agents-api/README.md): building and running Core from - source. +## Run Core without the installer -Core reads only its environment; the +The standalone archive and container give you Core alone: no Web, no `oac` +command and no `config.json`. They suit development, testing and operators who +supervise Core themselves. Core reads only its environment; the [configuration appendix](configuration.md#appendix-core-environment-without-the-installer) -lists the variables. The Docker-hosted variant of the standalone archive is retired: it -could not describe a complete Runtime release by itself. Docker-hosted deployments use -the Core distribution and its installer, whose manifest carries the complete release. - -## Distribution and installer rules - -[Configuration](configuration.md) is the canonical operator parameter reference. -[Installation options](getting-started/install-options.md) owns installer usage; -README Quick start and the installation guide link there instead of copying option -lists. Generate its flag-to-key table and the configuration reference from the schema. -`host` selects the gateway listener for managed ingress and Core/Web listeners for external ingress; `ports.web` uses only `--port`, and -`ports.core` uses `--core-port`. Defaults, validation and flag mappings live in the -schema. Flags seed config.json; health checks, setup, apply and generated service -files derive their addresses from that same config. Apply uses the last applied -address to contact running services before changing listeners. External non-loopback -binds require an HTTPS public origin. Managed ingress initially exposes Web by IP -over HTTP; Core stays loopback and PostgreSQL remains private. -Every process setting has one home: the installation's private `config.json`, -described by `deploy/install/config.schema.json`. The operator edits that -file, or uses the installer-owned managed-domain action through Web/`oac domain`; `oac 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.oac.inputs`, native `OAC_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`. State format 2 uses an `oac-` Compose -project; config.json's independent schema format stays 1. The operator command is -`oac` (`oac_cli.py`, packaged as `oac.pyz`), with default installation directory -`~/.oac/core`, private `~/.oac`, generated `x-oac` annotations and `.oac.lock`. -The project has no historical installation compatibility or in-place version upgrade -contract. Install only into an empty directory, or repair the exact same source -revision. Refuse old formats, conversion journals and different revisions before -installation mutation; retain their data and direct operators to reinstall separately. -Distributions contain only current installation and maintenance code; historical -layout conversion, brand migration and native binary replacement implementations -are not packaged. Keep refusal checks and their tests when retiring these paths. -The installer and every mutating `oac` command share the stable `.oac.lock` inode. -The installer owns this lock across creation, payload/native/launcher repair and -apply, invoking the already-locked apply implementation without nested locking. -Never unlink or replace the lock, including after an interrupted fresh install. -Current-version interrupted apply and rotation retain their existing recovery path. -The packaged `oac.pyz` entrypoint embeds the build source revision and checks it -against the existing `state.json.source_commit`; this adds no installation state -format. Node `--update` is refused; runtime generation operations are unchanged. - Core process settings use `OAC_*`, Web settings use `OAC_WEB_*`, and shared Go -logging uses `OAC_LOG_*`. Retired settings fail startup even when empty or when -the new name is also set; report every matching name without values. No supported installation entrypoint converts pre-rename files. Core and operator executables -are `oac-core`, `oac-core-migrate`, `oac-core-device` and -`oac-core-environment-key`; Web is `oac-web`, and the Core-host E2B helper is -`oac-e2b-provider` under `/opt/oac/e2b` in the image. -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. `install.sh --sandbox` calls -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. - -### Managed HTTPS ownership - -A default combined Docker installation adds two Compose services from one pinned -installer image: `gateway` runs Caddy, and `installation` runs the packaged -`oac domain-server`. The latter uses the installing account's UID and existing -local Docker socket access to invoke the same locked apply implementation. Its -only request surface is the private `ingress/api/api.sock`, with Core-key -authentication and a typed domain action. Core and Web get no Docker socket, -host process authority or writable installation configuration. Web gets only the -private API socket directory. Caddy's separate admin socket is never mounted in Web. - -The managed gateway owns ports 80/443 and the initial Web port. Caddy owns -certificate issuance and renewal; its private data persists in `ingress/data`. -`generated/Caddyfile` is derived from `config.json`, and apply reloads it through -the private Caddy socket even when container inputs already match. A successful -apply reconciles domain operation status after verifying the running services. -Domain preparation retains the old entry point while -verifying a trusted certificate and installation-specific response over HTTPS. -The existing operation record retains the last successfully applied public address. -Apply and start update it after gateway verification and service health checks; -generated files alone do not establish that a new address is active. Failed retries -restore through common apply even when a previous attempt partially changed services. -Only then does it update `public_url` and call the common apply path. Failure -restores the previous desired configuration and reports incomplete recovery. -Interrupted operations retain desired files and a visible failure/retry state; -they never create another service project or delete execution data. - -The domain operation refuses unrelated pending config edits and shares `.oac.lock` -with CLI mutations. Its status file is operation bookkeeping and the public-address -recovery receipt; `config.json` remains the source of desired process settings. -A Web restart ends console sessions; the UI provides the new -HTTPS login address instead of treating a dropped request as proof of success. -Split/native installations use external ingress and explicitly report automatic -Web setup unavailable. Ingress is an installation concern, independent of Runtime -and Sandbox Provider selection. - -The E2B template builder assigns traversable modes only to synthetic public archive -ancestors. Runtime file and directory permissions, private build contexts, key inputs -and output umask remain unchanged, including when invoked under umask 077. - -The installer packages Core and the Web console together, -with independent `--core-only` and `--web-only` modes. `site/` is the public static -landing, separate from `apps/web`; it must not create an onboarding prerequisite, -call a model, or claim complete protocol compatibility. Use Web's page and action names in user docs, and `OPENAI_BASE_URL` and -`OPENAI_API_KEY` for application examples. The [documentation ownership](../CONTRIBUTING.md#documentation-ownership) -map defines the authored sources. - -A distribution carries a fixed list of docs (`BUNDLED_DOCS` in -`scripts/core-distribution-manifest.py`). The build keeps relative links between -bundled docs, rewrites every other relative link to the same file on GitHub at the -bundle's commit (`@SOURCE_REVISION@`), and fails on a link or anchor that does not -resolve; `make check-distribution` runs the same check on the repository's docs. Keep -the list self-consistent when adding or moving a doc the installer or its output -refers to. - -`make build-core-distribution` builds from clean committed source and reuses the -existing API, Runtime, SDK, helper and Web builders. Artifacts record source and -immutable image identities, the actual Runtime manifest digest, checksums and -microsandbox runtime/firmware hashes and executable native payloads. Local distribution builds do not publish. The tag-triggered release workflow -reuses the full repository check on the exact build source, then publishes the -matched assets automatically; manual runs remain artifact-only or draft-only. -Only publication receives repository write permission. Never overwrite release -assets or move an existing version tag. The [maintainer guide](#publish-a-version) -owns tag syntax, prereleases and failed-publication recovery. -Release assets include `deploy/install-release.sh` as standalone `install.sh` -with a checksum. This public downloader resolves latest once (or a selected tag), -verifies the control-plane archive before safe extraction, and delegates to that -bundle's installer. Default installation downloads Core, Web and PostgreSQL payloads, -never the Runtime image or node execution artifacts. Offline archives remain an -explicit distribution option. It introduces no separate installation state, upgrade path or login flow. -Build/test success is distinct from real-model qualification; maintainers assess -that evidence before pushing a release tag, and no synthetic result substitutes -for native execution acceptance. - -Distribution `images` records each exported image's config digest; -`image_manifest_digests` records its OCI manifest/index digest. Derive and verify -both from the same archive, including its referenced config and layer bytes, and -require the build host's selected image ID to match one of them. Docker's classic -store identifies images by config, while its containerd store uses the OCI -descriptor. The builder therefore selects the digest from BuildKit's build metadata -that the local store resolves, never the `--iidfile` config digest alone, and -disables provenance attestations so each image and archive holds one platform -manifest in both stores. For the same reason the default PostgreSQL input is pinned -by its linux/amd64 platform manifest digest: a pulled multi-platform tag keeps its -whole index in the containerd store, and that export holds every platform. Core, -node and self-hosted installers share one resolver -for these required identities: confirm Linux amd64 and the returned immutable local ID, -then use that ID in service/provider configuration and Runtime launches. Tags do -not replace identity verification. The microsandbox-qualified `runtime_ref` -remains independent of Docker's local store identity. - -The manifest is the shared download contract for Core, node and self-hosted -installers: flat versioned filenames, compressed Runtime size/hash and unpacked -size/hash. Nodes obtain bootstrap metadata from their configured console (or a local offline -bundle). Web serves locally available artifacts first; for missing declared execution -artifacts it redirects the node to the versioned HTTPS release base in the verified -distribution manifest. Web does not download or cache those bytes. Only artifact -requests may follow HTTPS redirects, without credentials or cookies; metadata and -enrollment requests must remain on the configured console. Nodes retain size and -SHA-256 verification, resumable transfers and immutable release selection. Download into -private temporary files, verify before atomic promotion, and reuse only verified -cache entries or exact image identities. Core's default image must not acquire -execution-only payloads. Python zipapps bundle the shared resolver with each -remote bootstrap; the console publishes only fixed non-secret files and declared -artifact names. Candidate build automation creates artifacts and may create an -unpublished draft; a successful build is not real execution qualification. A -separate existing-host batch controller may publish that draft automatically only -after directly supervised real qualification and verified batch landing. Repository -visibility is public. Published release downloads are anonymous and must not -require GitHub login or repository credentials. -Manual builds use the legal `build-` release tag; tag-triggered -builds use the actual `v*` tag. The manifest download base and draft tag must match, -while artifact filenames and source provenance retain the full source SHA. -Qualify the exact downloaded production artifacts before publishing the draft; -keep qualified executable, image and source payload bytes and source identity -unchanged. A recorded release-address/checksum-only repack requires proof that -every other archive member is unchanged and verification of final published asset -digests and URLs. Never use an acceptance -image containing a private test CA or model credential as a release input. -Repository visibility is independent of publication. Do not add repository -credentials to installed node/Runtime configuration to bypass download access. - -`scripts/promote-qualified-release.py` requires an explicit full candidate source -SHA, binds it to the archive manifests and matching `build-` tag, and uses -existing local gh authentication and SSH. It uploads/downloads the complete -matching thin/offline/Runtime asset set and verifies archive members and asset -hashes. The separate qualification package is supplied by the maintainer with an -explicit reviewed manifest SHA256. Its complete file inventory, ordered Python -commands, bounded stage timeouts and private path/resource configuration are -verified before any Release mutation and again by the remote supervisor. Candidate -assets cannot select or replace this execution package. Keep host-specific -acceptance scripts, usernames and credential paths outside this public repository; -never put credential values in either manifest. - -The reviewed adapter receives a fresh canonical UUID and exact inventory over the -authenticated command channel. It directly supervises fresh-install, -current-lifecycle, managed-native-smoke, diagnostics-observations-smoke and -node-runtime-smoke in that order. This batch uses one fresh container installation, -one completed managed Session, read-only diagnostics for that Session, and one -current Runtime Session on one new node. It does not rerun the full multi-host, -generation or GC matrix. Every child must exit successfully and -return only its own passed check, the current controller identity and its observed -owned resources. Resources and the previous result flow between live children; -a supplied pass file, skipped check or old report cannot release the candidate. -Verify package and candidate bytes again after each stage. The small shared -`qualification_control.py` is pinned to the reviewed tooling commit and private -package. A live SSH stdin channel carries the request then heartbeats; EOF, timeout, -SIGTERM or SIGHUP stops later work. Each local stage or remote worker has one -foreground process group and a waiting owner outside that group. The owner cleans -the group on success, nonzero exit, timeout and cancellation, including foreground -descendants orphaned by an inner timeout or SIGKILL. Nested foreground commands -inherit the group; only explicitly recorded background resources may detach. -Those retained background resources are outside foreground cleanup. Private nested -workers use the same channel. -Already-issued writes may have unknown outcomes: retain intents/resources and do -not replay or claim rollback. Control tests exercise -short-lived fixture children only and never establish live qualification. - -The caller supplies the independently reviewed promotion-tooling commit. Its -changes from the candidate may only affect the exact promotion files enumerated -in the controller, including CONTRIBUTING, docs/maintainers and the current-batch -node-generation protocol wording correction; the Makefile -exception permits only registration of the controller and control-channel tests. Main must contain the -candidate source and have the reviewed tooling commit's tree. This permits normal -merge commit identity changes and release-only documentation updates without -rebuilding or relabeling the original candidate. The candidate's bundled docs and -source archive retain source 48 (CONTRIBUTING and the node-generation protocol -are present through the source archive, not as direct bundled docs); new release instructions live in the tooling -commit. Product changes or a different main tree block promotion of the old -candidate. Never infer batch membership from all open PRs or automatically merge -them in the publication command. - -Use one controller invocation for the batch. After successful qualification it -waits in the same process, within the explicit merge-wait budget, for the exact -reviewed batch tree to reach main. An ancestor main waits; conflicting main changes -fail immediately. Cancellation or expiration retains evidence and cannot turn a -saved result into resume authority. It verifies unchanged draft identity, -target, tag and downloaded bytes immediately before publication. After the final -download it rechecks main/tree/tag and the same draft ID, then updates that verified -Release ID directly rather than resolving the tag again. It checks the -published bytes afterward. Conflicting assets are never overwritten. An interrupted -run is reconciled before another invocation; stored qualification output is evidence, -not a resumable permission to publish. Preserve its isolated local/remote evidence -and installation resources. No runner, background service, new GitHub secret or -repository-visibility change is required by this finite batch path. - -Executor credentials are issued by the operator with the Core key, through Web or -a Core-key script, under -`/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` -and reuse the existing restricted issuer. The target must be a self_hosted -Environment of that Project whose Session exists; anything else is 404. The -Project's principal is the credential's execution principal, its scope stays -daemon enrollment and connection for that one Environment, and issue, rotate and -revoke each record an administrator audit entry in the write's transaction without -the secret. Project API keys cannot issue them. Native self-hosted installation runs the daemon on Linux, macOS or Windows with -its starting account's permissions. It owns no sandbox node or Core allocation, -adds no isolation and retains user-owned native history after uncertain launches. -Report started, connected and real execution success separately. -Native installation uses `oac-daemon install` and `start` with an explicit -credential-file path and the same `OAC_RUNTIME_HOME` for lifecycle commands. It is -current-version only and does not adopt an older container installation. Rotation -replaces the configured credential file for the same key and restarts the daemon; -never create a replacement Session history to recover a credential. The console's -older container-installer command remains a distinct packaged workflow, not the -native installation interface. Report connection and actual execution separately. -Core-key executor credential lists expose a required connection observation with -never_enrolled, connected or disconnected status, immutable bound key identity, -enrollment time and last authenticated heartbeat time. Read credential metadata -and binding facts in a closed read-only snapshot, then reuse runtimeenrollment's -current authority and actual gateway peer checks. Recheck executor and device -authority after reading the peer; rotation, revocation or Environment retirement -must not inherit a former key's connected state. No gateway means not connected, -never an authentication bypass. Known authority loss is disconnected; storage -errors remain errors. Keep digests/device IDs internal and public /v1 unchanged. -Connection timestamps are history, not execution/native/model readiness. - -Self-hosted installation confirms connection through the private daemon transport -using only its restricted executor credential. The read checks the exact live -Environment/key binding and current authenticated connection; it never enrolls, -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 retains the original Runtime identity and history; timeout is a -diagnostic failure, not permission to replay initialization or replace history. The installation public URL -(`public_url` in the installation's `config.json`, seeded by `--public-url`, and -`OAC_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 -API does not accept a Core address, and no deployment row stores one. Bootstrap -never uses request Host or caller-supplied placement fields. Enrollment names the -Core address the node uses; Core refuses one that is not the public URL (409, -token unconsumed) and records it. After the public URL changes, a node receives no -new sandboxes until re-added. This does -not widen sandbox network policies or change credential admission. - -The distribution build sets umask 022 for non-root-readable payloads; installation -credentials and state retain their explicit private permissions. -For a system node installation, capture the trusted bootstrap bytes before -dropping to the service account. Pass those bytes through the fork; the service -account writes its own retained generation helper. Never make the caller's private -download directory accessible or let root write into service-owned state to -work around bootstrap access. - -Installer progress describes the operation about to run. Do not imply fresh -health checks on a no-change repair. Keep terminal styling optional, honor -`NO_COLOR`, and preserve plain redirected logs. Summaries show credential file -locations, never their values. `install_display.py` owns shared terminal formatting; -`install_output.py` and `node_output.py` own their respective completion guidance. -Ship and checksum the display modules, including them in both the distributed node -bootstrap and retained helper. A node summary reports success only after Core -connection and provider readiness are confirmed. Service-user output stays plain -and passes through the existing terminal-control sanitizer. - -The Core/Web installer uses the launching account, including root, and a writable -installation directory. It never invokes sudo, switches accounts or changes host -Docker permissions. Check actual platform, Docker and directory prerequisites; -root alone is not a reason to refuse installation. Native Core retains its -systemd user-manager and lingering prerequisites for that same account. - -The first installer targets a trusted Linux amd64 Docker host. It installs a -private dedicated PostgreSQL service and separate Core and console services in -Compose by default, with zero execution nodes. The default requires neither KVM -nor systemd user services, imports no Runtime image, mounts neither the Docker -socket nor host devices into Core, and never adds its own host as a node. -`--sandbox docker|microsandbox|e2b|none` (default `microsandbox`; `none` and nothing -else with `--web-only`; `docker` prints its weaker isolation and needs a y/N -confirmation or `--accept-docker-risks` before anything is created) is a one-time -install action: once the services are healthy, the -installer POSTs `/core/v1/sandbox/deployment` as Web's setup would, and never on a -repair. It is not written to `config.json`; PostgreSQL owns the -selection. E2B needs a non-loopback HTTPS `public_url`, `--e2b-api-key-file` and -`--e2b-template`, and is refused before anything is installed. A loopback Docker or -microsandbox selection is saved, but no node can serve it until `public_url` is -guest-reachable HTTPS. `--sandbox-provider` and `--provider` are retired and fail. -The thin distribution supplies native Core binaries. Provider helpers, the node -agent, Runtime image and pinned msb runtime/firmware are separate, same-revision -assets. Web serves local offline artifacts or redirects the node to the verified -manifest's versioned HTTPS release. `/console/config` reports providers with a -complete set of local files or declared release downloads. Core packaging is independent of provider: -`--native-core` runs Core as a systemd user service, with PostgreSQL/Web in Compose -and a private loopback database port. Native Core needs no KVM or node assets. Core receives no -Docker socket or node identity mount in either mode. The ordinary standalone node -service owns its provider processes outside the Core container. Its `KillMode=process` -preserves resident microVM/helper processes across a node-service restart. User KVM -access and the Linux runtime libraries are prerequisites for microsandbox. The -installer runs as root and prepares the host: it creates or adopts the -`oac-node` system user, adds it to the `docker` or `kvm` device group (no other -group), and installs one root-owned system service per installation that runs the -same node program with `User=oac-node`. Sudo mode serves one Core per host, -because its nodes share that account. Docker group membership makes that user, -and so the node, root-equivalent on the host; that is inherent to Docker sandboxes, -not a least-privilege boundary. Microsandbox needs only `kvm`. Files the service -user owns are read, written and deleted only with its credentials, never by root, -in a child that starts its own session with /dev/null as input, so nothing it runs -can reach the administrator's terminal. That child also joins a new session -keyring and dies with its parent, and root shows its output only as plain text -(terminal controls become `?`). The installer turns SIGINT, SIGHUP and SIGTERM -into stopping that child and what it started, which would otherwise outlive a -closed terminal. The generated bootstrap requests only the six standard -HTTP/HTTPS proxy and bypass environment names through sudo, and normalizes -both spellings to the lowercase value when present, even if empty. This keeps -curl, urllib and the Go registration command on the same proxy and bypass rules. The installation child retains just those names -in addition to its fixed service-account environment. Proxy values stay out of -arguments, persisted configuration, service units and diagnostics; do not use -broad sudo environment inheritance. This is installation networking only, not -node-service proxy configuration. Root never runs a file that user can write, -opens a URL it wrote, or follows a link in its home. Sudo mode -never installs Docker, KVM or packages, never changes device permissions, refuses -SELinux-enforcing hosts and a token in the environment, and changes nothing when a -check fails. `--uninstall` removes a node only after Core rejects its credential, -never touches sandboxes, volumes or images (it keeps the Runtime image and the -microsandbox store), deletes the account only when the installer created it and no -node remains, and otherwise removes only the groups it added. Do not add any other launcher, scheduler or -recovery path. Node services restart after failures without a start limit, so a node -outlasts a Core outage, and stop restarting when the node program exits 78 -because Core answered 401 to its credential (a removed or retired node). -The basic API image and binary builds remain independent artifacts. -The standalone API release and Core distribution both include the nodes operator -reference (`HOSTED-SANDBOX-MANAGER.md`) at the relative path used by their packaged README. Include the -guide in each artifact checksum list so extracted documentation matches its build. -The node asset includes the `oac-node` binary. The installer's Docker and -microsandbox selections use Web's Standard size from -`apps/web/src/features/sandbox/standard-sizes.json`, which the distribution build -copies into the bundle; keep no second copy of those values. An existing database -selection is never overwritten by installer defaults. Node configuration and identity live under -`~/.oac/nodes//` in the node account's home (`/var/lib/oac-node` -in sudo mode); microsandbox uses its separate short private -Runtime home. Zero-node installs create no node identity state but retain the paired -Core key for first setup. - -Node installation refuses pre-rename resources for the same installation ID: old -records, node directories, units and Docker networks. It never adopts those -resources or removes another installation. Remove the node on its old Core, then -uninstall with the previous release before adding it again. The machine -configuration route rejects the retired product-named node header with -`400 invalid_request`; only -`X-OAC-Node-ID` identifies a retained node credential. - -User-managed hosts use the native daemon installer on Linux, macOS and Windows. -It does not select a supplier or create compute resources. The retired Docker -self-hosted installer/launcher and console payload are not retained as fallback. -Existing environments, credentials, workspaces and native history are never -automatically deleted or adopted by a new installation. - -One Runtime image contains the existing daemon, shared helpers and three native -harness packages. Their differences remain in the adapters. Core keeps exclusive -ownership of Session allocation, initialization, cancellation, snapshots and -cleanup. The node installer imports the Runtime image and prepares running -conditions; neither installer creates an execution Session or supplies a model -credential. Applications use the -existing write-only model execution extension, with the installation's persistent -credential encryption key. Provider identity/backend namespace and native history -must not change on a repeated install. - -Installation state and secrets live in a private directory under `~/.oac/` by -default. No credential enters build arguments, image layers, browser bundles or -diagnostic output. Compose configuration is confidential. The generated database, -Projects and their issued keys, provider identity and encryption -key survive reruns; automatic -revision replacement and provider migration are outside this initial installer. -Reruns also refuse enabling or disabling a sandbox provider on an existing -installation, including adding one to the default zero-node installation. -Stopping control-plane services does not stop all Provider resources; use Core's -existing release operations for full cleanup. No native restart promise covers -host reboot or a lost running microVM. Do not delete data or issue broad -container/volume pruning as recovery. - -`make check-distribution` covers the production proxy, installation rules and -release metadata. Real bundle validation covers default/provider selection, -component modes, existing Web connection, public native execution and restart -retention. Diagnostics report observed service health, not fabricated model or -complete environment readiness. Runtime observations are Core-owned; do not add -a duplicate monitoring/lifecycle framework to installation or the public landing. - -## Native daemon and Harness installation - -`oac-daemon install` owns interactive selection and CLI-only installation through -one options/validation path. No installation-options file input is supported. -Persisted installation state and explicitly supplied credential/tool-variable -files serve runtime operation, not a second installer configuration language. -The release bundles pinned Node/npm, native Harnesses and required adapter assets; -registration lives in CLI and native activation/readiness in each adapter's optional -`agent.Installation` descriptor. Core never selects native paths or OS-specific -installation steps. See [native installation](self-hosted-native.md). - -Self-hosted onboarding extends authenticated Session creation/detail responses with -`x_agents_core.installation`; Web displays the same Core-produced commands. Lists -and durable event journals never retain installation authorizations. The command -uses a 30-minute, Environment- and build-scoped grant to claim one connect-only -credential. The installer persists its generated secret before claiming it; retries -must prove that same secret. Reserve the Environment UUID as the onboarding key ID. -Existing, rotated or revoked credentials are never replaced by onboarding. Machine -bootstrap routes use this grant, not an Environment ID as authentication. Public -artifact routes contain no credentials. Native bundles must match the Core source -revision and Runtime wire version. Core release qualification consumes the same -three-platform native CI artifacts and publishes them as independent, source-qualified -Release assets. Core images and default control-service archives carry only their -small catalog (build, protocol, platform, checksum and versioned HTTPS URL), never -native execution archives. The public artifact route redirects a requested platform -to its catalog URL without proxying or caching it; clients verify the Core-provided -checksum before extraction. Installation grants are sent only to Core, never to -artifact hosts. Explicit offline distributions include one copy of each native -archive outside the Core image. The Core installer retains this directory privately -and mounts it read-only for container Core, or points native Core at the same files. -Core verifies local archives before serving; missing online archives redirect, while -corrupt local content fails closed. Neither installation nor repair downloads native -execution payloads; Session bootstrap requests only the current machine's platform. -`make check-distribution` exercises catalog assembly with manifests larger than -Node's default subprocess output buffer; catalog reads allow up to 64 MiB. -Bootstrap scripts own platform download/extraction only; installation, startup, -connection verification and Runtime execution remain common. Serialize background -PID inspection and publication so concurrent starts cannot create duplicate daemons. -An installed daemon discovers and registers only the adapter kinds named by its -verified installation manifest. The host PATH stays available to tools; its other -Harness executables and activation variables cannot extend that installation. -Direct `connect` rejects an installation manifest and directs the operator to -`start`; unmanaged image bootstrap remains available without that manifest. - -All mutations use the installation directory lock. Publish complete checksum-verified -components from staging, then commit configuration after native readiness passes. -Re-running with the same connection settings adds selected Harnesses and validates -existing contents. Never overwrite, upgrade, auto-repair or migrate installed -components. Missing, modified, wrong-platform or incompatible content is an explicit -error. A partial addition must preserve the old configuration and allow reuse of -complete components; it must not remove previous files or data. - -Installers run as the current user in writable directories. Native subprocess -diagnostics must not expose sensitive parameters or environment values. Adapter -readiness checks receive the installer cancellation context and must reap owned -processes before returning after interruption. Readiness, -authenticated connection and model configuration are separate reported facts. -Starting execution must not download or install Harnesses. Ordinary stop/reconnect -must preserve capability snapshots and native Session state. - -`scripts/build-native-installer.mjs` packages native inputs, validates pins/startup -and hashes every component file. It uses contained regular files and rejects -escaping links. Claude's dedicated frozen `pnpm deploy` export is reified with -the hoisted linker for this distribution before contained links are flattened; -the original standalone Claude archive contract remains unchanged. The native -installer workflow builds and tests on Linux, macOS and Windows, including actual -installation, addition/reuse, missing arguments and unsupported Windows MiniMax. -Native CI startup checks do not replace real model execution evidence or claim -manual Windows acceptance. Heavy builds belong on remote servers or CI. +lists the variables. `OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are +required; set `OAC_PUBLIC_URL` to the origin machines use to reach Core, or Core +runs without the daemon transport. + +- The [archive README](../services/agents-api/RELEASE.md) covers the standalone + archive. +- The [service guide](../services/agents-api/README.md) covers building and running + Core from source. + +To run the container, create a private directory (mode 0700) with `api.env` +(`OAC_DATABASE_URL` for a dedicated database, reachable from the container, +`OAC_CORE_KEY_DIGESTS_FILE=/run/core-key-digests.json`, and `OAC_PUBLIC_URL`) and +`core-key-digests.json`, a JSON array with the lowercase hex SHA-256 digest of your +Core key. Keep both files mode 0600 and the Core key itself elsewhere. Run the +migrations, then start Core: + +```sh +config_dir="$HOME/.oac/oac-core-deployment" +docker run --rm --read-only --cap-drop=ALL --security-opt=no-new-privileges \ + --env-file "$config_dir/api.env" \ + oac-core:dev /usr/local/bin/oac-core-migrate +docker run --name oac-core --detach --read-only \ + --cap-drop=ALL --security-opt=no-new-privileges \ + --user "$(id -u):$(id -g)" \ + --publish 127.0.0.1:8091:8091 \ + --env-file "$config_dir/api.env" \ + --mount "type=bind,source=$config_dir/core-key-digests.json,target=/run/core-key-digests.json,readonly" \ + oac-core:dev +curl --fail http://127.0.0.1:8091/healthz +``` + +`--user` lets the container read the key digest file as your non-root host user; +alternatively grant UID 65532 read access and omit it. Put a TLS reverse proxy in +front for remote clients. `/healthz` reports liveness only. All state is in +PostgreSQL, so the container needs no writable volume; stop and start it with +`docker stop` and `docker start`, and never remove the database to replace it. One +Core process serves each database; replicas add no availability. After startup, +use the Core key with the [administrator API](../contracts/agents-api/admin-api.md) +to create Projects and issue application keys. + +The image also contains `oac-core-device` for an +[internal execution device](../services/agents-api/README.md#internal-execution-device-connection) +and `oac-core-environment-key`, the +[break-glass credential command](../contracts/agents-api/environment-executor-credentials.md#break-glass-command). diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index af7cc2123..9b7208f2a 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -569,21 +569,11 @@ "regex": "and Parsar itself", "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." }, - { - "path": "services/agents-api/CONTAINER.md", - "regex": "Parsar user identities", - "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." - }, { "path": "services/agents-api/README.md", "regex": "Parsar's product|Parsar\\nworkspace|Parsar Skill/SP", "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." }, - { - "path": "services/agents-api/RELEASE.md", - "regex": "Parsar's product execution path", - "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." - }, { "path": "services/agents-api/credentials.md", "regex": "Parsar approval", @@ -664,11 +654,6 @@ "regex": "parsar-node(?:-[A-Za-z0-9<>_.-]*)?|\\.parsar/nodes", "reason": "The legacy-node section gives scoped uninstall instructions; new installation and enrollment examples use oac-node." }, - { - "path": "docs/getting-started/self-hosted.md", - "regex": "parsar-selfhost-\\*|\\.parsar/self-hosted", - "reason": "The legacy self-hosted section explains retained old resources and explicit refusal, not conversion or new defaults." - }, { "path": "docs/getting-started/operations.md", "regex": "(?:AGENTS_API_|CORE_CONSOLE_|PARSAR_)[A-Z0-9_]*\\*?|parsar(?:[A-Za-z0-9_.*/-]*)|agents-api(?:-[a-z0-9*-]+)?|core-console|agents-runtime-", @@ -739,11 +724,6 @@ "regex": "parsar-node(?:-[A-Za-z0-9<>_.-]*)?|\\.parsar/nodes", "reason": "Generated copy of docs/getting-started/nodes.md: The legacy-node section gives scoped uninstall instructions; new installation and enrollment examples use oac-node." }, - { - "path": "apps/docs/content/docs/self-hosted-execution.mdx", - "regex": "parsar-selfhost-\\*|\\.parsar/self-hosted", - "reason": "Generated copy of docs/getting-started/self-hosted.md: The legacy self-hosted section explains retained old resources and explicit refusal, not conversion or new defaults." - }, { "path": "apps/docs/content/docs/troubleshooting.mdx", "regex": "(?:AGENTS_API_|CORE_CONSOLE_|PARSAR_)[A-Z0-9_]*\\*?|parsar(?:[A-Za-z0-9_.*/-]*)|agents-api(?:-[a-z0-9*-]+)?|core-console|agents-runtime-", diff --git a/scripts/promote-qualified-release.py b/scripts/promote-qualified-release.py index 33a704a17..1875a5464 100644 --- a/scripts/promote-qualified-release.py +++ b/scripts/promote-qualified-release.py @@ -1,5 +1,67 @@ #!/usr/bin/env python3 -"""Promote one landed batch through existing gh and SSH authentication.""" +"""Qualify one draft candidate on a supervised host, then publish it after its batch lands. + +One invocation owns one explicit candidate and one qualification run, using the +existing gh authentication and SSH. Build the candidate with a manual core-release +run (draft_release=true); this command never builds. + +Inputs +- --source: the full candidate commit, never inferred from latest. It binds the + archive manifests and the build- release tag. +- --assets: only the generated flat candidate files: the thin and offline + archives with their checksums, the Runtime assets and the native installers with + their checksums. Every archive member and asset hash is verified, and the thin + and offline manifests and native catalogs must match. +- --qualification-package, --qualification-manifest-sha256: a separately reviewed + private package and its manifest digest. The manifest fixes the complete file + inventory, ordered Python commands, bounded stage timeouts and private path and + resource configuration. It is verified before any Release change and again by + the remote supervisor; candidate assets cannot select or replace it. Keep + host-specific scripts, user names and credential paths out of this repository, + and never put credential values in either manifest. +- --promotion-commit: the independently reviewed tooling commit. The local + promotion scripts must equal their bytes there. It may differ from the candidate + only in PROMOTION_FILES, and the Makefile only by registering the promotion and + control-channel tests. Product changes or a different main tree block promotion. +- --host, --remote-root: an existing SSH host alias and an isolated remote parent. +- --state: a new private local evidence directory; a previous one is never reused. +- --merge-wait-seconds: how long to wait for the batch to land, at most seven days. + +Flow +1. Check the qualification adapter, the tooling bytes, the candidate files and + the source tree. Create the build- draft when none exists; refuse a + published Release, a draft for another commit or a conflicting tag. Download + every asset and compare its bytes. +2. Copy the assets and the package to a fresh /. The reviewed + adapter supervises fresh-install, current-lifecycle, managed-native-smoke, + diagnostics-observations-smoke and node-runtime-smoke in that order: one fresh + container installation, one completed managed Session, read-only diagnostics + for it, and one current Runtime Session on one new node. The full multi-host, + generation and GC matrix is not rerun. Every check must pass in this run and + return this run's identity and its own owned resources; a supplied pass file, + skipped check or old report never releases the candidate. Candidate bytes are + verified again after the stages. +3. In the same process, wait until main's tree equals the promotion commit's tree. + A main that is behind waits; a conflicting main fails at once. Expiry or + cancellation keeps the evidence and grants no later permission to publish. +4. Recheck main, the tree, the tag and the draft ID, download every asset again, + then publish that Release ID (never a fresh tag lookup) as the latest release, + and download once more to check the published bytes. + +The SSH stdin channel carries the request and then heartbeats; EOF, timeout, +SIGTERM or SIGHUP stops later work. Each stage runs in its own foreground process +group with an owner outside it that cleans the group on success, failure, timeout +and cancellation, including descendants orphaned by an inner timeout or SIGKILL. +Only explicitly recorded background resources may detach. A write already issued +may have an unknown outcome: keep its intent and resources, never replay it or +claim a rollback. Control tests use short-lived fixture children and never count +as live qualification. + +Never overwrite conflicting assets. Reconcile an interrupted run before invoking +again; stored results are evidence, not permission to publish. Never infer batch +membership from open pull requests or merge them from this command. No runner, +background service, GitHub secret or repository visibility change is needed. +""" import argparse import base64 diff --git a/services/agents-api/CONTAINER.md b/services/agents-api/CONTAINER.md deleted file mode 100644 index 3790586f5..000000000 --- a/services/agents-api/CONTAINER.md +++ /dev/null @@ -1,111 +0,0 @@ -# Standalone container (advanced) - -This is not the installation path for new users. To install Core with Web, nodes and -the `oac` command, use the [installation guide](../../docs/getting-started/install.md). - -This image packages the execution API, its embedded migrator and device operator -command. It needs a dedicated PostgreSQL database/account and an external daemon -for native execution. It contains no Parsar product service, frontend, product -migrations or harness. Supported protocol slices and execution limits remain as -listed in the [service guide](README.md) and [coverage](../../contracts/agents-api/README.md). -Container packaging does not imply complete protocol compatibility. - -## Build - -```bash -make docker-build-agents-api -# Optional local image name: -OAC_DEV_CORE_IMAGE=oac-core:local make docker-build-agents-api -``` - -The target needs Go, Docker and access to pinned Go modules and the base image. -It reuses the isolated binary build and sends only those executables, the E2B helper -and the image recipe to Docker. Linux amd64 is the current runtime target; other architectures -and registry publication are not included. The runtime base is the digest-pinned -`debian:bookworm-slim` image, with CA certificates and the glibc/libgcc runtime that -the bundled E2B helper needs. It keeps Debian's shell and package manager. The default -user is UID/GID 65532. No model credentials or tenant keys belong in the image. - -## Configure and run - -Use a new private directory for deployment configuration. Create `api.env` with -`OAC_DATABASE_URL` pointing to the dedicated execution database and -`OAC_CORE_KEY_DIGESTS_FILE=/run/core-key-digests.json`. Create -`core-key-digests.json` as a JSON array of Core key SHA-256 digests. -Keep the Core key separately and use the [administrator API](../../contracts/agents-api/admin-api.md) -to create Projects and issue application keys after startup. Keep both -files private, for example mode 0600 inside a mode 0700 directory. The database -hostname must be reachable from the container; container localhost is not the host. - -Run migrations explicitly before starting the service. They belong to this API -alone and must never target the product database: - -```bash -config_dir="$HOME/.oac/oac-core-deployment" -docker run --rm --read-only --cap-drop=ALL --security-opt=no-new-privileges \ - --env-file "$config_dir/api.env" \ - oac-core:dev /usr/local/bin/oac-core-migrate -``` - -The following Linux example uses the non-root host UID to read its private key -file. Alternatively, grant the image's default UID read access and omit `--user`. -Do not run the example from a root shell. - -```bash -docker run --name oac-core --detach --read-only \ - --cap-drop=ALL --security-opt=no-new-privileges \ - --user "$(id -u):$(id -g)" \ - --publish 127.0.0.1:8091:8091 \ - --env-file "$config_dir/api.env" \ - --mount "type=bind,source=$config_dir/core-key-digests.json,target=/run/core-key-digests.json,readonly" \ - oac-core:dev -curl --fail http://127.0.0.1:8091/healthz -docker logs oac-core -``` - -The container listens on `:8091`; use a TLS reverse proxy for remote clients. -`/healthz` is liveness only. Database startup validation does not make it a -continuous readiness probe. Persistent API state is in PostgreSQL, so the image -needs no writable application volume. Send SIGTERM with `docker stop oac-core` -and start it again with `docker start oac-core`; never remove database storage -as part of replacing the API container. An execution worker currently permits one -active service per execution database; container replicas do not add HA/recovery. - -## Connect execution - -Set `OAC_PUBLIC_URL` in `api.env` to the API's externally reachable origin, -such as `https://core.example`, then start the container. Core derives the daemon -WebSocket URL from it. Provision a device using this image with -`/usr/local/bin/oac-core-device` as the command and the arguments documented in -[Internal execution device connection](README.md#internal-execution-device-connection). -Pass the same private environment file. The operator command emits a secret profile; -redirect it into a new private file and transfer it securely to the executor. - -Install the daemon and native harness separately. Native history stays on that -executor; an API container restart must not be treated as a new native Session. -Model credentials belong in the executor's private configuration. API keys, device -credentials and Parsar user identities are separate. The daemon URL is not the -upstream `self_hosted.remote_url` protocol. Daemon distribution and product cutover -remain separate work. - -## Verify - -On Linux, with a non-root host user, an `oac_*_tests` database with API migrations applied -and the fixed official Python SDK installed: - -```bash -OAC_TEST_DATABASE_URL='postgres://.../oac_local_tests' \ - OAC_TEST_OFFICIAL_SDK_PYTHON=python3 make check-agents-api-container -``` - -This reuses the existing SDK/raw-HTTP/Go-client suite against read-only containers, -including authentication, tenant isolation and restart persistence. Its host -network is a test convenience. Real daemon/model acceptance is additional evidence; -synthetic or HTTP-only checks do not prove native execution or full compatibility. - -The image also includes `/usr/local/bin/oac-core-environment-key` for operator -issuance, rotation and revocation of exact-Environment executor credentials. Run it -with only the execution database configuration and the arguments in the -[native transport guide](README.md#user-managed-runtime-enrollment). Redirect -its secret stdout to a mode-0600 file under `~/.oac/`; do not bake credentials -into the image or pass the broader caller key to an executor. diff --git a/services/agents-api/README.md b/services/agents-api/README.md index 3e238afbc..255db7c30 100644 --- a/services/agents-api/README.md +++ b/services/agents-api/README.md @@ -74,11 +74,11 @@ The default output is `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core`: Use these executables in place of the corresponding `go run` commands below. The build needs Go and access to its pinned module dependencies; it does not need Node, Docker, the product service or frontend. An isolated source context enforces -that boundary on every build. [Contributor rules](../../docs/maintainers.md#independent-core-build-artifacts) +that boundary on every build. [Contributor rules](../../docs/maintainers.md#standalone-core-builds) define the allowed shared packages and required checks. Runtime database/key configuration and a separately installed execution daemon are still required; these binaries do not establish full protocol coverage. For a standalone Linux -container, see [Container deployment](CONTAINER.md). +container, see [Run Core without the installer](../../docs/maintainers.md#run-core-without-the-installer). `make build-agents-api-release` packages these commands and `oac-node` in a versioned Linux amd64 archive, with source/protocol identity, checksums, a license and @@ -502,7 +502,7 @@ management ID and full principal; neither changes the key's restriction. Unknown historical creators cannot enroll. API bearer keys and executor keys are separate. On the executor host, save that JSON as a private -`$OAC_RUNTIME_HOME/daemon/executor-key.json`, outside the tool workspace. Use the [native installer](../../docs/self-hosted-native.md) to prepare the selected +`$OAC_RUNTIME_HOME/daemon/executor-key.json`, outside the tool workspace. Use the [native installer](../../docs/getting-started/self-hosted.md) to prepare the selected Harnesses. Managed images preinstall them. A preconfigured Runtime can connect with the values returned by Session creation: diff --git a/services/agents-api/RELEASE.md b/services/agents-api/RELEASE.md index e618023f1..948d4952e 100644 --- a/services/agents-api/RELEASE.md +++ b/services/agents-api/RELEASE.md @@ -1,22 +1,17 @@ -# Standalone Core archive (advanced) +# Standalone Core archive -This is not the installation path for new users. To install Core with Web, nodes and -the `oac` command, use the -[Core distribution and its installer](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/install.md). - -This Linux amd64 package contains the independent API, embedded migrator, operator -commands and `oac-node`. It needs your own PostgreSQL and separately -installed execution software, and it has no Web console. -It does not need a source checkout, Go, Node, the Parsar product or its database. -The [coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/README.md) -describes supported workflows and remaining protocol gaps. Packaging does not -establish complete OpenAI Agents API compatibility. +This Linux amd64 archive holds Core alone: the API server `oac-core`, its migrator +`oac-core-migrate`, and the operator commands `oac-core-device`, +`oac-core-environment-key` and `oac-node`. It has no Web console, installer or +`oac` command, and it needs your own PostgreSQL. To install Core with Web and nodes, +use the +[installation guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/install.md). ## Verify and extract -Verify the archive checksum supplied alongside the package, then extract into a -new directory under `~/.oac/`. Keep deployment configuration outside the extracted -package so replacing binaries does not replace credentials or state. +Verify the archive checksum supplied with the package, then extract it into a new +directory under `~/.oac/`. Keep configuration outside the extracted package so +that replacing the binaries does not replace credentials or state. ```sh sha256sum -c @ARCHIVE_NAME@.tar.gz.sha256 @@ -27,147 +22,53 @@ sha256sum -c SHA256SUMS core_bin_dir="$PWD/bin" ``` -`manifest.json` records the source revision/tree, target platform, fixed upstream -protocol and binary hashes. The package also includes its license. Checksums -detect changed bytes; obtain the archive and checksum from a trusted distributor. +`manifest.json` records the source commit and tree, the platform, the Go version, +the pinned upstream protocol and the binary hashes. Checksums detect changed +bytes; obtain the archive and its checksum from a trusted source. -## Start a new API installation +## Configure and start -Provision a dedicated PostgreSQL database and account. Use neither the product -database nor its migrations. The following local example assumes an unused port -8091. For remote clients, place the API behind TLS and set the advertised daemon URL to -the reachable WSS service address. +Create a dedicated PostgreSQL database and account. Generate a random Core key, +keep it in private storage, and write its lowercase hex SHA-256 digest as a JSON +array to `core-key-digests.json`: ```sh umask 077 core_config_dir="$HOME/.oac/oac-core-deployment" mkdir -p "$core_config_dir" -export OAC_DATABASE_URL='postgres://:@/' +export OAC_DATABASE_URL='postgres://:@/' export OAC_CORE_KEY_DIGESTS_FILE="$core_config_dir/core-key-digests.json" export OAC_ADDR=127.0.0.1:8091 -export OAC_DEFAULT_HARNESS=codex -``` - -Create `core-key-digests.json` as a JSON array containing the SHA-256 digest of a -random Core key. Keep the Core key separately in private operator storage. After startup, use it to create a Project and issue an -application key through the [administrator API](../../contracts/agents-api/admin-api.md). -Projects and application keys live only in PostgreSQL. Keys in one Project share -its scope and principal; rotation uses issuance and revocation without a restart. - -Set the reachable public origin; Core derives the daemon endpoint from it: - -```sh export OAC_PUBLIC_URL=http://127.0.0.1:8091 ``` -Keep the configuration and key files mode 0600. Run migrations explicitly, then -start the API in the foreground or through your existing service supervisor: +`OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required. `OAC_PUBLIC_URL` +is the origin that applications and machines use to reach Core: an HTTPS origin, +or plain HTTP on loopback only. The +[configuration reference](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/configuration.md#appendix-core-environment-without-the-installer) +lists every variable. Run the migrations, then start Core in the foreground or +under your own supervisor: ```sh "$core_bin_dir/oac-core-migrate" "$core_bin_dir/oac-core" ``` -`GET /healthz` provides liveness. Project creation establishes its immutable execution scope. One API execution worker owns -each database; starting replicas does not provide execution HA. Native history -belongs to the harness host and must survive API replacement. - -## Use the public client - -Install the official Python client at the commit in `manifest.json` (SDK 3.13.0). -In a client process, set `OPENAI_BASE_URL=http://127.0.0.1:8091/v1` and supply the -private caller key as `OPENAI_API_KEY`. Create an empty self-hosted Session: - -```python -from openai import OpenAI - -client = OpenAI() -session = client.beta.agents.sessions.create( - agent={"model": ""}, - environment={"type": "self_hosted", "workspace_directory": "/workspace"}, -) -print(session.id, session.environment.id, session.environment.remote_url) -``` - -Keep the API running. In a separate operator shell on the API host, issue an -executor credential restricted to this Environment with the Core key, read from -the private file named by `CORE_KEY_FILE`. `PROJECT_ID` is the Project whose key -created the Session; `KEY_ID` is a new canonical -lowercase UUID that you retain for listing, rotation and revocation. The response -is the credential file and is returned only once, so save it to a new private -file: - -```sh -umask 077 -KEY_ID=$(python3 -c 'import uuid; print(uuid.uuid4())') -curl -fsS -X POST \ - -H @<(printf 'Authorization: Bearer %s\n' "$(cat "$CORE_KEY_FILE")") \ - -H 'Content-Type: application/json' -d "{\"key_id\":\"$KEY_ID\"}" \ - "http://127.0.0.1:8091/core/v1/projects/$PROJECT_ID/environments/$ENVIRONMENT_ID/executor-credentials" \ - > "$core_config_dir/executor-key.json" -``` - -If the response is uncertain, do not retry automatically: list the credentials -with `GET` on the same path, then rotate the same `KEY_ID` with `"rotate":true` or -issue it again. Revoke with `DELETE …/executor-credentials/$KEY_ID`. See -[executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) -for the rules, including the 404 and 409 cases. - -Break-glass only: `oac-core-environment-key` issues, rotates or revokes the -same credential directly in the database when the Core API is unavailable. It -needs the private database configuration and the Project's execution principal -(tenant UUID from the `projects` table, organization `core`, project -`proj_`, subject `service_account/project:`). It -bypasses the Core API: it skips the archived-Project check and writes no -administrator audit entry, so use the Core-key route whenever Core is running. - -Deploy the qualified V1 Runtime containing our daemon, selected native harness, -local tools and workspace. Transfer the host file `$core_config_dir/executor-key.json` -into the Runtime as `$OAC_RUNTIME_HOME/executor-key.json`; the host path is not available -inside the Runtime. Keep only this scoped key in the protected daemon -state directory as an owned mode-0600 file. Keep API caller and database credentials -outside Runtime. Configure the model through the existing private adapter options; -native tools must not inherit model credentials or read native history. - -Inside that Runtime, use the exact values returned by Session creation: - -```sh -oac-daemon connect --remote "$REMOTE_URL" \ - --environment-id "$ENVIRONMENT_ID" \ - --credential-file "$OAC_RUNTIME_HOME/daemon/executor-key.json" -``` - -The daemon fills the executor role. No separate Codex executor or service-side -harness is required. This is our private daemon transport, not stock exec-server -wire interoperability. Use WSS outside loopback. Runtime packaging must provide -`/environment/workspace`, its `/workspace` alias, helpers and native isolation; -a directory or key binding alone does not isolate same-user processes. User-owned -E2B deployment uses the [official-SDK startup example](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/services/agents-api/deploy/e2b/README.md). -Core does not allocate or reclaim that compute. - -In the same Python client, stream a Turn after connecting the executor: - -```python -with client.beta.agents.sessions.stream( - session.id, input="Run a command in the workspace and explain its result." -) as stream: - for event in stream: - print(event.type) -``` - -Keep the Session ID for later Turns. After an API restart, reconnect the client -and recover through Session, Turn and Items queries; SSE does not replay history. -Reuse the database, caller identities, daemon profile and native history. Do not -resubmit uncertain execution as new work. Graceful shutdown or connection closure -does not by itself prove all native descendants have exited. - -For key rotation, executor-key revocation and supported execution profiles, use the [versioned service guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/services/agents-api/README.md). -This package does not install PostgreSQL, daemons, harnesses, TLS or a supervisor, -and it does not switch Parsar's product execution path. - -## Sandbox nodes - -The release includes `oac-node` for local and remote hosts. See the -[nodes and sandbox backends reference](HOSTED-SANDBOX-MANAGER.md) for provider -selection, manual registration, administrator credentials, fixed Session placement -and maintenance. +`GET /healthz` reports liveness. One Core process serves each database; replicas +add no availability. Keep the database when you replace the binaries. Put a TLS +reverse proxy in front for remote clients. + +## Next steps + +- Use the Core key with the + [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/admin-api.md) + to create a Project and issue its API key, then follow the + [quickstart](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/quickstart.md). +- To run Sessions on your own machines, follow the + [self-hosted guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/self-hosted.md). + The one-command installation needs this release's native installer catalog in + `OAC_NATIVE_INSTALLER_DIR`. The + [executor credential contract](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) + covers issuing credentials with the Core key and `oac-core-environment-key`. +- To add sandbox nodes with `oac-node`, see the + [node guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/nodes.md). diff --git a/services/agents-api/deploy/claude/README.md b/services/agents-api/deploy/claude/README.md index 41273787f..09d5bdc4f 100644 --- a/services/agents-api/deploy/claude/README.md +++ b/services/agents-api/deploy/claude/README.md @@ -6,16 +6,8 @@ The SDK owns the model/tool loop. Public clients use the same Agents API contrac ## Build and configure -On Linux amd64, build the existing shared workspace helpers, then run: - -```sh -bash scripts/build-claude-sdk-runtime.sh -bash scripts/build-claude-runtime.sh -docker build --platform linux/amd64 -t agents-runtime:claude \ - "${OAC_DEV_HOME:-$HOME/.oac}/build/claude-runtime" -``` - -The bundle pins SDK `0.3.269` and native Claude Code `2.1.269`. The Dockerfile pins +The [maintainer guide](../../../../docs/maintainers.md#runtime-images-and-helpers) +builds the image. The bundle pins SDK `0.3.269` and native Claude Code `2.1.269`. The Dockerfile pins Node's Linux amd64 manifest. Keep the exported SDK bundle immutable. Configure Core's database-owned managed deployment with the resulting immutable Runtime image. Existing Docker outer security settings are unchanged; the daemon and @@ -28,7 +20,7 @@ has no automatic apt installation, sudo or elevated daemon permissions; npm/Python package and setup commands run directly with the existing user's permissions. The packaged image no longer needs bubblewrap or socat for an inner sandbox. For native self-hosting and platform limits, see the -[native guide](../../../../docs/self-hosted-native.md). +[self-hosted guide](../../../../docs/getting-started/self-hosted.md#platforms). Set `OAC_DEFAULT_HARNESS=claude_sdk`. Configure the deployment default model provider with the Core key, in Web or through Core's API: @@ -57,9 +49,7 @@ protocol compatibility. ## Runtime and adapter rules -Build the pinned SDK bundle with `scripts/build-claude-sdk-runtime.sh`, then use -`scripts/build-claude-runtime.sh`. Native self-hosted installations use the same -bundle and daemon protocol. See [Build and configure](#build-and-configure). +Native self-hosted installations use the same bundle and daemon protocol. The shared local binding selects the actual workspace. SDK history, home and scratch remain under `OAC_RUNTIME_HOME/runtime/claude-sdk` for session ownership, without restricting tools. Authentication remains under `OAC_RUNTIME_HOME/daemon`. diff --git a/services/agents-api/deploy/codex/README.md b/services/agents-api/deploy/codex/README.md index 5cdb35fa2..729ca5d5a 100644 --- a/services/agents-api/deploy/codex/README.md +++ b/services/agents-api/deploy/codex/README.md @@ -3,7 +3,7 @@ A managed Session runs the daemon, Codex and workspace in a dedicated outer Environment. Core stays outside it. Use Codex 0.153.4 and its matching `codex-resources`. The same daemon supports native self-hosted installations; see -[the native guide](../../../../docs/self-hosted-native.md) for platform status. +[the self-hosted guide](../../../../docs/getting-started/self-hosted.md#platforms) for platform status. Codex tools run with the daemon user's existing permissions. There is no inner filesystem, permission or network sandbox, no immutable Codex requirements file, @@ -41,13 +41,10 @@ is not acceptance. ## Managed Runtime image and Docker adapter -Extract the official npm package `@openai/codex@0.153.4-linux-x64` beneath -`~/.oac/`. Set `AGENTS_RUNTIME_CODEX_PACKAGE` to its extracted `package` directory -and run `scripts/build-agents-runtime.sh`. It builds the existing daemon and -prepares a binary-only Docker context at `~/.oac/build/agents-runtime`; build -that context with the printed Docker command. This initial image is Linux amd64. -The package includes the unmodified native executable and matching resources. -It does not contain the product server, product CLI, credentials or workspace data. +The [maintainer guide](../../../../docs/maintainers.md#runtime-images-and-helpers) +builds the Linux amd64 image from the official `@openai/codex@0.153.4-linux-x64` +package. It contains the unmodified native executable and matching resources, and +no product server, product CLI, credentials or workspace data. The service's `internal/sandbox` interface has five operations. Its Docker adapter uses the official Moby Go client and an operator-selected immutable image digest, diff --git a/services/agents-api/deploy/e2b/README.md b/services/agents-api/deploy/e2b/README.md index 6acca018c..78d97b834 100644 --- a/services/agents-api/deploy/e2b/README.md +++ b/services/agents-api/deploy/e2b/README.md @@ -64,7 +64,10 @@ The builder preserves the existing image's binaries, native configuration and private workspace layout. Its `template` output is an immutable `templateID:build_UUID`; use that exact value. The build must qualify every harness it advertises; a combined Runtime image can include several harnesses. No E2B account key, executor key or model credential belongs in a -build, template environment, metadata, command argument or log. +build, template environment, metadata, command argument or log. The builder gives +traversable modes only to the public archive ancestors it creates (`usr`, +`usr/local`, `etc`). Runtime file and directory modes, private build contexts, +key inputs and the umask of its output stay unchanged, including under umask 077. System dependencies must be installed when building the template. The builder may use root during image construction, but the running daemon remains UID/GID 1000 diff --git a/services/agents-api/deploy/mcode/README.md b/services/agents-api/deploy/mcode/README.md index 6065804c2..185b68d48 100644 --- a/services/agents-api/deploy/mcode/README.md +++ b/services/agents-api/deploy/mcode/README.md @@ -22,25 +22,15 @@ Set `OAC_RUNTIME_MCODE_BIN` to that absolute executable and `OAC_RUNTIME_MCODE_A for the daemon. The opt-in only advertises the profile for the qualified version. Use the existing authenticated daemon connection and operator device enrollment; native self-hosted installation uses the same Runtime protocol. See the -[native guide](../../../../docs/self-hosted-native.md); MiniMax on Windows remains +[self-hosted guide](../../../../docs/getting-started/self-hosted.md#platforms); MiniMax on Windows remains unsupported. Set `OAC_DEFAULT_HARNESS=mcode` in the independent Core deployment. Existing Sessions retain their engine. Do not expose a new public harness selector. ## Docker workspace -Build the shared workspace helpers and the single CLI/companion artifact from -the pinned native source. Supply the matching npm package only for its native -runtime dependencies: - -```sh -MCODE_NATIVE_SOURCE=/absolute/minimax-code \ -MCODE_CLI_DIR=/absolute/pinned-package bash scripts/build-mcode-harness.sh -MCODE_HARNESS_BUILD_DIR=/absolute/built-companion \ -bash scripts/build-mcode-runtime.sh -docker build --platform linux/amd64 -t agents-runtime:mcode \ - "${OAC_DEV_HOME:-$HOME/.oac}/build/mcode-runtime" -``` +The [maintainer guide](../../../../docs/maintainers.md#runtime-images-and-helpers) +builds the companion from the pinned native source and the Runtime image. Configure Core's existing managed Docker provider with the immutable image ID, `deploy/codex/seccomp.json` and `nested_sandbox: true`. Core, database ownership, diff --git a/services/agents-api/deploy/microsandbox/README.md b/services/agents-api/deploy/microsandbox/README.md index fe8e66840..8d9b07ed1 100644 --- a/services/agents-api/deploy/microsandbox/README.md +++ b/services/agents-api/deploy/microsandbox/README.md @@ -50,35 +50,16 @@ removing an environment variable does not migrate its configuration ownership. ## Host and binaries -Use a dedicated service account with access to `/dev/kvm`, a C compiler and the -repository's Go version for source builds. The node's helper requires glibc and -the standard Linux dynamic libraries. Core remains a CGO-disabled build. Its -Debian slim container image does not run the helper; the standalone native node does. -Use the matched distribution's ordinary node installer for an operator installation. - -Build from the repository root: - -```sh -make build-agents-api build-daemon build-microsandbox-provider -make check-microsandbox-provider -``` - -The helper is written to -`~/.oac/build/microsandbox-provider/oac-microsandbox-provider`. -Its separate Go module pins the published SDK and embeds its matching FFI library. -`make check` runs the pure-Go provider tests on every supported host. On Linux it -also runs the SDK helper module; other hosts print an explicit skip for that -Linux-only module. A full Linux check is required before publishing this profile. - -Install the Linux x86_64 archive from the official -[v0.7.2 release](https://github.com/superradcompany/microsandbox/releases/tag/v0.7.2) -into a fresh private directory under `~/.oac/runtime/`. Verify the release -checksum before extraction. The qualified archive is -`microsandbox-linux-x86_64.tar.gz`, SHA256 -`47c223e3ef5298abf05f47ed9f87981106e400d99bb3f1d042d4d6881346b18b`. -It supplies `msb` and `libkrunfw.so.5.6.1`. Record each extracted file's SHA256 in -the provider configuration. The helper verifies both files on every invocation; -it does not install or upgrade them. +Use a dedicated service account with access to `/dev/kvm`. The node's helper +requires glibc and the standard Linux dynamic libraries. Core's container image +does not run the helper; the standalone native node does. Use the matched +distribution's ordinary node installer for an operator installation. The +[maintainer guide](../../../../docs/maintainers.md#runtime-images-and-helpers) +builds the helper and names the checksum-verified `msb` and `libkrunfw.so.5.6.1` +release archive. `make check` runs the pure-Go provider tests on every supported +host. On Linux it also runs the SDK helper module; other hosts print an explicit +skip for that Linux-only module. A full Linux check is required before publishing +this profile. For a manual node installation, create a private, short runtime state path, for example `~/.oac/msb`, with mode 0700. The ordinary installer instead selects @@ -95,7 +76,7 @@ Use the immutable Runtime release saved in the deployment specification. The ordinary node installer verifies the matched distribution manifest and imports its microsandbox image under the declared digest reference. The image must be available to the local microsandbox installation before provisioning. For source -builds, the existing [Runtime image build](../codex/README.md#managed-runtime-image-and-docker-adapter) +builds, the [Runtime image build](../../../../docs/maintainers.md#runtime-images-and-helpers) remains the image source; produce and select a matching distribution rather than substituting a local image for an already saved release. diff --git a/services/agents-api/tools/e2b-provider/README.md b/services/agents-api/tools/e2b-provider/README.md index 011f49dda..5411c15f2 100644 --- a/services/agents-api/tools/e2b-provider/README.md +++ b/services/agents-api/tools/e2b-provider/README.md @@ -89,25 +89,11 @@ Unconfirmed initialization commands require reclaiming the whole allocation. ## Build -From the repository root: - -```sh -E2B_PROVIDER_BUILD_DIR="$HOME/.oac/build/e2b-provider" scripts/build-e2b-provider.sh -``` - -Docker builds Linux amd64 output with the pinned CPython 3.12.12/Debian 12 image. -The full Python dependency closure, including PyInstaller, has hashes in -`requirements.lock`. Native `pyqwest` and `protobuf-py-ext` wheels are included. -No account credential is needed for builds or `--check`. When building from an -archived source tree, supply `E2B_SOURCE_REVISION` with its actual commit. - -The output is `oac-e2b-provider-linux-amd64.tar.gz` and its `.sha256` file. -Extraction yields `oac-e2b-provider/oac-e2b-provider`, `_internal/`, -`licenses/`, `requirements.lock` and `manifest.json`. The artifact contains only -regular files/directories, with executable permissions preserved. Core's image -and native installer use the same tree; the target needs compatible Linux/glibc -and CA certificates, but no separately installed Python. The installation owns -the durable receipt path independently of this immutable helper payload. +The [maintainer guide](../../../../docs/maintainers.md#runtime-images-and-helpers) +builds the helper. The artifact contains only regular files and directories, with +executable permissions preserved, including the native `pyqwest` and +`protobuf-py-ext` wheels. `--check` needs no account credential. The installation +owns the durable receipt path independently of this immutable helper payload. `deploy/e2b/build-template.py` packages `init.py` and `managed_init.py` with the qualified Runtime image. Existing templates without these files must be rebuilt. diff --git a/services/agents-api/tools/microsandbox-provider/README.md b/services/agents-api/tools/microsandbox-provider/README.md index 87af8167b..0a090d0f7 100644 --- a/services/agents-api/tools/microsandbox-provider/README.md +++ b/services/agents-api/tools/microsandbox-provider/README.md @@ -31,17 +31,10 @@ database. ## Build and installation -From this directory, using the repository Go version: - -```sh -GOWORK=off CGO_ENABLED=1 go build -mod=readonly -trimpath -o "$HOME/.oac/bin/oac-microsandbox-provider" . -GOWORK=off go test ./... -``` - -The relative replacement for the parent Core module refers to this checkout. -The SDK is the real published module, pinned in go.mod and go.sum; it has no -local-source replacement. The normal build embeds its matching FFI library. -Do not build production with the SDK's development `microsandbox_ffi_path` tag. +The [maintainer guide](../../../../docs/maintainers.md#runtime-images-and-helpers) +builds and tests the helper. The relative replacement for the parent Core module +refers to this checkout. The SDK is the real published module, pinned in go.mod +and go.sum, with no local-source replacement. Install the matching v0.7.2 msb runtime and firmware from checksum-verified release artifacts. Supply absolute helper/runtime/firmware paths and expected SHA256 From 8329df4840af7c1ceda7478eca7ae0fea065425e Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 06:46:41 +0000 Subject: [PATCH 5/8] docs: regenerate the documentation site --- apps/docs/content/docs/admin-api.mdx | 2 +- .../content/docs/environments-and-files.mdx | 6 +- apps/docs/content/docs/execution-model.mdx | 2 +- apps/docs/content/docs/harness-onboarding.mdx | 2 +- apps/docs/content/docs/index.mdx | 1 - apps/docs/content/docs/public-api.mdx | 4 +- .../content/docs/self-hosted-execution.mdx | 228 ++++++++++++------ apps/docs/content/docs/user-guide.mdx | 2 +- apps/docs/content/guide-sources.json | 36 ++- 9 files changed, 180 insertions(+), 103 deletions(-) diff --git a/apps/docs/content/docs/admin-api.mdx b/apps/docs/content/docs/admin-api.mdx index 07ef5079a..1bdef28a1 100644 --- a/apps/docs/content/docs/admin-api.mdx +++ b/apps/docs/content/docs/admin-api.mdx @@ -59,7 +59,7 @@ failures retain the sign-in error shape above. Poll the same-origin GET while preparing. Applying the change restarts Web and ends its sign-in sessions; provide a link to the submitted HTTPS origin for a fresh login. A dropped request or cross-origin browser probe does not prove -success. The [installer contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/maintainers.md#managed-https-ownership) owns +success. The [installer contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/deploy/install/README.md#managed-https) owns certificate verification, locking, retry and rollback. ## Console to Core diff --git a/apps/docs/content/docs/environments-and-files.mdx b/apps/docs/content/docs/environments-and-files.mdx index f56ad979c..9325b68f9 100644 --- a/apps/docs/content/docs/environments-and-files.mdx +++ b/apps/docs/content/docs/environments-and-files.mdx @@ -526,9 +526,13 @@ tools, files or network access. Outer Environments own managed isolation, and unsupported network restrictions reject instead of silently running unrestricted. Native installation and validation limits are in the -[native guide](/self-hosted-native). Historical acceptance evidence +[self-hosted guide](/self-hosted-execution#platforms). Historical acceptance evidence stays limited to its recorded binaries and inputs. +On Windows, npm package installation and stdio MCP commands named `npm` or `npx` +(including their `.cmd` shims) run through the resolved npm installation's +JavaScript entrypoint with Node, without an extra shell. + ### Environment initialization and compute wake Environment initialization has pending, running, complete and failed states. diff --git a/apps/docs/content/docs/execution-model.mdx b/apps/docs/content/docs/execution-model.mdx index 003336f41..b44304ec0 100644 --- a/apps/docs/content/docs/execution-model.mdx +++ b/apps/docs/content/docs/execution-model.mdx @@ -49,7 +49,7 @@ exception: the console authenticates the same browser session and origin, then calls the installer's private Unix socket with its server-held Core key. This is not a Core `/core/v1` route and does not use `AdminClient`. It can configure only the managed domain; it cannot submit shell commands or arbitrary process settings. -The [installer rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/maintainers.md#managed-https-ownership) own application, +The [installer rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/deploy/install/README.md#managed-https) own application, certificates and recovery. Before a domain is configured, the console accepts same-origin HTTP requests at literal IP addresses; after apply, only the configured HTTPS origin is accepted. diff --git a/apps/docs/content/docs/harness-onboarding.mdx b/apps/docs/content/docs/harness-onboarding.mdx index eb4bd57b6..afc56ea72 100644 --- a/apps/docs/content/docs/harness-onboarding.mdx +++ b/apps/docs/content/docs/harness-onboarding.mdx @@ -46,7 +46,7 @@ same contract. Operating-system support belongs in the implementation and its qualification. The native daemon supports Linux, macOS and Windows; each adapter declares its qualified platform scope. Managed Providers remain Linux-only. A platform-neutral interface alone does not qualify a harness on another platform. -See [native Runtime validation](/self-hosted-native) for the current +See [self-hosted platforms](/self-hosted-execution#platforms) for the current acceptance limits. Runtime connection, installed capability snapshot, Session Executor and Turn each have their own lifetime; see [Executor and Turn lifetimes](/runtime-protocol#executor-and-turn-lifetimes). diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx index 9c86b0981..22db7d8f2 100644 --- a/apps/docs/content/docs/index.mdx +++ b/apps/docs/content/docs/index.mdx @@ -20,7 +20,6 @@ API. Pick the path that matches your role. New here? Read the | [Configuration](/configure) | `config.json`, default models, sandbox deployment | | [Nodes](/hosted-providers) | Adding, checking and removing managed nodes | | [Self-hosted execution](/self-hosted-execution) | Connecting your own machine to a Session | -| [Native Runtime](/self-hosted-native) | Platforms, installing and operating `oac-daemon` | | [Operations](/troubleshooting) | Services, backups, keys, repair and troubleshooting | | [Web console](/console) | What the console shows and manages | diff --git a/apps/docs/content/docs/public-api.mdx b/apps/docs/content/docs/public-api.mdx index ca6557cd6..59c941a54 100644 --- a/apps/docs/content/docs/public-api.mdx +++ b/apps/docs/content/docs/public-api.mdx @@ -58,7 +58,7 @@ core() { # core METHOD PATH [JSON body] | Archive a Project (revokes all keys) | `core POST /projects/$PROJECT_ID/archive` | | See harnesses and their default models | `core GET /harnesses` | | Set Codex's default model | `core PUT /harnesses/codex/model-configuration '{"model": "your-model-id", "model_provider": {"protocol": "responses", "base_url": "https://provider.example/v1", "api_key": "sk-..."}}'` | -| Issue an executor credential | See [self-hosted execution](/self-hosted-execution#operator-credential-management) | +| Issue an executor credential | See [executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#core-key-routes) | | Installation facts, including the API base URL | `core GET /installation` | Errors use the [Core error envelope](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core-errors.md). @@ -129,7 +129,7 @@ Creating or reading a `self_hosted` Session returns short-lived install commands Machine installers use `POST /api/v1/agent-daemon/installation` and its `/claim` subroute with the installation Bearer authorization. Qualified artifacts under `/api/v1/agent-daemon/install/{version}/` are public, immutable release content. See -the [native Runtime guide](/self-hosted-native) for expiry, retry, credential +the [self-hosted guide](/self-hosted-execution) for expiry, retry, credential ownership and platform rules. The console-local `GET`/`POST /console/installation/domain` surface uses the signed-in diff --git a/apps/docs/content/docs/self-hosted-execution.mdx b/apps/docs/content/docs/self-hosted-execution.mdx index 5e2917a45..0d4d20da5 100644 --- a/apps/docs/content/docs/self-hosted-execution.mdx +++ b/apps/docs/content/docs/self-hosted-execution.mdx @@ -3,25 +3,51 @@ title: "Self-hosted executors" description: "Connect a user-owned Runtime to its Environment with a restricted executor credential." --- -A `self_hosted` Session runs on a machine the application owns. The application -creates the Session through `/v1` and receives a command that installs and connects -`oac-daemon`. Web displays the same command in the Session; it is optional. -Linux, macOS and Windows use the same Runtime protocol. Core-managed Providers -remain Linux-only. - -**The daemon is not a sandbox:** tools run with its launching user's permissions. -Use a container or VM if you need isolation; see +A `self_hosted` Session runs on a machine your application owns: a workstation, a +VM or a sandbox you manage. The application creates the Session through `/v1` and +receives a command that installs `oac-daemon`, starts it and connects it to Core. +Web shows the same command on the Session's page; it is optional. Core never +creates, stops or reclaims the machine. + +**The daemon is not a sandbox.** Tools run with the permissions of the account +that starts it and can reach whatever that account can. Use a container or VM when +you need isolation; see [Runtime and outer isolation](/concepts#runtime-and-outer-isolation). -Platforms, prerequisites and commands are in the -[native Runtime guide](/self-hosted-native). +The daemon does not restrict network access, so a Template that requires a +network policy is rejected for a self-hosted Session. -An executor credential works for one Environment only, and for nothing else. The -Session must bring its own model provider; the installation default never applies -([why](/user-guide#which-model-provider-a-session-uses)). +The Session brings its own model provider; the installation default never applies +([why](/user-guide#which-model-provider-a-session-uses)). The machine gets an +executor credential that works for this one Environment and nothing else. -## Connect a host +## Platforms -1. Choose an absolute workspace path on the target host. Create a Session with +| Platform | Codex | Claude Code | MiniMax Code | +| --- | --- | --- | --- | +| Linux amd64 | Supported | Supported | Supported | +| macOS arm64 | Supported | Supported | Supported | +| Windows amd64 | Supported | Supported | Not supported | + +The installer brings its own pinned Node.js and Harness versions (listed in +[`scripts/build-native-installer.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/scripts/build-native-installer.mjs)) +and leaves other installations of those tools untouched. On a platform without a +matching installer, the command fails. + +The machine needs: + +- HTTPS access to Core (plain HTTP only on loopback), and to the release download + host unless Core carries an offline copy of the installers; +- Bash for environment setup and MiniMax Code tools; on Windows, Git Bash, which + Claude Code also requires; +- Python and pip when the Session's packages need them; +- any system packages your setup needs. The daemon never runs apt, sudo or another + elevation command, so install them through the host's normal administration. + +No administrator privileges or Docker are needed. + +## Connect a machine + +1. Choose an absolute workspace path on the target machine. Create a Session with that path and the application's Project API key: ```python @@ -44,22 +70,52 @@ Session must bring its own model provider; the installation default never applie }, ) installation = session.model_dump()["x_agents_core"]["installation"] - print(installation["commands"]["posix"]) # use "powershell" for Windows + print(installation["commands"]["posix"]) # use "powershell" on Windows ``` -2. Run the returned command on the target machine. Select the Harnesses and - installation directory when prompted. Installation creates the workspace if - needed, starts the daemon and checks its connection. For automation, append - `--non-interactive --harness codex` and optionally `--install-dir ABS`. -3. Send a Turn. A connected Environment proves only the machine connection; the - first Turn checks the harness and model. +2. Run the command on the target machine with the account that should run the + tools. It downloads the installer matched to this Core, verifies its checksum, + asks which Harnesses to install and where, installs them, creates the workspace + if needed, starts the daemon and checks its connection. +3. Send a Turn. A connected machine proves only authentication; the first Turn + checks the Harness and the model. + +In Web, open the Session and copy the command under **Connect a host**. + +The command expires after 30 minutes. Read the Session again, or reload its page +in Web, for a fresh one. Treat the command as a temporary secret: it can claim the +machine's credential but cannot run work or read files. The +[credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#installation-grant) +describes what invalidates it. + +The installer reports three results: + +| Result | Meaning | +| --- | --- | +| **Installation** | The selected Harnesses passed their readiness checks | +| **Daemon connection** | Core confirmed the daemon's authenticated connection | +| **Model configuration** | Not checked; the first Turn uses the Session's model provider | + +If the connection is not confirmed within 45 seconds, the installer prints the +path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the +same command again with the same installation directory: completed components and +the credential are kept and a running daemon is reused. Do not remove the +workspace or the Session to retry. + +### Options for automation -In Web, open the **Self-hosted** Session and copy the command under **Connect a -host**. A command expires after 30 minutes; fetch the Session again for a fresh -one. The [native guide](/self-hosted-native#install-and-connect) covers retry, -platform prerequisites and credential storage. Core must be reachable from the -host with TLS outside loopback. Native installation does not require Docker. +Append these to the command: +| Option | Effect | +| --- | --- | +| `--non-interactive` | Never prompt; missing input fails | +| `--harness codex,claude,minimax` | Harnesses to install, comma-separated. Must include the Session's Harness | +| `--install-dir ABS` | Installation directory. Default: `environments/` under `~/.oac`, or under `OAC_RUNTIME_HOME` when set | +| `--capability-directory ABS` | Where [capability snapshots](#local-capability-directories) are stored. Default: `capabilities` in the installation directory | +| `--tool-env-file ABS` | A JSON file of string variables for tools and MCP servers; see [explicit local tool environment](/environments-and-files#explicit-local-tool-environment) | + +The workspace is fixed when the Session is created. For a different workspace, +create another Session. ## Local capability directories @@ -73,12 +129,13 @@ environment = { } ``` -Core accepts absolute Unix, Windows drive and UNC source paths without checking -its own filesystem. Runtime validates them using the executor host's path syntax. -Populate these directories before connecting the Runtime. They are ordinary paths visible -to that process; naming a directory does not mount it or create a sandbox. -Use `x_agents_core.environment` for the same project-owned Skills, Plugin archives, -files, dependencies, setup commands or Template used by a managed Session: +Paths are absolute in the machine's own syntax (Unix, Windows drive or UNC); the +daemon checks them, not Core. Fill these directories before the daemon connects. +They are ordinary paths visible to the daemon; naming one does not mount it or +create a sandbox. + +Use `x_agents_core.environment` for the same Project-owned Skills, Plugin archives, +files, packages, setup commands or Template used by a managed Session: ```python session = client.beta.agents.sessions.create( @@ -92,58 +149,77 @@ session = client.beta.agents.sessions.create( ``` The same extension works with `environment={"type": "openai_hosted"}`. Do not -repeat a field in both `environment` and the extension. The +repeat a field in both `environment` and the extension. Setup runs with the +daemon's account permissions. Deployment model keys are never sent to your +machine. + +Before the first Turn the daemon copies these sources into a snapshot. Reconnecting +reuses the snapshot even after you edit the sources; a new Session takes a new +snapshot. The [preparation contract](/environments-and-files#runtime-capability-preparation) -lists fields, merge rules and failure semantics. Your machine needs the selected -Harness and any required system dependencies; setup uses your account permissions. -Deployment model keys are never sent automatically to a user-managed machine. - -Runtime snapshots sources before native execution and uses the same parser and -`installed.json` format as managed bundles to supply Skills and Plugin MCP. -The native installer defaults the snapshot destination to `capabilities` under -`OAC_RUNTIME_HOME`; `--capability-directory` selects another local destination. -It is an operator setting, not a public API write destination. Reconnect reuses -installed contents even after source edits; a new Session captures its own -configuration. A missing or inconsistent snapshot fails preparation rather than -silently reinstalling. Local discovery does not create entries in the public -API-managed installation arrays. Snapshot file modes do not isolate the snapshot -from tools running as the same user. - -Closing an executor, cancelling a Turn or losing its connection preserves the -snapshot and workspace. The compute owner remains responsible for explicit -cleanup. [Historical qualification](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/user-managed-runtime-v1.md) -records only its stated inputs and binaries; it does not qualify the current native -platforms or local capability preparation. +lists fields, merge rules, snapshot behavior and failures. -## Rotate or revoke +## Operate the installation + +The installation's `bin/oac-daemon` finds its own installation. Use it for: -| Action in Web | Effect | +| Command | Effect | | --- | --- | -| **Rotate** | The credential gets a new secret; the old secret stops working at once. Rotating a revoked credential restores it | -| **Revoke** | The credential stops working at once | +| `oac-daemon start` | Validate the installed Harnesses and start the daemon in the background | +| `oac-daemon status` | Show the local profile and process; not the connection | +| `oac-daemon logs -n 100`, `oac-daemon logs -f` | Print or follow the daemon log | +| `oac-daemon stop` | Stop the daemon | -To reconnect a native installation, stop it with `oac-daemon stop`, rotate the same -credential, replace the JSON at its configured credential-file path, and run -`oac-daemon start`. Keep the same `OAC_RUNTIME_HOME` for every command. Issuing a -new credential does not reconnect an Environment already bound to its first -credential; rotate that credential instead. Do not run `install` again over the -existing installation. +If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the +connection under **Host connection** on the Session's page in Web, or with the +[connection status](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#connection-status). -Stopping the daemon keeps its workspace and native history. Deleting a Session -does not remove host files. In an archived Project, credentials cannot be issued -or rotated; revocation remains available. +To add a Harness, run the original install command again with the same connection +options and the Harness to add. The installer checks the existing contents, adds +only missing components and keeps the Harnesses already installed. Restart a +running daemon afterwards so it discovers the new Harness. -## Operator credential management +Stopping the daemon, cancelling a Turn or deleting the Session never removes the +machine's workspace, native history or capability snapshot. An installation from +another daemon version, or one whose files were changed, is refused. The installer +never upgrades, repairs or migrates it; install into a separate directory. -Operators can still issue, rotate or revoke executor credentials using a Core key -through Core's loopback port. This is not required for one-command onboarding. -See the [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md) -for those routes and uncertain-response handling. +## Rotate or revoke + +In Web, the Session's **Executor credentials** list the machine's credential: + +| Action | Effect | +| --- | --- | +| **Rotate** | The credential gets a new secret; the old secret stops working at once. On a revoked credential the action is **Restore** | +| **Revoke** | The credential stops working at once | -## Installation scope +To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the +JSON at its configured credential-file path with the new credential, and run +`oac-daemon start`. Do not run `install` again over the existing installation, and +do not issue a second credential: the Environment stays bound to the credential it +first connected with. + +In an archived Project, credentials cannot be issued or rotated; revocation remains +available. Operators can manage credentials with the Core key; see the +[credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#core-key-routes). + +## Install from an extracted distribution + +The same installer accepts an already extracted distribution and a credential +file issued by an operator, without the install command: + +```sh +./oac-daemon install --non-interactive --harness codex \ + --install-dir "$HOME/.oac/my-runtime" \ + --remote 'wss://core.example/api/v1/agent-daemon/ws' \ + --environment-id '11111111-2222-4333-8444-555555555555' \ + --workspace "$HOME/workspace" \ + --credential-file "$HOME/executor-credential.json" +"$HOME/.oac/my-runtime/bin/oac-daemon" start +``` -The [native installation guide](/self-hosted-native#add-harnesses-and-operate-the-installation) -owns supported installation operations and component validation. Stopping a Runtime -or deleting a Session never removes the user's files or native history. +Use the Session's `remote_url` and Environment ID. In PowerShell, run +`.\oac-daemon.exe` with native absolute paths. This mode needs an existing +workspace and does not start the daemon until you run `start`. [Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/self-hosted.md) diff --git a/apps/docs/content/docs/user-guide.mdx b/apps/docs/content/docs/user-guide.mdx index 69655bfc8..4059308af 100644 --- a/apps/docs/content/docs/user-guide.mdx +++ b/apps/docs/content/docs/user-guide.mdx @@ -145,7 +145,7 @@ After a lost response or connection: 3. Never resend without a key; you may run the work twice. On a self-hosted machine, restart the same installation to keep its workspace and -history; see [operating the installation](/self-hosted-native#add-harnesses-and-operate-the-installation). +history; see [operating the installation](/self-hosted-execution#operate-the-installation). ## Diagnose a failure diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json index 5a70d29b6..6ad73079b 100644 --- a/apps/docs/content/guide-sources.json +++ b/apps/docs/content/guide-sources.json @@ -1,62 +1,60 @@ { "revision": "f6d258735fc601c521dd990e6f9e1ed261f4ef2d", "sources": { - "docs/getting-started/README.md": "d46e1b6cd0693a282f504297c87df6ddf26b99ce8c7afdb613666acd836767f5", + "docs/getting-started/README.md": "7b7f82e484a20d7847e970c2192709091107b6acc253709c9f38ef91caeabcf1", "docs/design-principles.md": "21a05fb089804db6fab5674ec9a6cf849a3affedad2ca927a38d9b46eeec9a50", - "docs/web/architecture.md": "7d5daf7745d2a25bf1113eea48512b085137f4f806c3fa0b6b16af63f7b18c4e", + "docs/web/architecture.md": "cf694aa624cf6078a154c2f8fdd67055d41dea0c0bb80b8dc8f0cdbc5d572fb1", "docs/getting-started/install.md": "84b21002136acd4f006392685b7a071323336e87c1aac6e3c23ca56acb40833c", "docs/getting-started/install-options.md": "e03487bb6d978f84c596467028e25fd99fdc21cf2060af24ceb68df885084798", "docs/configuration.md": "7dc0031145bb5811bf22cab15270b511b1c43b1bd1ee2b71175d3bc848751bd6", "docs/web/core-connection.md": "861c75c32676fd17b84eb356b263096703af5750c1ceb71bb339e84607fffc56", "docs/getting-started/quickstart.md": "b989682ac2e58d59a794118fd371b37d1ea64c957d3512ff53739458a95d0f9a", - "docs/api/README.md": "dbe3f172a997ee2fd3d2e5765d382b4fb7cf7c50f1dd17212ab9646f6959d63e", + "docs/api/README.md": "3a633c6e9820ab15952d1361856951c9ce463ea9d4c3d32868d147d05ecd77fd", "contracts/agents-api/execution-tools.md": "fe1e3cf471fe9c7ef04afa7230e6b7742fbf2146c74bd944a7a22d5d4d4197da", "docs/api/public-agent-api.md": "e1111aaf5215392178246969e281b930374b22ee380d529b08b2482836d6333d", "docs/examples.md": "c12c34adc5b2fa57c81602b23d8ecc8317596f9c5a4aedbf1f1fe934a08a94f5", - "contracts/agents-api/environments.md": "3ec73248afbc04e79e2fd0cb6cf4e6fbc0ff915722df2ee676d2537f30fd43ad", + "contracts/agents-api/environments.md": "2f4d506d7353c3ab4734bd2c06216dbac231d951e95e397171e793d74dc535b0", "docs/getting-started/nodes.md": "7c1b7e364ce85b5b5916358cafc618f29042b2125ac45653be665ba07e772fdb", - "docs/getting-started/self-hosted.md": "5ade597e09cbfa2e321f341693b47730b3696fe7bd4d6834eafbe6b279a55e6b", - "docs/self-hosted-native.md": "b3cf736f88792e6925c50b81a4c33eba6b9ee86e195f9ed4e2187db7bed71d88", + "docs/getting-started/self-hosted.md": "f46d367ef10ac989d883bdefc66a1f17968e59413f7c3aa70a04df85e315914b", "apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", "docs/web/README.md": "47159689b2488f0a94a1af8488bcf98b465e0cca4003d64a4699d7b4db24e086", - "docs/api/web-management.md": "fe24e883f5745d262d3bd88eb73ae2cbbb723297b9a237f28849f9e3b50dffd4", + "docs/api/web-management.md": "fc064c53874ca2aa6d17e5265763a4a4c744860062c27353cfafe74d3e6ac790", "contracts/agents-api/runtime-observability-api.md": "cd46e777fe716a7e76374f4655c96dfbad9d8be1bfca6203ed6118ab87874c2b", "docs/getting-started/operations.md": "4069e89deed7ef619f28cc8d9779055669246eabe322089113ac3a786c3cb2d5", - "docs/user-guide.md": "bd9049f1f5361dbb643d1174e9445c13a83eac02b17e24bb348f6c8a2ea53654", + "docs/user-guide.md": "078ad930003dd333e65beb152dab11809b08d58dcc9d5d108c61f60dc3a320e2", "docs/assets/development-architecture.png": "24e6d0145d4f16ad70b07b6bc643808a6455aaf6398434d199cf74def200fca6", "docs/development.md": "f5c340253036a2cabc43e22aecdf70522d90280d6511e7649278ae93712ab8b6", - "contracts/agents-api/harness-onboarding.md": "acbc9bf8c1365e1242d4c9a934b068a19ef3450a4aef2fc08356624380222266", + "contracts/agents-api/harness-onboarding.md": "03b069b0c2ae18248cf6b1d1c82b6c3a6cd74719746bd343acb128a86d2c67a2", "docs/runtime-bootstrap.md": "0d49aed73b298039e04453e6f465b0e925fb2227fa35d4206820bb3acbd8df39", "docs/runtime-protocol.md": "4bce016e8ec239281969c84c8483044cf3247051eb062ec30d3effa00b661001", "docs/sandbox-provider.md": "a1323c7f6a28a4f5f0e823274c08422707a5dab85b11e008ccb28a3aafc02058", - "apps/docs/scripts/guides.json": "3c3768fb94fd3d42464d4ce8c55724b9ba8360133c7cc19d513d8ec83a8e034f" + "apps/docs/scripts/guides.json": "19265370dbcb8e30b125079d0d25fad05b6f783ec368b7ebb6e81389b7853342" }, "outputs": { - "content/docs/index.mdx": "432dd8f02f72edb9f39b92d272719b146c5c1bf5b0429191d0f4a56932b92353", + "content/docs/index.mdx": "bf78e8aab0a834fb62ee7287fef3e29363b7384e63ec61aefcf7448f65a71a80", "content/docs/concepts.mdx": "88ba65e1ba902921d6cd5da2866620a6dbef52c041db991312a138884fb43a4e", - "content/docs/execution-model.mdx": "50133f3911c5ad801868e695ad52651e83809b80cbc887ee0bca25769ad32065", + "content/docs/execution-model.mdx": "7b8d8498a1b157270f00a8c370c9a86547fa481df9ffa8904c810946b1626b38", "content/docs/install.mdx": "6c3ec01d1b80cbce35d58e3f141b0fa832d6294a2ecc07c3b286ecc56ba66919", "content/docs/install-options.mdx": "aaafc209293a905d5b433511149aa1f0cc422bd31d5bb5b6cd7eec0d532ab8eb", "content/docs/configure.mdx": "02d1eee789646fdf65ed2ec48b6fe954c81107d6ff5891536ec6ac2df0a8c4dc", "content/docs/bootstrap-projects-keys.mdx": "91a6f62cbe41737adfce9e8ec56e6dc3e2fb0ec8f6b677576f941cdfb378dc47", "content/docs/quickstart.mdx": "d0ab9537ab68f6dd3109353e937a29c5d58ec3894b56a52873c9a1d79dfa60a6", - "content/docs/public-api.mdx": "cd59f4a661f897d1a9dc6ff08a09f7b7504c355288bbf022eff9b70500a9865c", + "content/docs/public-api.mdx": "ca7b39e0ff296e82f5be012692a166fea9f278d7cfe02224f0f0fb9f17f6722d", "content/docs/agents-and-tools.mdx": "3ca16d2e2ff1c759251fbdf6e071ff09a93d5efc7acaca36a48854597d4f9576", "content/docs/sessions.mdx": "e8693563738f690c21be92e0ea09e925528bc85bea2c0fb4799c3f1c4074cfba", "content/docs/examples.mdx": "5f1dde8043695c40edf775345159d93b2444d5ce6a109829b78678b0b5de0d46", - "content/docs/environments-and-files.mdx": "e054c18b22581c3573a6018a9659879ed2776e029d0d12c17e90eb537becb50c", + "content/docs/environments-and-files.mdx": "a51751d78649cbd5af3458890efd360a857f82538de889d6419a458d01b2a02d", "content/docs/hosted-providers.mdx": "9c241090709a9929ab6a34615db1e20a94c1f36649026281836060e81ac40b4c", - "content/docs/self-hosted-execution.mdx": "eeed4c6b3646927ccc3c7ac2d20b2c0f9c8a65e42d734310fe3959e016324e57", - "content/docs/self-hosted-native.mdx": "671451580dfb4f5c134221991f68292a4f992008174d23190b1da3742046571f", + "content/docs/self-hosted-execution.mdx": "0a414f3bc3a8ec5c86eedea52fd889ef0b83cba96a8d380bf9de769880e6d70a", "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", "content/docs/console.mdx": "a930ce36035de74f0c2bef71bed067fc2287ba1c70a485758bf100d484f6adfd", - "content/docs/admin-api.mdx": "f43f4f1cac887bc4baa4968d76542a87ba1a6ddeef9259299b53cd98a4aea5e1", + "content/docs/admin-api.mdx": "6fcfe4c5b5b70d5322c131ce4ab70f1a7ea8b0033a5f500b20cf27abb338236e", "content/docs/observability.mdx": "c81214e1865517a9163c48c7ce7c396f5490c9d162080dfa05fa5ebc147f6c8b", "content/docs/troubleshooting.mdx": "bccd725d2389a80b66caf1a1da80532bd68dd3d470bf851799d0114f84e0af25", - "content/docs/user-guide.mdx": "cbe8d7f62666ca229041e7413f254cef051bc93b8abe2aefd94ee77c00fa4c85", + "content/docs/user-guide.mdx": "92b839c8d1a2fbf4d35388c414c4524734f32724c30c07395edc994fa11cf702", "public/images/source/docs/assets/development-architecture.png": "24e6d0145d4f16ad70b07b6bc643808a6455aaf6398434d199cf74def200fca6", "content/docs/development.mdx": "2e5249ddfca571d264e93fd1e0e390a4bd5b0b623bda100cd02f2982929850bb", - "content/docs/harness-onboarding.mdx": "f0d91fd8f4cbaaa9475a25e24b08d9bfb0c25a8549af47b81073886444e5ac44", + "content/docs/harness-onboarding.mdx": "e434d62bd0b558745774c8d729e4d86d2b40ed3026e3d77b777c48aef141c659", "content/docs/runtime-bootstrap.mdx": "58982955811c9ad46a9fc04d8d2aef5a762fc9d25d5833f88aa75ee3fc46523f", "content/docs/runtime-protocol.mdx": "560f887d5fc9fa673275a6481220f5fb997c4ece30c09384b81b14dbc7aa0dd4", "content/docs/sandbox-provider.mdx": "83a6244fb2140836aa6746c990c9cd04d04e58d5b3a52faf1a84e5e846dfd99f" From 9dfb0299a0fb32acacb664079793cd10c1da6947 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 06:55:03 +0000 Subject: [PATCH 6/8] docs: unwrap prose in build, release and self-hosted docs Unwrap the Markdown files this PR owns (unwrap_md.py; rendering verified identical with render_equal.mjs) and regenerate the docs site. Let the service README's name allowlist entry match its line wrapped or not. --- .../content/docs/self-hosted-execution.mdx | 111 ++---- apps/docs/content/guide-sources.json | 4 +- .../environment-executor-credentials.md | 175 ++------- deploy/install/README.md | 353 ++++-------------- docs/getting-started/self-hosted.md | 111 ++---- docs/maintainers.md | 242 +++--------- scripts/name-allowlist.json | 2 +- services/agents-api/RELEASE.md | 45 +-- 8 files changed, 219 insertions(+), 824 deletions(-) diff --git a/apps/docs/content/docs/self-hosted-execution.mdx b/apps/docs/content/docs/self-hosted-execution.mdx index 0d4d20da5..9c65e1233 100644 --- a/apps/docs/content/docs/self-hosted-execution.mdx +++ b/apps/docs/content/docs/self-hosted-execution.mdx @@ -3,22 +3,11 @@ title: "Self-hosted executors" description: "Connect a user-owned Runtime to its Environment with a restricted executor credential." --- -A `self_hosted` Session runs on a machine your application owns: a workstation, a -VM or a sandbox you manage. The application creates the Session through `/v1` and -receives a command that installs `oac-daemon`, starts it and connects it to Core. -Web shows the same command on the Session's page; it is optional. Core never -creates, stops or reclaims the machine. - -**The daemon is not a sandbox.** Tools run with the permissions of the account -that starts it and can reach whatever that account can. Use a container or VM when -you need isolation; see -[Runtime and outer isolation](/concepts#runtime-and-outer-isolation). -The daemon does not restrict network access, so a Template that requires a -network policy is rejected for a self-hosted Session. - -The Session brings its own model provider; the installation default never applies -([why](/user-guide#which-model-provider-a-session-uses)). The machine gets an -executor credential that works for this one Environment and nothing else. +A `self_hosted` Session runs on a machine your application owns: a workstation, a VM or a sandbox you manage. The application creates the Session through `/v1` and receives a command that installs `oac-daemon`, starts it and connects it to Core. Web shows the same command on the Session's page; it is optional. Core never creates, stops or reclaims the machine. + +**The daemon is not a sandbox.** Tools run with the permissions of the account that starts it and can reach whatever that account can. Use a container or VM when you need isolation; see [Runtime and outer isolation](/concepts#runtime-and-outer-isolation). The daemon does not restrict network access, so a Template that requires a network policy is rejected for a self-hosted Session. + +The Session brings its own model provider; the installation default never applies ([why](/user-guide#which-model-provider-a-session-uses)). The machine gets an executor credential that works for this one Environment and nothing else. ## Platforms @@ -28,27 +17,20 @@ executor credential that works for this one Environment and nothing else. | macOS arm64 | Supported | Supported | Supported | | Windows amd64 | Supported | Supported | Not supported | -The installer brings its own pinned Node.js and Harness versions (listed in -[`scripts/build-native-installer.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/scripts/build-native-installer.mjs)) -and leaves other installations of those tools untouched. On a platform without a -matching installer, the command fails. +The installer brings its own pinned Node.js and Harness versions (listed in [`scripts/build-native-installer.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/scripts/build-native-installer.mjs)) and leaves other installations of those tools untouched. On a platform without a matching installer, the command fails. The machine needs: -- HTTPS access to Core (plain HTTP only on loopback), and to the release download - host unless Core carries an offline copy of the installers; -- Bash for environment setup and MiniMax Code tools; on Windows, Git Bash, which - Claude Code also requires; +- HTTPS access to Core (plain HTTP only on loopback), and to the release download host unless Core carries an offline copy of the installers; +- Bash for environment setup and MiniMax Code tools; on Windows, Git Bash, which Claude Code also requires; - Python and pip when the Session's packages need them; -- any system packages your setup needs. The daemon never runs apt, sudo or another - elevation command, so install them through the host's normal administration. +- any system packages your setup needs. The daemon never runs apt, sudo or another elevation command, so install them through the host's normal administration. No administrator privileges or Docker are needed. ## Connect a machine -1. Choose an absolute workspace path on the target machine. Create a Session with - that path and the application's Project API key: +1. Choose an absolute workspace path on the target machine. Create a Session with that path and the application's Project API key: ```python import os @@ -73,20 +55,12 @@ No administrator privileges or Docker are needed. print(installation["commands"]["posix"]) # use "powershell" on Windows ``` -2. Run the command on the target machine with the account that should run the - tools. It downloads the installer matched to this Core, verifies its checksum, - asks which Harnesses to install and where, installs them, creates the workspace - if needed, starts the daemon and checks its connection. -3. Send a Turn. A connected machine proves only authentication; the first Turn - checks the Harness and the model. +2. Run the command on the target machine with the account that should run the tools. It downloads the installer matched to this Core, verifies its checksum, asks which Harnesses to install and where, installs them, creates the workspace if needed, starts the daemon and checks its connection. +3. Send a Turn. A connected machine proves only authentication; the first Turn checks the Harness and the model. In Web, open the Session and copy the command under **Connect a host**. -The command expires after 30 minutes. Read the Session again, or reload its page -in Web, for a fresh one. Treat the command as a temporary secret: it can claim the -machine's credential but cannot run work or read files. The -[credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#installation-grant) -describes what invalidates it. +The command expires after 30 minutes. Read the Session again, or reload its page in Web, for a fresh one. Treat the command as a temporary secret: it can claim the machine's credential but cannot run work or read files. The [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#installation-grant) describes what invalidates it. The installer reports three results: @@ -96,11 +70,7 @@ The installer reports three results: | **Daemon connection** | Core confirmed the daemon's authenticated connection | | **Model configuration** | Not checked; the first Turn uses the Session's model provider | -If the connection is not confirmed within 45 seconds, the installer prints the -path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the -same command again with the same installation directory: completed components and -the credential are kept and a running daemon is reused. Do not remove the -workspace or the Session to retry. +If the connection is not confirmed within 45 seconds, the installer prints the path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the same command again with the same installation directory: completed components and the credential are kept and a running daemon is reused. Do not remove the workspace or the Session to retry. ### Options for automation @@ -114,8 +84,7 @@ Append these to the command: | `--capability-directory ABS` | Where [capability snapshots](#local-capability-directories) are stored. Default: `capabilities` in the installation directory | | `--tool-env-file ABS` | A JSON file of string variables for tools and MCP servers; see [explicit local tool environment](/environments-and-files#explicit-local-tool-environment) | -The workspace is fixed when the Session is created. For a different workspace, -create another Session. +The workspace is fixed when the Session is created. For a different workspace, create another Session. ## Local capability directories @@ -129,13 +98,9 @@ environment = { } ``` -Paths are absolute in the machine's own syntax (Unix, Windows drive or UNC); the -daemon checks them, not Core. Fill these directories before the daemon connects. -They are ordinary paths visible to the daemon; naming one does not mount it or -create a sandbox. +Paths are absolute in the machine's own syntax (Unix, Windows drive or UNC); the daemon checks them, not Core. Fill these directories before the daemon connects. They are ordinary paths visible to the daemon; naming one does not mount it or create a sandbox. -Use `x_agents_core.environment` for the same Project-owned Skills, Plugin archives, -files, packages, setup commands or Template used by a managed Session: +Use `x_agents_core.environment` for the same Project-owned Skills, Plugin archives, files, packages, setup commands or Template used by a managed Session: ```python session = client.beta.agents.sessions.create( @@ -148,16 +113,9 @@ session = client.beta.agents.sessions.create( ) ``` -The same extension works with `environment={"type": "openai_hosted"}`. Do not -repeat a field in both `environment` and the extension. Setup runs with the -daemon's account permissions. Deployment model keys are never sent to your -machine. +The same extension works with `environment={"type": "openai_hosted"}`. Do not repeat a field in both `environment` and the extension. Setup runs with the daemon's account permissions. Deployment model keys are never sent to your machine. -Before the first Turn the daemon copies these sources into a snapshot. Reconnecting -reuses the snapshot even after you edit the sources; a new Session takes a new -snapshot. The -[preparation contract](/environments-and-files#runtime-capability-preparation) -lists fields, merge rules, snapshot behavior and failures. +Before the first Turn the daemon copies these sources into a snapshot. Reconnecting reuses the snapshot even after you edit the sources; a new Session takes a new snapshot. The [preparation contract](/environments-and-files#runtime-capability-preparation) lists fields, merge rules, snapshot behavior and failures. ## Operate the installation @@ -170,19 +128,11 @@ The installation's `bin/oac-daemon` finds its own installation. Use it for: | `oac-daemon logs -n 100`, `oac-daemon logs -f` | Print or follow the daemon log | | `oac-daemon stop` | Stop the daemon | -If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the -connection under **Host connection** on the Session's page in Web, or with the -[connection status](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#connection-status). +If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the connection under **Host connection** on the Session's page in Web, or with the [connection status](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#connection-status). -To add a Harness, run the original install command again with the same connection -options and the Harness to add. The installer checks the existing contents, adds -only missing components and keeps the Harnesses already installed. Restart a -running daemon afterwards so it discovers the new Harness. +To add a Harness, run the original install command again with the same connection options and the Harness to add. The installer checks the existing contents, adds only missing components and keeps the Harnesses already installed. Restart a running daemon afterwards so it discovers the new Harness. -Stopping the daemon, cancelling a Turn or deleting the Session never removes the -machine's workspace, native history or capability snapshot. An installation from -another daemon version, or one whose files were changed, is refused. The installer -never upgrades, repairs or migrates it; install into a separate directory. +Stopping the daemon, cancelling a Turn or deleting the Session never removes the machine's workspace, native history or capability snapshot. An installation from another daemon version, or one whose files were changed, is refused. The installer never upgrades, repairs or migrates it; install into a separate directory. ## Rotate or revoke @@ -193,20 +143,13 @@ In Web, the Session's **Executor credentials** list the machine's credential: | **Rotate** | The credential gets a new secret; the old secret stops working at once. On a revoked credential the action is **Restore** | | **Revoke** | The credential stops working at once | -To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the -JSON at its configured credential-file path with the new credential, and run -`oac-daemon start`. Do not run `install` again over the existing installation, and -do not issue a second credential: the Environment stays bound to the credential it -first connected with. +To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the JSON at its configured credential-file path with the new credential, and run `oac-daemon start`. Do not run `install` again over the existing installation, and do not issue a second credential: the Environment stays bound to the credential it first connected with. -In an archived Project, credentials cannot be issued or rotated; revocation remains -available. Operators can manage credentials with the Core key; see the -[credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#core-key-routes). +In an archived Project, credentials cannot be issued or rotated; revocation remains available. Operators can manage credentials with the Core key; see the [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#core-key-routes). ## Install from an extracted distribution -The same installer accepts an already extracted distribution and a credential -file issued by an operator, without the install command: +The same installer accepts an already extracted distribution and a credential file issued by an operator, without the install command: ```sh ./oac-daemon install --non-interactive --harness codex \ @@ -218,8 +161,6 @@ file issued by an operator, without the install command: "$HOME/.oac/my-runtime/bin/oac-daemon" start ``` -Use the Session's `remote_url` and Environment ID. In PowerShell, run -`.\oac-daemon.exe` with native absolute paths. This mode needs an existing -workspace and does not start the daemon until you run `start`. +Use the Session's `remote_url` and Environment ID. In PowerShell, run `.\oac-daemon.exe` with native absolute paths. This mode needs an existing workspace and does not start the daemon until you run `start`. [Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/self-hosted.md) diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json index 6ad73079b..48a8be4af 100644 --- a/apps/docs/content/guide-sources.json +++ b/apps/docs/content/guide-sources.json @@ -15,7 +15,7 @@ "docs/examples.md": "c12c34adc5b2fa57c81602b23d8ecc8317596f9c5a4aedbf1f1fe934a08a94f5", "contracts/agents-api/environments.md": "2f4d506d7353c3ab4734bd2c06216dbac231d951e95e397171e793d74dc535b0", "docs/getting-started/nodes.md": "7c1b7e364ce85b5b5916358cafc618f29042b2125ac45653be665ba07e772fdb", - "docs/getting-started/self-hosted.md": "f46d367ef10ac989d883bdefc66a1f17968e59413f7c3aa70a04df85e315914b", + "docs/getting-started/self-hosted.md": "6e77e3566309f51c6228d18648f33a9e7fe093715c03e6b930681314cd5a6de7", "apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", "docs/web/README.md": "47159689b2488f0a94a1af8488bcf98b465e0cca4003d64a4699d7b4db24e086", "docs/api/web-management.md": "fc064c53874ca2aa6d17e5265763a4a4c744860062c27353cfafe74d3e6ac790", @@ -45,7 +45,7 @@ "content/docs/examples.mdx": "5f1dde8043695c40edf775345159d93b2444d5ce6a109829b78678b0b5de0d46", "content/docs/environments-and-files.mdx": "a51751d78649cbd5af3458890efd360a857f82538de889d6419a458d01b2a02d", "content/docs/hosted-providers.mdx": "9c241090709a9929ab6a34615db1e20a94c1f36649026281836060e81ac40b4c", - "content/docs/self-hosted-execution.mdx": "0a414f3bc3a8ec5c86eedea52fd889ef0b83cba96a8d380bf9de769880e6d70a", + "content/docs/self-hosted-execution.mdx": "fea4a3dab34fcdb8f4ea31f1a9cb564a9e98889b5ab1057a1bea143a4a16952d", "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", "content/docs/console.mdx": "a930ce36035de74f0c2bef71bed067fc2287ba1c70a485758bf100d484f6adfd", "content/docs/admin-api.mdx": "6fcfe4c5b5b70d5322c131ce4ab70f1a7ea8b0033a5f500b20cf27abb338236e", diff --git a/contracts/agents-api/environment-executor-credentials.md b/contracts/agents-api/environment-executor-credentials.md index 682a25f79..efe946666 100644 --- a/contracts/agents-api/environment-executor-credentials.md +++ b/contracts/agents-api/environment-executor-credentials.md @@ -1,31 +1,17 @@ # Environment executor credentials -An executor credential lets `oac-daemon` enroll and connect for one `self_hosted` -Environment. It authorizes only the private daemon transport -(`/api/v1/agent-daemon/*`) for that Environment, never `/v1`, `/core/v1`, -sandbox-node enrollment or Project resources. The Project's principal is its -execution principal. Core stores only a digest of the secret. +An executor credential lets `oac-daemon` enroll and connect for one `self_hosted` Environment. It authorizes only the private daemon transport (`/api/v1/agent-daemon/*`) for that Environment, never `/v1`, `/core/v1`, sandbox-node enrollment or Project resources. The Project's principal is its execution principal. Core stores only a digest of the secret. A credential comes from one of two places: -- **The installation grant.** A `self_hosted` Session returns an install command. - The installer uses the command's short-lived grant to claim one credential; it - needs neither Web nor the Core key. The - [self-hosted guide](../../docs/getting-started/self-hosted.md) shows the steps. -- **The Core-key routes.** An operator issues, rotates and revokes credentials - through Web or `/core/v1`. +- **The installation grant.** A `self_hosted` Session returns an install command. The installer uses the command's short-lived grant to claim one credential; it needs neither Web nor the Core key. The [self-hosted guide](../../docs/getting-started/self-hosted.md) shows the steps. +- **The Core-key routes.** An operator issues, rotates and revokes credentials through Web or `/core/v1`. -Core never creates, stops or reclaims the machine. Disconnecting, revoking a -credential or deleting the Session does not prove that every native process has -stopped; the machine's owner stops and cleans up its own compute. +Core never creates, stops or reclaims the machine. Disconnecting, revoking a credential or deleting the Session does not prove that every native process has stopped; the machine's owner stops and cleans up its own compute. ## Installation grant -Session create, retrieve and update responses of a `self_hosted` Session carry -`x_agents_core.installation`; Session lists do not. Web reads the same object with -the Core key at -`GET /core/v1/projects/{project_id}/environments/{environment_id}/installation` -and shows its commands without changing them. +Session create, retrieve and update responses of a `self_hosted` Session carry `x_agents_core.installation`; Session lists do not. Web reads the same object with the Core key at `GET /core/v1/projects/{project_id}/environments/{environment_id}/installation` and shows its commands without changing them. | Field | Meaning | | --- | --- | @@ -34,19 +20,9 @@ and shows its commands without changing them. | `expires_at` | Unix time when the grant expires, 30 minutes after the response | | `commands.posix`, `commands.powershell` | The install command for Linux/macOS and for Windows PowerShell | -The grant is bound to the Environment, the Session creator's principal and the -Core build. It stops working when it expires, when the Session is deleted, when the -Project is archived or when Core runs a different build. Reading the Session again -returns a fresh grant. Treat the command as a temporary secret: it can claim the -credential, but it cannot run work or read files. +The grant is bound to the Environment, the Session creator's principal and the Core build. It stops working when it expires, when the Session is deleted, when the Project is archived or when Core runs a different build. Reading the Session again returns a fresh grant. Treat the command as a temporary secret: it can claim the credential, but it cannot run work or read files. -The installer generates the secret and saves it privately as -`daemon/executor-credential.json` in the installation directory before it claims -the key. Core stores the digest under the key ID equal to the Environment ID. A -lost response is safe to retry: the retry must present the same secret. A grant -never replaces or restores a credential. If the Environment already has a -different, rotated or revoked credential, the claim fails with 409 -`executor_credential_exists`. +The installer generates the secret and saves it privately as `daemon/executor-credential.json` in the installation directory before it claims the key. Core stores the digest under the key ID equal to the Environment ID. A lost response is safe to retry: the retry must present the same secret. A grant never replaces or restores a credential. If the Environment already has a different, rotated or revoked credential, the claim fails with 409 `executor_credential_exists`. The installer calls these machine routes on Core: @@ -57,19 +33,11 @@ The installer calls these machine routes on Core: | `POST /api/v1/agent-daemon/installation` | Grant | The frozen binding: `version`, `protocol_version`, `environment_id`, `remote_url`, `workspace_directory`, `harness` | | `POST /api/v1/agent-daemon/installation/claim` | Grant | `{"executor_token":"SECRET"}`; 204 | -An invalid or expired grant returns 401 `installation_authorization_invalid`. -Without matching installers the grant routes return 503 `installation_unavailable`. -A malformed secret returns 400. Artifact routes carry no credential, and the grant -is sent only to Core, never to an artifact host. +An invalid or expired grant returns 401 `installation_authorization_invalid`. Without matching installers the grant routes return 503 `installation_unavailable`. A malformed secret returns 400. Artifact routes carry no credential, and the grant is sent only to Core, never to an artifact host. ## Core-key routes -All routes are under -`/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` -and require the Core key. They apply only to a `self_hosted` Environment of that -Project whose Session exists (is not deleted); any other Project, Environment -type, missing Environment or deleted Session returns 404. Project API keys cannot -use them. +All routes are under `/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` and require the Core key. They apply only to a `self_hosted` Environment of that Project whose Session exists (is not deleted); any other Project, Environment type, missing Environment or deleted Session returns 404. Project API keys cannot use them. | Operation | Request | Result | | --- | --- | --- | @@ -77,131 +45,52 @@ use them. | Issue or rotate | `POST …/executor-credentials` with `{"key_id":"UUID","rotate":false}` | 201 credential file, returned once | | Revoke | `DELETE …/executor-credentials/{key_id}` | 204 | -The list holds metadata only, oldest first, for the credentials restricted to this -Environment; `revoked_at` is null while a credential is active. It never contains -a secret. +The list holds metadata only, oldest first, for the credentials restricted to this Environment; `revoked_at` is null while a credential is active. It never contains a secret. -`key_id` is a canonical nonzero UUID chosen and retained before the request. -`rotate` is optional and defaults to false. The 201 response is the daemon -credential-file format: +`key_id` is a canonical nonzero UUID chosen and retained before the request. `rotate` is optional and defaults to false. The 201 response is the daemon credential-file format: ```json {"key_id":"UUID","environment_id":"ENVIRONMENT_UUID","executor_token":"ONE_TIME_SECRET"} ``` -Responses use `Cache-Control: no-store`. Save the response directly to an owned -mode-0600 file; never place it in shell arguments, logs, a workspace or source. +Responses use `Cache-Control: no-store`. Save the response directly to an owned mode-0600 file; never place it in shell arguments, logs, a workspace or source. -Writes have two conflicts, both 409. `executor_credential_exists`: an issuance -whose `key_id` already exists and does not set `rotate:true`, even after -revocation. `project_archived`: the Project is archived, so it gets no new or -rotated credential; listing and revocation remain available there, because -revoking must always work. +Writes have two conflicts, both 409. `executor_credential_exists`: an issuance whose `key_id` already exists and does not set `rotate:true`, even after revocation. `project_archived`: the Project is archived, so it gets no new or rotated credential; listing and revocation remain available there, because revoking must always work. -An issuance or rotation is checked in this order, and the first failure is -returned: the request body (400); the target Environment (404); an archived -Project (409 `project_archived`); then the key itself (409 -`executor_credential_exists` without `rotate`, or 404 when rotating a `key_id` -that was never issued). +An issuance or rotation is checked in this order, and the first failure is returned: the request body (400); the target Environment (404); an archived Project (409 `project_archived`); then the key itself (409 `executor_credential_exists` without `rotate`, or 404 when rotating a `key_id` that was never issued). -Rotation replaces the secret of an existing key restricted to this Environment, -keeps that Environment, invalidates the previous secret at once and restores a -revoked key. Revocation is idempotent and returns 204 each time. It denies further -enrollment and connection. +Rotation replaces the secret of an existing key restricted to this Environment, keeps that Environment, invalidates the previous secret at once and restores a revoked key. Revocation is idempotent and returns 204 each time. It denies further enrollment and connection. -After an uncertain result, such as a timeout, do not retry automatically. List -the credentials, then either rotate the same `key_id` (it was issued but its -secret was lost) or issue it again (it was not issued). +After an uncertain result, such as a timeout, do not retry automatically. List the credentials, then either rotate the same `key_id` (it was issued but its secret was lost) or issue it again (it was not issued). -Issue, rotate and revoke each record an administrator audit entry -(`resource_type:"executor_credential"`, the key ID as `resource_id`, action -`issue`, `rotate` or `revoke`) in the same transaction as the write. The audit -never contains the secret. +Issue, rotate and revoke each record an administrator audit entry (`resource_type:"executor_credential"`, the key ID as `resource_id`, action `issue`, `rotate` or `revoke`) in the same transaction as the write. The audit never contains the secret. ### Break-glass command -`oac-core-environment-key` issues, rotates or revokes a credential directly in the -database when the Core API is unavailable. It needs the private database -configuration and the Project's execution principal: `--tenant` (the Project's -tenant UUID in the `projects` table), `--organization core`, -`--project proj_`, `--subject-kind service_account`, -`--subject-id project:` and `--key-id`. `--environment` restricts a -new credential to one Environment. The command bypasses the Core API: it skips the -archived-Project check and writes no audit entry, so use the Core-key routes -whenever Core is running. A credential issued without an Environment restriction -cannot be managed through the routes above. +`oac-core-environment-key` issues, rotates or revokes a credential directly in the database when the Core API is unavailable. It needs the private database configuration and the Project's execution principal: `--tenant` (the Project's tenant UUID in the `projects` table), `--organization core`, `--project proj_`, `--subject-kind service_account`, `--subject-id project:` and `--key-id`. `--environment` restricts a new credential to one Environment. The command bypasses the Core API: it skips the archived-Project check and writes no audit entry, so use the Core-key routes whenever Core is running. A credential issued without an Environment restriction cannot be managed through the routes above. ## Connection status -The list's required `connection` object contains `status` (`never_enrolled`, -`connected` or `disconnected`), `bound_key_id`, `enrolled_at` and `last_seen_at`. -All three binding fields are null before enrollment. Once enrolled, the bound key -and enrollment time describe the existing device; a null `last_seen_at` means no -authenticated heartbeat has been recorded. Issuing another key does not change the -binding. Rotation or revocation can make the binding disconnected while its -history remains visible. Expired Environments remain readable under the existing -list rules but cannot have current executor authority. - -Connected means the Environment is connected, its device and executor key still -have current Core authority, and the process-local gateway has an open peer that -authenticated with that current key. Core rechecks authority after observing the -peer. A former key's live socket, a device timestamp or a ready-looking -Environment alone is insufficient; without a gateway, Core never returns -connected. These facts are an observation, not a reservation of connectivity or -of native or model readiness. `last_seen_at` may lag by a heartbeat interval. - -List metadata and binding facts use one read-only database snapshot. That snapshot -ends before the live authority checks, so a committed rotation or revocation is -not hidden by snapshot isolation. Known authority loss projects as disconnected; -observation and storage failures remain errors. Device IDs and credential digests -are internal and never serialized. The public `/v1` Environment shape is unchanged. +The list's required `connection` object contains `status` (`never_enrolled`, `connected` or `disconnected`), `bound_key_id`, `enrolled_at` and `last_seen_at`. All three binding fields are null before enrollment. Once enrolled, the bound key and enrollment time describe the existing device; a null `last_seen_at` means no authenticated heartbeat has been recorded. Issuing another key does not change the binding. Rotation or revocation can make the binding disconnected while its history remains visible. Expired Environments remain readable under the existing list rules but cannot have current executor authority. + +Connected means the Environment is connected, its device and executor key still have current Core authority, and the process-local gateway has an open peer that authenticated with that current key. Core rechecks authority after observing the peer. A former key's live socket, a device timestamp or a ready-looking Environment alone is insufficient; without a gateway, Core never returns connected. These facts are an observation, not a reservation of connectivity or of native or model readiness. `last_seen_at` may lag by a heartbeat interval. + +List metadata and binding facts use one read-only database snapshot. That snapshot ends before the live authority checks, so a committed rotation or revocation is not hidden by snapshot isolation. Known authority loss projects as disconnected; observation and storage failures remain errors. Device IDs and credential digests are internal and never serialized. The public `/v1` Environment shape is unchanged. ### Private connection confirmation -`GET /api/v1/agent-daemon/connection?environment_id=UUID` uses the executor bearer, -sent directly to Core (the reverse proxy routes `/api/v1` to Core; the console does -not serve it). It is part of the private daemon transport, not the public Agents -API. It reads existing authorization and binding only; it never enrolls a device, -starts execution or changes resources. The no-store response contains only the -requested `environment_id` and `status` (`connected` or `disconnected`). Connected -requires the Environment observation, its exact Session and device binding, -current executor authority and a live gateway socket authenticated with that same -credential. A stale observation or a socket carrying a rotated key cannot confirm -connection. - -Invalid, revoked, foreign or deleted-Session authority returns 401; a different -key for an already bound Environment returns 409. Responses do not expose the -actual binding or database diagnostics. - -The installer derives this route from the returned `remote_url` and does not -follow redirects. After starting the daemon it polls once a second for up to 45 seconds. -A 401 or 409 fails at once. On timeout it prints the path of the daemon's -`connect.log` and asks you to rerun the same command; the daemon keeps -reconnecting, and the installation, credential and history stay in place. A -confirmed connection proves authentication only, not model access, Harness -capability or completed execution. +`GET /api/v1/agent-daemon/connection?environment_id=UUID` uses the executor bearer, sent directly to Core (the reverse proxy routes `/api/v1` to Core; the console does not serve it). It is part of the private daemon transport, not the public Agents API. It reads existing authorization and binding only; it never enrolls a device, starts execution or changes resources. The no-store response contains only the requested `environment_id` and `status` (`connected` or `disconnected`). Connected requires the Environment observation, its exact Session and device binding, current executor authority and a live gateway socket authenticated with that same credential. A stale observation or a socket carrying a rotated key cannot confirm connection. + +Invalid, revoked, foreign or deleted-Session authority returns 401; a different key for an already bound Environment returns 409. Responses do not expose the actual binding or database diagnostics. + +The installer derives this route from the returned `remote_url` and does not follow redirects. After starting the daemon it polls once a second for up to 45 seconds. A 401 or 409 fails at once. On timeout it prints the path of the daemon's `connect.log` and asks you to rerun the same command; the daemon keeps reconnecting, and the installation, credential and history stay in place. A confirmed connection proves authentication only, not model access, Harness capability or completed execution. ## Revoked or rotated credential -When Core permanently rejects the daemon (enrollment 401 or 409, a permanent -WebSocket rejection, or a daemon version from another Core distribution), the -daemon prints the reason once and makes no further requests until it is stopped; -it then exits successfully, so a supervisor that restarts on exit does not loop. -When started again, it tries enrollment once and parks again. Transient -failures keep the normal reconnect behavior and never replay execution. - -To reconnect, rotate the same `key_id` (**Rotate**, or **Restore** for a revoked -credential, in the Session's **Executor credentials**), stop the daemon, replace -the credential file at its configured path, and start the daemon again. A new -`key_id` cannot reconnect an Environment that is already bound: issuing it -succeeds, but enrollment with it returns 409. Rotation does not reinstall -Harnesses, change the workspace or replace native history; never create a new -Session history to recover a credential. +When Core permanently rejects the daemon (enrollment 401 or 409, a permanent WebSocket rejection, or a daemon version from another Core distribution), the daemon prints the reason once and makes no further requests until it is stopped; it then exits successfully, so a supervisor that restarts on exit does not loop. When started again, it tries enrollment once and parks again. Transient failures keep the normal reconnect behavior and never replay execution. + +To reconnect, rotate the same `key_id` (**Rotate**, or **Restore** for a revoked credential, in the Session's **Executor credentials**), stop the daemon, replace the credential file at its configured path, and start the daemon again. A new `key_id` cannot reconnect an Environment that is already bound: issuing it succeeds, but enrollment with it returns 409. Rotation does not reinstall Harnesses, change the workspace or replace native history; never create a new Session history to recover a credential. ## Model provider -A `self_hosted` Session carries its own model provider; deployment defaults never -apply. [Model execution](model-execution.md) owns the delivery rules. A saved -Agent's provider key is delivered to the executor of every `self_hosted` Session -created with that Agent in the Project, so anyone who can create `self_hosted` -Sessions in the Project and run an executor can read it. +A `self_hosted` Session carries its own model provider; deployment defaults never apply. [Model execution](model-execution.md) owns the delivery rules. A saved Agent's provider key is delivered to the executor of every `self_hosted` Session created with that Agent in the Project, so anyone who can create `self_hosted` Sessions in the Project and run an executor can read it. diff --git a/deploy/install/README.md b/deploy/install/README.md index f01d2da74..a03dbc01a 100644 --- a/deploy/install/README.md +++ b/deploy/install/README.md @@ -1,17 +1,6 @@ # Installer design rules -This directory holds the Core/Web installer, the `oac` command and the node -installer. The self-hosted daemon installer (`oac-daemon install`) lives in -`apps/parsar-daemon/internal/cli`; its rules are in -[Native daemon installer](#native-daemon-installer). These are the rules to keep -when you change them. Operator usage is in the -[installation guide](../../docs/getting-started/install.md), the -[node guide](../../docs/getting-started/nodes.md) and the -[self-hosted guide](../../docs/getting-started/self-hosted.md); the version policy -is in [Operations](../../docs/getting-started/operations.md#installation-version-policy). -Building and publishing distributions is in the -[maintainer guide](../../docs/maintainers.md). `make check-distribution` runs this -directory's tests. +This directory holds the Core/Web installer, the `oac` command and the node installer. The self-hosted daemon installer (`oac-daemon install`) lives in `apps/parsar-daemon/internal/cli`; its rules are in [Native daemon installer](#native-daemon-installer). These are the rules to keep when you change them. Operator usage is in the [installation guide](../../docs/getting-started/install.md), the [node guide](../../docs/getting-started/nodes.md) and the [self-hosted guide](../../docs/getting-started/self-hosted.md); the version policy is in [Operations](../../docs/getting-started/operations.md#installation-version-policy). Building and publishing distributions is in the [maintainer guide](../../docs/maintainers.md). `make check-distribution` runs this directory's tests. | Module | Role | | --- | --- | @@ -31,307 +20,115 @@ directory's tests. ## Scope -- Installers prepare hosts and services. Core alone owns Session allocation, - initialization, cancellation, snapshots and cleanup. No installer creates an - execution Session or supplies a model credential. -- The Core/Web installer never adds its own host as a node, imports no Runtime - image and gives Core neither the Docker socket nor host devices. Nodes are added - afterwards from Web, this host included. -- Ingress is an installation concern, independent of Runtime and Sandbox Provider - selection. -- A repeated installation or repair never changes provider identity, the backend - namespace or native history. The database, Projects and their keys, provider - identity and the credential encryption key survive repair. +- Installers prepare hosts and services. Core alone owns Session allocation, initialization, cancellation, snapshots and cleanup. No installer creates an execution Session or supplies a model credential. +- The Core/Web installer never adds its own host as a node, imports no Runtime image and gives Core neither the Docker socket nor host devices. Nodes are added afterwards from Web, this host included. +- Ingress is an installation concern, independent of Runtime and Sandbox Provider selection. +- A repeated installation or repair never changes provider identity, the backend namespace or native history. The database, Projects and their keys, provider identity and the credential encryption key survive repair. - Recovery never deletes data and never prunes containers, volumes or images. - Do not add another launcher, scheduler, supervisor or recovery path. ## Configuration and apply -- Every process setting has one home: the installation's private `config.json`, - described by `config.schema.json`. Keep the schema, `config_model.py`, the - generator and the reference tables that `scripts/config-reference.py` renders - into [configuration](../../docs/configuration.md) and - [installation options](../../docs/getting-started/install-options.md) in step. -- Installation flags only seed `config.json`. A rerun of the installer accepts only - `--install-dir` and repairs. -- `oac apply` validates `config.json`, derives `generated/` and converges on what - actually runs. Each service carries the digest of its inputs (Compose label - `io.oac.inputs`, native `OAC_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 an interrupted one. Apply - contacts running services at the last applied address before changing listeners. -- Health checks, setup, apply and generated service files derive addresses from - the same `config.json`. `host` selects the gateway listener for managed ingress - and the Core and Web listeners for external ingress. A non-loopback external bind - requires an HTTPS public origin. -- Runtime settings stay in PostgreSQL and change through Web or `/core/v1`. - Secrets live once each in `secrets/`; identity and installation facts live in - the tool-written `state.json` (format 2, with an `oac-` Compose project). - `config.json` has its own format, 1. -- Core reads only its environment and has no configuration loader; it serves the - non-secret snapshot at `GET /core/v1/installation`. Do not add a second operator - configuration file, loader precedence, hot reload, fallback to earlier setting - names or an embedded Core node. -- Names: Core settings use `OAC_*`, Web settings `OAC_WEB_*`, shared Go logging - `OAC_LOG_*`. A renamed setting fails startup even when empty or when the new name - is also set; report every matching name without its value. Executables are - `oac-core`, `oac-core-migrate`, `oac-core-device`, `oac-core-environment-key`, - `oac-node`, `oac-web`, and the Core-host E2B helper `oac-e2b-provider` under - `/opt/oac/e2b` in the image. The default installation directory is - `~/.oac/core`; generated files carry `x-oac` annotations. +- Every process setting has one home: the installation's private `config.json`, described by `config.schema.json`. Keep the schema, `config_model.py`, the generator and the reference tables that `scripts/config-reference.py` renders into [configuration](../../docs/configuration.md) and [installation options](../../docs/getting-started/install-options.md) in step. +- Installation flags only seed `config.json`. A rerun of the installer accepts only `--install-dir` and repairs. +- `oac apply` validates `config.json`, derives `generated/` and converges on what actually runs. Each service carries the digest of its inputs (Compose label `io.oac.inputs`, native `OAC_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 an interrupted one. Apply contacts running services at the last applied address before changing listeners. +- Health checks, setup, apply and generated service files derive addresses from the same `config.json`. `host` selects the gateway listener for managed ingress and the Core and Web listeners for external ingress. A non-loopback external bind requires an HTTPS public origin. +- Runtime settings stay in PostgreSQL and change through Web or `/core/v1`. Secrets live once each in `secrets/`; identity and installation facts live in the tool-written `state.json` (format 2, with an `oac-` Compose project). `config.json` has its own format, 1. +- Core reads only its environment and has no configuration loader; it serves the non-secret snapshot at `GET /core/v1/installation`. Do not add a second operator configuration file, loader precedence, hot reload, fallback to earlier setting names or an embedded Core node. +- Names: Core settings use `OAC_*`, Web settings `OAC_WEB_*`, shared Go logging `OAC_LOG_*`. A renamed setting fails startup even when empty or when the new name is also set; report every matching name without its value. Executables are `oac-core`, `oac-core-migrate`, `oac-core-device`, `oac-core-environment-key`, `oac-node`, `oac-web`, and the Core-host E2B helper `oac-e2b-provider` under `/opt/oac/e2b` in the image. The default installation directory is `~/.oac/core`; generated files carry `x-oac` annotations. ## Versions and the lock -- Install only into an empty directory, or repair the same source revision. - Refuse older formats and different revisions before changing anything; keep - their data and direct the operator to install separately. Distributions carry - only current installation code: no conversion, migration or binary replacement. - Keep the refusal checks and their tests. -- The packaged `oac.pyz` embeds its build revision and refuses a `state.json` - whose `source_commit` differs. -- The installer and every mutating `oac` command share `.oac.lock`. The installer - holds it across creation, payload, native service and launcher repair, and apply, - calling the already-locked apply implementation without locking again. Never - unlink or replace the lock file, even after an interrupted fresh installation; - its inode must stay stable. +- Install only into an empty directory, or repair the same source revision. Refuse older formats and different revisions before changing anything; keep their data and direct the operator to install separately. Distributions carry only current installation code: no conversion, migration or binary replacement. Keep the refusal checks and their tests. +- The packaged `oac.pyz` embeds its build revision and refuses a `state.json` whose `source_commit` differs. +- The installer and every mutating `oac` command share `.oac.lock`. The installer holds it across creation, payload, native service and launcher repair, and apply, calling the already-locked apply implementation without locking again. Never unlink or replace the lock file, even after an interrupted fresh installation; its inode must stay stable. ## Install-time sandbox selection -`--sandbox docker|microsandbox|e2b|none` (default `microsandbox`; only `none` with -`--web-only`) is a one-time action. After the services are healthy, the installer -posts `/core/v1/sandbox/deployment` once, as Web's setup would, and never on a -repair. The choice is not written to `config.json`; PostgreSQL owns it, and an -existing database selection is never overwritten. - -- Docker and microsandbox use Web's Standard size from - `apps/web/src/features/sandbox/standard-sizes.json`, which the distribution - build copies into the bundle. Keep no other copy of those values. -- `docker` prints its weaker isolation and needs a y/N confirmation or - `--accept-docker-risks` before anything is created. -- `e2b` needs a non-loopback HTTPS `public_url`, `--e2b-api-key-file` and - `--e2b-template`; otherwise the installer refuses before installing anything. -- A Docker or microsandbox selection with a loopback `public_url` is saved, but no - node can serve it until `public_url` is guest-reachable HTTPS. +`--sandbox docker|microsandbox|e2b|none` (default `microsandbox`; only `none` with `--web-only`) is a one-time action. After the services are healthy, the installer posts `/core/v1/sandbox/deployment` once, as Web's setup would, and never on a repair. The choice is not written to `config.json`; PostgreSQL owns it, and an existing database selection is never overwritten. + +- Docker and microsandbox use Web's Standard size from `apps/web/src/features/sandbox/standard-sizes.json`, which the distribution build copies into the bundle. Keep no other copy of those values. +- `docker` prints its weaker isolation and needs a y/N confirmation or `--accept-docker-risks` before anything is created. +- `e2b` needs a non-loopback HTTPS `public_url`, `--e2b-api-key-file` and `--e2b-template`; otherwise the installer refuses before installing anything. +- A Docker or microsandbox selection with a loopback `public_url` is saved, but no node can serve it until `public_url` is guest-reachable HTTPS. - `--sandbox-provider` and `--provider` fail with a message naming `--sandbox`. ## Accounts and permissions -- The Core/Web installer runs as the launching account, root included, in a - writable installation directory. It never invokes sudo, switches accounts or - changes Docker permissions. Check the actual platform, Docker and directory - prerequisites; root alone is no reason to refuse. -- `--native-core` runs Core as a systemd user service of that account, with - lingering, and keeps PostgreSQL and Web in Compose with a private loopback - database port. Native Core needs no KVM or node assets. -- Installation state and secrets are private under `~/.oac/`. No credential enters - build arguments, image layers, browser bundles or diagnostic output. The Compose - file is confidential. -- The distribution build uses umask 022 so non-root service users can read the - payload; installation credentials and state keep their private modes. +- The Core/Web installer runs as the launching account, root included, in a writable installation directory. It never invokes sudo, switches accounts or changes Docker permissions. Check the actual platform, Docker and directory prerequisites; root alone is no reason to refuse. +- `--native-core` runs Core as a systemd user service of that account, with lingering, and keeps PostgreSQL and Web in Compose with a private loopback database port. Native Core needs no KVM or node assets. +- Installation state and secrets are private under `~/.oac/`. No credential enters build arguments, image layers, browser bundles or diagnostic output. The Compose file is confidential. +- The distribution build uses umask 022 so non-root service users can read the payload; installation credentials and state keep their private modes. ## Managed HTTPS -A default combined Docker installation adds two Compose services from one pinned -image: `gateway` runs Caddy, and `installation` runs the packaged -`oac domain-server`. The latter runs with the installing account's UID and its -Docker socket access and calls the same locked apply implementation. Its only -request surface is the private `ingress/api/api.sock`, with Core-key -authentication and one typed domain action. Core and Web get no Docker socket, host -process authority or writable installation configuration. Web gets only the -private API socket directory, never Caddy's admin socket. - -- The gateway owns ports 80 and 443 and the initial Web port. Caddy issues and - renews certificates and keeps its private data in `ingress/data`. - `generated/Caddyfile` is derived from `config.json`, and apply reloads it through - the private Caddy socket even when container inputs already match. -- A domain change keeps the old entry point while it verifies a trusted - certificate and an installation-specific response over HTTPS. Only then does it - set `public_url` and call the common apply path. Failure restores the previous - configuration and reports incomplete recovery; failed retries restore through - the common apply even after a partial change. -- The operation record keeps the last successfully applied public address. Apply - and start update it after gateway verification and service health checks; - generated files alone never prove that a new address is active. A successful - apply reconciles the domain operation status after verifying the running - services. -- The domain operation refuses unrelated pending `config.json` edits and shares - `.oac.lock` with the CLI. Its status file is bookkeeping and the recovery - receipt; `config.json` stays the source of desired settings. An interrupted - operation keeps its desired files and a visible failure and retry state; it - never creates another service project or deletes execution data. -- A Web restart ends console sessions, so the UI gives the new HTTPS sign-in - address instead of treating a dropped request as success. -- Split and native installations use external ingress and report that automatic - Web domain setup is unavailable. +A default combined Docker installation adds two Compose services from one pinned image: `gateway` runs Caddy, and `installation` runs the packaged `oac domain-server`. The latter runs with the installing account's UID and its Docker socket access and calls the same locked apply implementation. Its only request surface is the private `ingress/api/api.sock`, with Core-key authentication and one typed domain action. Core and Web get no Docker socket, host process authority or writable installation configuration. Web gets only the private API socket directory, never Caddy's admin socket. + +- The gateway owns ports 80 and 443 and the initial Web port. Caddy issues and renews certificates and keeps its private data in `ingress/data`. `generated/Caddyfile` is derived from `config.json`, and apply reloads it through the private Caddy socket even when container inputs already match. +- A domain change keeps the old entry point while it verifies a trusted certificate and an installation-specific response over HTTPS. Only then does it set `public_url` and call the common apply path. Failure restores the previous configuration and reports incomplete recovery; failed retries restore through the common apply even after a partial change. +- The operation record keeps the last successfully applied public address. Apply and start update it after gateway verification and service health checks; generated files alone never prove that a new address is active. A successful apply reconciles the domain operation status after verifying the running services. +- The domain operation refuses unrelated pending `config.json` edits and shares `.oac.lock` with the CLI. Its status file is bookkeeping and the recovery receipt; `config.json` stays the source of desired settings. An interrupted operation keeps its desired files and a visible failure and retry state; it never creates another service project or deletes execution data. +- A Web restart ends console sessions, so the UI gives the new HTTPS sign-in address instead of treating a dropped request as success. +- Split and native installations use external ingress and report that automatic Web domain setup is unavailable. ## Output -- Progress describes the operation about to run. Do not imply fresh health checks - on a no-change repair. +- Progress describes the operation about to run. Do not imply fresh health checks on a no-change repair. - Terminal styling is optional: honor `NO_COLOR` and keep redirected logs plain. - Summaries show credential file locations, never their values. -- `install_display.py` owns shared terminal formatting; `install_output.py` and - `node_output.py` own the completion guidance. Ship and checksum the display - modules in both the node bootstrap and its retained helper. -- A node summary reports success only after Core connection and provider readiness - are confirmed. -- Output from the service account stays plain and passes through the - terminal-control sanitizer. +- `install_display.py` owns shared terminal formatting; `install_output.py` and `node_output.py` own the completion guidance. Ship and checksum the display modules in both the node bootstrap and its retained helper. +- A node summary reports success only after Core connection and provider readiness are confirmed. +- Output from the service account stays plain and passes through the terminal-control sanitizer. ## Node installer -The node installer runs as root and prepares the host for one node per -installation. - -- It creates or adopts the `oac-node` system user, adds it to the `docker` or - `kvm` group (no other group), and installs one root-owned system service per - installation that runs the node program as `User=oac-node`. Nodes on a host - share that account, so a host serves one Core. -- Docker group membership makes that user, and so the node, root-equivalent on the - host; that is inherent to Docker sandboxes. microsandbox needs only `kvm`, user - KVM access and the Linux runtime libraries. -- Node configuration and identity live under `~/.oac/nodes//` in - the node account's home (`/var/lib/oac-node`); microsandbox uses a separate short - private Runtime home. -- The node service owns its provider processes outside the Core container. - `KillMode=process` keeps resident microVM and helper processes across a service - restart. The service restarts after failures with no start limit, so a node - outlasts a Core outage, and stops restarting when the node program exits 78 - because Core answered 401 to its credential (a removed node). -- It never installs Docker, KVM or packages and never changes device permissions. - It refuses SELinux-enforcing hosts and changes nothing when a check fails. The - enrollment token comes only on standard input, never in arguments or the - environment. -- Files the service account owns are read, written and deleted only with that - account's credentials, never by root. That work runs in a child that starts its - own session with `/dev/null` as input, joins a new session keyring and dies with - its parent; root shows its output only as plain text (terminal controls become - `?`). SIGINT, SIGHUP and SIGTERM stop that child and what it started. -- Root never runs a file the service account can write, opens a URL it wrote, or - follows a link in its home. Capture the trusted bootstrap bytes before dropping - to the service account and pass them through the fork; the service account - writes its own retained generation helper. Never open the caller's private - download directory to it or let root write into service-owned state. -- The generated bootstrap passes only the six standard HTTP/HTTPS proxy and bypass - variables through sudo and gives both spellings the lowercase value when present, - even if empty, so curl, urllib and the Go registration command follow the same - rules. The installation child keeps just those names beside its fixed - environment. Proxy values stay out of arguments, saved configuration, service - units and diagnostics; never use broad sudo environment inheritance. This covers - installation downloads only, not the node service. -- `--uninstall` removes a node only after Core rejects its credential. It never - touches sandboxes, volumes or images (the Runtime image and the microsandbox - store stay), deletes the account only when the installer created it and no node - remains, and otherwise removes only the groups it added. -- Refuse resources of an older product name for the same installation ID; never - adopt them or remove another installation's resources. +The node installer runs as root and prepares the host for one node per installation. + +- It creates or adopts the `oac-node` system user, adds it to the `docker` or `kvm` group (no other group), and installs one root-owned system service per installation that runs the node program as `User=oac-node`. Nodes on a host share that account, so a host serves one Core. +- Docker group membership makes that user, and so the node, root-equivalent on the host; that is inherent to Docker sandboxes. microsandbox needs only `kvm`, user KVM access and the Linux runtime libraries. +- Node configuration and identity live under `~/.oac/nodes//` in the node account's home (`/var/lib/oac-node`); microsandbox uses a separate short private Runtime home. +- The node service owns its provider processes outside the Core container. `KillMode=process` keeps resident microVM and helper processes across a service restart. The service restarts after failures with no start limit, so a node outlasts a Core outage, and stops restarting when the node program exits 78 because Core answered 401 to its credential (a removed node). +- It never installs Docker, KVM or packages and never changes device permissions. It refuses SELinux-enforcing hosts and changes nothing when a check fails. The enrollment token comes only on standard input, never in arguments or the environment. +- Files the service account owns are read, written and deleted only with that account's credentials, never by root. That work runs in a child that starts its own session with `/dev/null` as input, joins a new session keyring and dies with its parent; root shows its output only as plain text (terminal controls become `?`). SIGINT, SIGHUP and SIGTERM stop that child and what it started. +- Root never runs a file the service account can write, opens a URL it wrote, or follows a link in its home. Capture the trusted bootstrap bytes before dropping to the service account and pass them through the fork; the service account writes its own retained generation helper. Never open the caller's private download directory to it or let root write into service-owned state. +- The generated bootstrap passes only the six standard HTTP/HTTPS proxy and bypass variables through sudo and gives both spellings the lowercase value when present, even if empty, so curl, urllib and the Go registration command follow the same rules. The installation child keeps just those names beside its fixed environment. Proxy values stay out of arguments, saved configuration, service units and diagnostics; never use broad sudo environment inheritance. This covers installation downloads only, not the node service. +- `--uninstall` removes a node only after Core rejects its credential. It never touches sandboxes, volumes or images (the Runtime image and the microsandbox store stay), deletes the account only when the installer created it and no node remains, and otherwise removes only the groups it added. +- Refuse resources of an older product name for the same installation ID; never adopt them or remove another installation's resources. ## Download contract -The distribution manifest is the one download contract for the Core, node and -self-hosted installers: flat versioned file names, and the compressed and -unpacked size and SHA-256 of the Runtime. - -- The default installation downloads the Core, Web and PostgreSQL payloads, never - the Runtime image or node execution artifacts. Core's image never acquires - execution-only payloads. The offline archive stays an explicit option. -- A node obtains bootstrap metadata from the console that generated its command, - or from a local offline bundle. Web serves artifacts it has locally and - redirects missing declared execution artifacts to the versioned HTTPS release - base in the verified manifest. Web never downloads or caches those bytes. -- Only artifact requests may follow HTTPS redirects, and only without credentials - or cookies. Metadata and enrollment requests stay on the configured console. - The console publishes only fixed non-secret files and declared artifact names. -- Download into private temporary files, verify size and SHA-256 before an - atomic rename, resume interrupted transfers, and reuse only verified cache - entries or exact image identities. Never select a release other than the - pinned one. -- Python zipapps bundle the shared resolver with each remote bootstrap. The node - asset includes the `oac-node` binary. -- Release downloads are anonymous. Never add repository credentials to installed - node or Runtime configuration. -- Manual builds use the `build-` release tag and tag builds the `v*` tag. - The manifest's download base must match the release tag; artifact file names and - source provenance keep the full source SHA. +The distribution manifest is the one download contract for the Core, node and self-hosted installers: flat versioned file names, and the compressed and unpacked size and SHA-256 of the Runtime. + +- The default installation downloads the Core, Web and PostgreSQL payloads, never the Runtime image or node execution artifacts. Core's image never acquires execution-only payloads. The offline archive stays an explicit option. +- A node obtains bootstrap metadata from the console that generated its command, or from a local offline bundle. Web serves artifacts it has locally and redirects missing declared execution artifacts to the versioned HTTPS release base in the verified manifest. Web never downloads or caches those bytes. +- Only artifact requests may follow HTTPS redirects, and only without credentials or cookies. Metadata and enrollment requests stay on the configured console. The console publishes only fixed non-secret files and declared artifact names. +- Download into private temporary files, verify size and SHA-256 before an atomic rename, resume interrupted transfers, and reuse only verified cache entries or exact image identities. Never select a release other than the pinned one. +- Python zipapps bundle the shared resolver with each remote bootstrap. The node asset includes the `oac-node` binary. +- Release downloads are anonymous. Never add repository credentials to installed node or Runtime configuration. +- Manual builds use the `build-` release tag and tag builds the `v*` tag. The manifest's download base must match the release tag; artifact file names and source provenance keep the full source SHA. ## Image identity -The manifest's `images` records each exported image's config digest, and -`image_manifest_digests` its OCI manifest or index digest. Derive and verify both -from the same archive, including its referenced config and layer bytes, and -require the build host's selected image ID to match one of them. - -Docker's classic image store identifies images by config digest, and its -containerd store by the OCI descriptor. The build therefore takes the digest the -local store resolves from BuildKit's build metadata, never the `--iidfile` config -digest alone, and disables provenance attestations so each image and archive holds -one platform manifest in both stores. For the same reason the default PostgreSQL -image is pinned by its linux/amd64 platform manifest digest: a pulled -multi-platform tag keeps its whole index in the containerd store, and its export -holds every platform. - -The Core, node and self-hosted installers share one resolver for these identities. -It confirms Linux amd64 and the returned immutable local ID, and service and -provider configuration and Runtime launches use that ID. Tags never replace -identity verification. The microsandbox `runtime_ref` is independent of Docker's -local store identity. +The manifest's `images` records each exported image's config digest, and `image_manifest_digests` its OCI manifest or index digest. Derive and verify both from the same archive, including its referenced config and layer bytes, and require the build host's selected image ID to match one of them. + +Docker's classic image store identifies images by config digest, and its containerd store by the OCI descriptor. The build therefore takes the digest the local store resolves from BuildKit's build metadata, never the `--iidfile` config digest alone, and disables provenance attestations so each image and archive holds one platform manifest in both stores. For the same reason the default PostgreSQL image is pinned by its linux/amd64 platform manifest digest: a pulled multi-platform tag keeps its whole index in the containerd store, and its export holds every platform. + +The Core, node and self-hosted installers share one resolver for these identities. It confirms Linux amd64 and the returned immutable local ID, and service and provider configuration and Runtime launches use that ID. Tags never replace identity verification. The microsandbox `runtime_ref` is independent of Docker's local store identity. ## Native daemon installer -`oac-daemon install` installs the daemon and selected Harnesses on a self-hosted -Linux, macOS or Windows machine; the -[credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) -covers the grant it claims. - -- Interactive selection and CLI-only installation share one options and - validation path. There is no installation-options file. The saved installation - state and explicitly supplied credential and tool-variable files serve runtime - operation, not a second configuration language. -- Each release bundles pinned Node.js and npm, the native Harnesses and their - adapter assets. Registration lives in the CLI, and native activation and - readiness in each adapter's optional `agent.Installation` descriptor. Core never - selects native paths or OS-specific steps. -- Bootstrap scripts only download and extract the current platform's archive, - after verifying the checksum Core provides. Installation, startup, connection - verification and execution stay common. Native bundles must match Core's source - revision and Runtime wire version. -- Neither Core installation nor repair downloads native payloads. Core serves the - local offline archives or redirects to the catalog URL without proxying or - caching; it verifies local archives before serving, and a corrupt local archive - fails closed. -- Every mutation holds the installation directory lock. Publish complete, - checksum-verified components from staging, then commit the configuration after - native readiness passes. A rerun with the same connection settings adds the - selected Harnesses and validates existing contents. Never overwrite, upgrade, - repair or migrate installed components; missing, modified, wrong-platform or - incompatible content is an explicit error. A partial addition keeps the old - configuration and reusable complete components and removes nothing. -- Serialize background PID inspection and publication so concurrent starts cannot - create two daemons. An installed daemon registers only the adapter kinds its - verified installation manifest names; other Harness executables on `PATH` - cannot extend it. Direct `connect` refuses an installed Runtime and points to - `start`. -- The installer runs as the current user in writable directories and never - elevates. Subprocess diagnostics never expose sensitive parameters or - environment values. Readiness checks take the installer's cancellation context - and reap their processes before returning. -- Report installation, authenticated connection and model configuration as - separate results. Starting execution never downloads or installs Harnesses. - Stop and reconnect keep capability snapshots and native Session state. -- `scripts/build-native-installer.mjs` validates pins and startup, hashes every - component file, accepts only contained regular files and rejects escaping links. - For the native bundle, Claude's frozen `pnpm deploy` export is reinstalled with - the hoisted linker before contained links are flattened; the Runtime image's - Claude archive is unchanged. The `native-check` workflow builds and tests - installation, addition and reuse, missing arguments and the unsupported Windows - MiniMax case on Linux, macOS and Windows. +`oac-daemon install` installs the daemon and selected Harnesses on a self-hosted Linux, macOS or Windows machine; the [credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) covers the grant it claims. + +- Interactive selection and CLI-only installation share one options and validation path. There is no installation-options file. The saved installation state and explicitly supplied credential and tool-variable files serve runtime operation, not a second configuration language. +- Each release bundles pinned Node.js and npm, the native Harnesses and their adapter assets. Registration lives in the CLI, and native activation and readiness in each adapter's optional `agent.Installation` descriptor. Core never selects native paths or OS-specific steps. +- Bootstrap scripts only download and extract the current platform's archive, after verifying the checksum Core provides. Installation, startup, connection verification and execution stay common. Native bundles must match Core's source revision and Runtime wire version. +- Neither Core installation nor repair downloads native payloads. Core serves the local offline archives or redirects to the catalog URL without proxying or caching; it verifies local archives before serving, and a corrupt local archive fails closed. +- Every mutation holds the installation directory lock. Publish complete, checksum-verified components from staging, then commit the configuration after native readiness passes. A rerun with the same connection settings adds the selected Harnesses and validates existing contents. Never overwrite, upgrade, repair or migrate installed components; missing, modified, wrong-platform or incompatible content is an explicit error. A partial addition keeps the old configuration and reusable complete components and removes nothing. +- Serialize background PID inspection and publication so concurrent starts cannot create two daemons. An installed daemon registers only the adapter kinds its verified installation manifest names; other Harness executables on `PATH` cannot extend it. Direct `connect` refuses an installed Runtime and points to `start`. +- The installer runs as the current user in writable directories and never elevates. Subprocess diagnostics never expose sensitive parameters or environment values. Readiness checks take the installer's cancellation context and reap their processes before returning. +- Report installation, authenticated connection and model configuration as separate results. Starting execution never downloads or installs Harnesses. Stop and reconnect keep capability snapshots and native Session state. +- `scripts/build-native-installer.mjs` validates pins and startup, hashes every component file, accepts only contained regular files and rejects escaping links. For the native bundle, Claude's frozen `pnpm deploy` export is reinstalled with the hoisted linker before contained links are flattened; the Runtime image's Claude archive is unchanged. The `native-check` workflow builds and tests installation, addition and reuse, missing arguments and the unsupported Windows MiniMax case on Linux, macOS and Windows. ## Validation -`make check-distribution` covers the production proxy, the installation rules, -release metadata and native catalog assembly, including bundle manifests larger -than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). A real -bundle check covers default and provider selection, component modes, connecting to -an existing Web, public native execution and restart retention. Diagnostics report observed service health, never -fabricated model or environment readiness. Runtime observations belong to Core; do -not add monitoring or lifecycle tracking to the installer or the landing site. +`make check-distribution` covers the production proxy, the installation rules, release metadata and native catalog assembly, including bundle manifests larger than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). A real bundle check covers default and provider selection, component modes, connecting to an existing Web, public native execution and restart retention. Diagnostics report observed service health, never fabricated model or environment readiness. Runtime observations belong to Core; do not add monitoring or lifecycle tracking to the installer or the landing site. diff --git a/docs/getting-started/self-hosted.md b/docs/getting-started/self-hosted.md index 085749597..8a943115a 100644 --- a/docs/getting-started/self-hosted.md +++ b/docs/getting-started/self-hosted.md @@ -1,21 +1,10 @@ # Self-hosted executors -A `self_hosted` Session runs on a machine your application owns: a workstation, a -VM or a sandbox you manage. The application creates the Session through `/v1` and -receives a command that installs `oac-daemon`, starts it and connects it to Core. -Web shows the same command on the Session's page; it is optional. Core never -creates, stops or reclaims the machine. - -**The daemon is not a sandbox.** Tools run with the permissions of the account -that starts it and can reach whatever that account can. Use a container or VM when -you need isolation; see -[Runtime and outer isolation](../design-principles.md#runtime-and-outer-isolation). -The daemon does not restrict network access, so a Template that requires a -network policy is rejected for a self-hosted Session. - -The Session brings its own model provider; the installation default never applies -([why](../user-guide.md#which-model-provider-a-session-uses)). The machine gets an -executor credential that works for this one Environment and nothing else. +A `self_hosted` Session runs on a machine your application owns: a workstation, a VM or a sandbox you manage. The application creates the Session through `/v1` and receives a command that installs `oac-daemon`, starts it and connects it to Core. Web shows the same command on the Session's page; it is optional. Core never creates, stops or reclaims the machine. + +**The daemon is not a sandbox.** Tools run with the permissions of the account that starts it and can reach whatever that account can. Use a container or VM when you need isolation; see [Runtime and outer isolation](../design-principles.md#runtime-and-outer-isolation). The daemon does not restrict network access, so a Template that requires a network policy is rejected for a self-hosted Session. + +The Session brings its own model provider; the installation default never applies ([why](../user-guide.md#which-model-provider-a-session-uses)). The machine gets an executor credential that works for this one Environment and nothing else. ## Platforms @@ -25,27 +14,20 @@ executor credential that works for this one Environment and nothing else. | macOS arm64 | Supported | Supported | Supported | | Windows amd64 | Supported | Supported | Not supported | -The installer brings its own pinned Node.js and Harness versions (listed in -[`scripts/build-native-installer.mjs`](../../scripts/build-native-installer.mjs)) -and leaves other installations of those tools untouched. On a platform without a -matching installer, the command fails. +The installer brings its own pinned Node.js and Harness versions (listed in [`scripts/build-native-installer.mjs`](../../scripts/build-native-installer.mjs)) and leaves other installations of those tools untouched. On a platform without a matching installer, the command fails. The machine needs: -- HTTPS access to Core (plain HTTP only on loopback), and to the release download - host unless Core carries an offline copy of the installers; -- Bash for environment setup and MiniMax Code tools; on Windows, Git Bash, which - Claude Code also requires; +- HTTPS access to Core (plain HTTP only on loopback), and to the release download host unless Core carries an offline copy of the installers; +- Bash for environment setup and MiniMax Code tools; on Windows, Git Bash, which Claude Code also requires; - Python and pip when the Session's packages need them; -- any system packages your setup needs. The daemon never runs apt, sudo or another - elevation command, so install them through the host's normal administration. +- any system packages your setup needs. The daemon never runs apt, sudo or another elevation command, so install them through the host's normal administration. No administrator privileges or Docker are needed. ## Connect a machine -1. Choose an absolute workspace path on the target machine. Create a Session with - that path and the application's Project API key: +1. Choose an absolute workspace path on the target machine. Create a Session with that path and the application's Project API key: ```python import os @@ -70,20 +52,12 @@ No administrator privileges or Docker are needed. print(installation["commands"]["posix"]) # use "powershell" on Windows ``` -2. Run the command on the target machine with the account that should run the - tools. It downloads the installer matched to this Core, verifies its checksum, - asks which Harnesses to install and where, installs them, creates the workspace - if needed, starts the daemon and checks its connection. -3. Send a Turn. A connected machine proves only authentication; the first Turn - checks the Harness and the model. +2. Run the command on the target machine with the account that should run the tools. It downloads the installer matched to this Core, verifies its checksum, asks which Harnesses to install and where, installs them, creates the workspace if needed, starts the daemon and checks its connection. +3. Send a Turn. A connected machine proves only authentication; the first Turn checks the Harness and the model. In Web, open the Session and copy the command under **Connect a host**. -The command expires after 30 minutes. Read the Session again, or reload its page -in Web, for a fresh one. Treat the command as a temporary secret: it can claim the -machine's credential but cannot run work or read files. The -[credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) -describes what invalidates it. +The command expires after 30 minutes. Read the Session again, or reload its page in Web, for a fresh one. Treat the command as a temporary secret: it can claim the machine's credential but cannot run work or read files. The [credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) describes what invalidates it. The installer reports three results: @@ -93,11 +67,7 @@ The installer reports three results: | **Daemon connection** | Core confirmed the daemon's authenticated connection | | **Model configuration** | Not checked; the first Turn uses the Session's model provider | -If the connection is not confirmed within 45 seconds, the installer prints the -path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the -same command again with the same installation directory: completed components and -the credential are kept and a running daemon is reused. Do not remove the -workspace or the Session to retry. +If the connection is not confirmed within 45 seconds, the installer prints the path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the same command again with the same installation directory: completed components and the credential are kept and a running daemon is reused. Do not remove the workspace or the Session to retry. ### Options for automation @@ -111,8 +81,7 @@ Append these to the command: | `--capability-directory ABS` | Where [capability snapshots](#local-capability-directories) are stored. Default: `capabilities` in the installation directory | | `--tool-env-file ABS` | A JSON file of string variables for tools and MCP servers; see [explicit local tool environment](../../contracts/agents-api/environments.md#explicit-local-tool-environment) | -The workspace is fixed when the Session is created. For a different workspace, -create another Session. +The workspace is fixed when the Session is created. For a different workspace, create another Session. ## Local capability directories @@ -126,13 +95,9 @@ environment = { } ``` -Paths are absolute in the machine's own syntax (Unix, Windows drive or UNC); the -daemon checks them, not Core. Fill these directories before the daemon connects. -They are ordinary paths visible to the daemon; naming one does not mount it or -create a sandbox. +Paths are absolute in the machine's own syntax (Unix, Windows drive or UNC); the daemon checks them, not Core. Fill these directories before the daemon connects. They are ordinary paths visible to the daemon; naming one does not mount it or create a sandbox. -Use `x_agents_core.environment` for the same Project-owned Skills, Plugin archives, -files, packages, setup commands or Template used by a managed Session: +Use `x_agents_core.environment` for the same Project-owned Skills, Plugin archives, files, packages, setup commands or Template used by a managed Session: ```python session = client.beta.agents.sessions.create( @@ -145,16 +110,9 @@ session = client.beta.agents.sessions.create( ) ``` -The same extension works with `environment={"type": "openai_hosted"}`. Do not -repeat a field in both `environment` and the extension. Setup runs with the -daemon's account permissions. Deployment model keys are never sent to your -machine. +The same extension works with `environment={"type": "openai_hosted"}`. Do not repeat a field in both `environment` and the extension. Setup runs with the daemon's account permissions. Deployment model keys are never sent to your machine. -Before the first Turn the daemon copies these sources into a snapshot. Reconnecting -reuses the snapshot even after you edit the sources; a new Session takes a new -snapshot. The -[preparation contract](../../contracts/agents-api/environments.md#runtime-capability-preparation) -lists fields, merge rules, snapshot behavior and failures. +Before the first Turn the daemon copies these sources into a snapshot. Reconnecting reuses the snapshot even after you edit the sources; a new Session takes a new snapshot. The [preparation contract](../../contracts/agents-api/environments.md#runtime-capability-preparation) lists fields, merge rules, snapshot behavior and failures. ## Operate the installation @@ -167,19 +125,11 @@ The installation's `bin/oac-daemon` finds its own installation. Use it for: | `oac-daemon logs -n 100`, `oac-daemon logs -f` | Print or follow the daemon log | | `oac-daemon stop` | Stop the daemon | -If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the -connection under **Host connection** on the Session's page in Web, or with the -[connection status](../../contracts/agents-api/environment-executor-credentials.md#connection-status). +If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the connection under **Host connection** on the Session's page in Web, or with the [connection status](../../contracts/agents-api/environment-executor-credentials.md#connection-status). -To add a Harness, run the original install command again with the same connection -options and the Harness to add. The installer checks the existing contents, adds -only missing components and keeps the Harnesses already installed. Restart a -running daemon afterwards so it discovers the new Harness. +To add a Harness, run the original install command again with the same connection options and the Harness to add. The installer checks the existing contents, adds only missing components and keeps the Harnesses already installed. Restart a running daemon afterwards so it discovers the new Harness. -Stopping the daemon, cancelling a Turn or deleting the Session never removes the -machine's workspace, native history or capability snapshot. An installation from -another daemon version, or one whose files were changed, is refused. The installer -never upgrades, repairs or migrates it; install into a separate directory. +Stopping the daemon, cancelling a Turn or deleting the Session never removes the machine's workspace, native history or capability snapshot. An installation from another daemon version, or one whose files were changed, is refused. The installer never upgrades, repairs or migrates it; install into a separate directory. ## Rotate or revoke @@ -190,20 +140,13 @@ In Web, the Session's **Executor credentials** list the machine's credential: | **Rotate** | The credential gets a new secret; the old secret stops working at once. On a revoked credential the action is **Restore** | | **Revoke** | The credential stops working at once | -To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the -JSON at its configured credential-file path with the new credential, and run -`oac-daemon start`. Do not run `install` again over the existing installation, and -do not issue a second credential: the Environment stays bound to the credential it -first connected with. +To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the JSON at its configured credential-file path with the new credential, and run `oac-daemon start`. Do not run `install` again over the existing installation, and do not issue a second credential: the Environment stays bound to the credential it first connected with. -In an archived Project, credentials cannot be issued or rotated; revocation remains -available. Operators can manage credentials with the Core key; see the -[credential contract](../../contracts/agents-api/environment-executor-credentials.md#core-key-routes). +In an archived Project, credentials cannot be issued or rotated; revocation remains available. Operators can manage credentials with the Core key; see the [credential contract](../../contracts/agents-api/environment-executor-credentials.md#core-key-routes). ## Install from an extracted distribution -The same installer accepts an already extracted distribution and a credential -file issued by an operator, without the install command: +The same installer accepts an already extracted distribution and a credential file issued by an operator, without the install command: ```sh ./oac-daemon install --non-interactive --harness codex \ @@ -215,6 +158,4 @@ file issued by an operator, without the install command: "$HOME/.oac/my-runtime/bin/oac-daemon" start ``` -Use the Session's `remote_url` and Environment ID. In PowerShell, run -`.\oac-daemon.exe` with native absolute paths. This mode needs an existing -workspace and does not start the daemon until you run `start`. +Use the Session's `remote_url` and Environment ID. In PowerShell, run `.\oac-daemon.exe` with native absolute paths. This mode needs an existing workspace and does not start the daemon until you run `start`. diff --git a/docs/maintainers.md b/docs/maintainers.md index ce8c05e64..8305f6a03 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -1,21 +1,12 @@ # Build and release OpenAgentCore -This guide is for maintainers who build and publish OpenAgentCore. To install Core -and Web, use the [installation guide](getting-started/install.md). The rules the -installer code follows are in [Installer design rules](../deploy/install/README.md); -required checks are in [CONTRIBUTING](../CONTRIBUTING.md#required-checks). +This guide is for maintainers who build and publish OpenAgentCore. To install Core and Web, use the [installation guide](getting-started/install.md). The rules the installer code follows are in [Installer design rules](../deploy/install/README.md); required checks are in [CONTRIBUTING](../CONTRIBUTING.md#required-checks). ## Build a distribution -A distribution is the matched set of Linux amd64 release assets built from one -commit: the control archive (the installer, the `oac` command, and the Core, Web, -gateway and PostgreSQL images), the Runtime image and node artifacts as separate -files, and the native installers. +A distribution is the matched set of Linux amd64 release assets built from one commit: the control archive (the installer, the `oac` command, and the Core, Web, gateway and PostgreSQL images), the Runtime image and node artifacts as separate files, and the native installers. -Build on Linux x86_64 with a glibc compatible with Debian 12, Docker, the Go -version in `go.mod`, Node, pnpm, Python 3.9 or newer, curl and tar. The source -must be clean and committed. First prepare the pinned Codex package and MiniMax -Code companion, then build: +Build on Linux x86_64 with a glibc compatible with Debian 12, Docker, the Go version in `go.mod`, Node, pnpm, Python 3.9 or newer, curl and tar. The source must be clean and committed. First prepare the pinned Codex package and MiniMax Code companion, then build: ```sh bash scripts/prepare-release-runtimes.sh @@ -26,8 +17,7 @@ export CORE_DISTRIBUTION_RELEASE_BASE_URL=https://github.com/MiniMax-AI/parsar-c make build-core-distribution ``` -`prepare-release-runtimes.sh` refuses an existing `~/.oac/build/release-inputs`; -use a fresh build host or directory. +`prepare-release-runtimes.sh` refuses an existing `~/.oac/build/release-inputs`; use a fresh build host or directory. | Variable | Effect | | --- | --- | @@ -41,57 +31,28 @@ use a fresh build host or directory. | `CORE_DISTRIBUTION_MICROSANDBOX_ARCHIVE` | Cached microsandbox release archive. Default: `~/.oac/cache/microsandbox-v0.7.2-linux-x86_64.tar.gz`, downloaded when missing | | `CORE_DISTRIBUTION_DATABASE_IMAGE` | PostgreSQL 16 image; the default is pinned by its linux/amd64 manifest digest | -The build reuses the Core, Web, Runtime, SDK and helper builders. The manifest -records the commit and source tree, image config and OCI manifest digests, the -Runtime OCI manifest digest, the microsandbox runtime and firmware hashes, and the -size and hash of every downloadable artifact. Output is the control archive and its -`.sha256`, the optional offline archive, and the versioned Runtime, node and native -installer assets. Nothing is published. Rebuilding into a directory that already -holds this commit's distribution is refused. - -The control archive carries no Runtime image or node execution artifacts. Nodes -fetch them from the Web that generated their command, which serves a local copy or -redirects to the release base; the offline archive carries them instead. The -[download contract](../deploy/install/README.md#download-contract) owns these rules. - -A distribution carries the docs listed in `BUNDLED_DOCS` in -`scripts/core-distribution-manifest.py`. Links between bundled docs stay relative; -every other relative link is rewritten to the same file on GitHub at the bundle's -commit. The build fails when a link or anchor does not resolve, and -`make check-distribution` runs the same check on the repository. Update the list -when you add or move a doc that the installer or its output refers to. +The build reuses the Core, Web, Runtime, SDK and helper builders. The manifest records the commit and source tree, image config and OCI manifest digests, the Runtime OCI manifest digest, the microsandbox runtime and firmware hashes, and the size and hash of every downloadable artifact. Output is the control archive and its `.sha256`, the optional offline archive, and the versioned Runtime, node and native installer assets. Nothing is published. Rebuilding into a directory that already holds this commit's distribution is refused. + +The control archive carries no Runtime image or node execution artifacts. Nodes fetch them from the Web that generated their command, which serves a local copy or redirects to the release base; the offline archive carries them instead. The [download contract](../deploy/install/README.md#download-contract) owns these rules. + +A distribution carries the docs listed in `BUNDLED_DOCS` in `scripts/core-distribution-manifest.py`. Links between bundled docs stay relative; every other relative link is rewritten to the same file on GitHub at the bundle's commit. The build fails when a link or anchor does not resolve, and `make check-distribution` runs the same check on the repository. Update the list when you add or move a doc that the installer or its output refers to. ### Native installers -Self-hosted machines install `oac-daemon` from per-platform native installers: -Linux amd64, macOS arm64 and Windows amd64. Each is built on its own OS by the -`native-check` workflow (`scripts/build-native-installer.mjs`, whose `pins` object -fixes the Node.js and Harness versions) and uploaded as -`oac-native-installer--.tar.gz`. For a local distribution, download the -three artifacts from the native run for the same commit, then assemble the catalog -from that checkout: +Self-hosted machines install `oac-daemon` from per-platform native installers: Linux amd64, macOS arm64 and Windows amd64. Each is built on its own OS by the `native-check` workflow (`scripts/build-native-installer.mjs`, whose `pins` object fixes the Node.js and Harness versions) and uploaded as `oac-native-installer--.tar.gz`. For a local distribution, download the three artifacts from the native run for the same commit, then assemble the catalog from that checkout: ```sh node scripts/build-native-catalog.mjs INPUT_DIR OUTPUT_DIR export OAC_NATIVE_INSTALLER_BUILD_DIR=OUTPUT_DIR ``` -The catalog records the commit, the Runtime protocol version and each archive's -checksum. The control archive and Core image carry only `native-installers/catalog.json`; -the archives become separate `oac-native--.tar.gz` release -assets, and the offline archive holds one copy of each outside the Core image. -Without a catalog, Sessions report the install command as unavailable, and the -release workflow refuses to publish. +The catalog records the commit, the Runtime protocol version and each archive's checksum. The control archive and Core image carry only `native-installers/catalog.json`; the archives become separate `oac-native--.tar.gz` release assets, and the offline archive holds one copy of each outside the Core image. Without a catalog, Sessions report the install command as unavailable, and the release workflow refuses to publish. ### Runtime images and helpers -`make build-core-distribution` builds all of these. Build one on its own to test a -Harness image or a helper. Run every command from the repository root; default -outputs go under `${OAC_DEV_HOME:-$HOME/.oac}/build`. +`make build-core-distribution` builds all of these. Build one on its own to test a Harness image or a helper. Run every command from the repository root; default outputs go under `${OAC_DEV_HOME:-$HOME/.oac}/build`. -**Codex Runtime image.** Extract the official npm package -`@openai/codex@0.153.4-linux-x64` under `~/.oac` (for example with -`npm pack --ignore-scripts` and `tar -xzf`), then: +**Codex Runtime image.** Extract the official npm package `@openai/codex@0.153.4-linux-x64` under `~/.oac` (for example with `npm pack --ignore-scripts` and `tar -xzf`), then: ```sh export AGENTS_RUNTIME_CODEX_PACKAGE=/absolute/path/to/package @@ -99,9 +60,7 @@ make build-agents-runtime docker build --platform linux/amd64 -t oac-runtime:codex "${OAC_DEV_HOME:-$HOME/.oac}/build/agents-runtime" ``` -The script checks the package version, builds `oac-daemon` for Linux amd64 and -prepares a context with only the daemon, the unmodified native executable, its -resources and `services/agents-api/deploy/codex/Dockerfile`. +The script checks the package version, builds `oac-daemon` for Linux amd64 and prepares a context with only the daemon, the unmodified native executable, its resources and `services/agents-api/deploy/codex/Dockerfile`. **Claude Code Runtime image.** Node 20 or newer and pnpm are required. @@ -111,14 +70,9 @@ make build-claude-runtime docker build --platform linux/amd64 -t oac-runtime:claude "${OAC_DEV_HOME:-$HOME/.oac}/build/claude-runtime" ``` -The first step exports the adapter with the pinned Claude Agent SDK -(`packages/claude-sdk-adapter/package.json`) as a checksummed archive; the second -verifies it and adds the daemon. Keep the exported archive unchanged. +The first step exports the adapter with the pinned Claude Agent SDK (`packages/claude-sdk-adapter/package.json`) as a checksummed archive; the second verifies it and adds the daemon. Keep the exported archive unchanged. -**MiniMax Code Runtime image.** Build the companion from a checkout of the revision -pinned in `packages/mcode-harness/source.json`, with the `@minimax-ai/code` npm -package of the same version for native dependencies. It builds on Linux x86_64 or -macOS arm64 into a new directory: +**MiniMax Code Runtime image.** Build the companion from a checkout of the revision pinned in `packages/mcode-harness/source.json`, with the `@minimax-ai/code` npm package of the same version for native dependencies. It builds on Linux x86_64 or macOS arm64 into a new directory: ```sh MCODE_NATIVE_SOURCE=/absolute/minimax-code \ @@ -130,10 +84,7 @@ docker build --platform linux/amd64 -t oac-runtime:mcode "${OAC_DEV_HOME:-$HOME/ `scripts/prepare-release-runtimes.sh` runs the companion build from the pins. -The distribution combines the three Harness images into one Runtime image -(`deploy/distribution/Runtime.Dockerfile`) that contains the daemon, the shared -helpers and the three native Harness packages. It verifies that each image -carries the daemon built from the same commit. +The distribution combines the three Harness images into one Runtime image (`deploy/distribution/Runtime.Dockerfile`) that contains the daemon, the shared helpers and the three native Harness packages. It verifies that each image carries the daemon built from the same commit. **E2B helper.** @@ -141,15 +92,7 @@ carries the daemon built from the same commit. make build-e2b-provider ``` -Docker builds the Linux amd64 helper with the pinned CPython and Debian 12 image. -The Python dependency closure, including PyInstaller, is hash-locked in -`services/agents-api/tools/e2b-provider/requirements.lock`; no E2B account key is -needed. Set `E2B_PROVIDER_BUILD_DIR` for another output directory and -`E2B_SOURCE_REVISION` when building from an exported source tree. The output is -`oac-e2b-provider-linux-amd64.tar.gz` with its `.sha256`; it extracts to -`oac-e2b-provider/` with the executable, `_internal/`, `licenses/`, -`requirements.lock` and `manifest.json`. The Core image and native Core use the -same tree; the host needs a compatible glibc and CA certificates, not Python. +Docker builds the Linux amd64 helper with the pinned CPython and Debian 12 image. The Python dependency closure, including PyInstaller, is hash-locked in `services/agents-api/tools/e2b-provider/requirements.lock`; no E2B account key is needed. Set `E2B_PROVIDER_BUILD_DIR` for another output directory and `E2B_SOURCE_REVISION` when building from an exported source tree. The output is `oac-e2b-provider-linux-amd64.tar.gz` with its `.sha256`; it extracts to `oac-e2b-provider/` with the executable, `_internal/`, `licenses/`, `requirements.lock` and `manifest.json`. The Core image and native Core use the same tree; the host needs a compatible glibc and CA certificates, not Python. **microsandbox helper.** Linux only, with a C compiler: @@ -158,51 +101,17 @@ make build-microsandbox-provider make check-microsandbox-provider ``` -The helper is written to -`~/.oac/build/microsandbox-provider/oac-microsandbox-provider`. Its separate Go -module pins the microsandbox Go SDK v0.7.2 and embeds the matching FFI library; -never build production with the SDK's `microsandbox_ffi_path` tag. Core itself -stays a CGO-disabled build. The helper needs glibc and runs only on nodes. - -**microsandbox runtime.** The distribution uses the official -[v0.7.2 release](https://github.com/superradcompany/microsandbox/releases/tag/v0.7.2) -archive `microsandbox-linux-x86_64.tar.gz`, SHA256 -`47c223e3ef5298abf05f47ed9f87981106e400d99bb3f1d042d4d6881346b18b` -(`RUNTIME_ARCHIVE_SHA256` in `scripts/core-distribution-manifest.py`). The build -verifies the checksum before extracting `msb` and `libkrunfw.so.5.6.1` and records -both files' hashes. The helper checks those hashes on every call and never -installs or upgrades them. +The helper is written to `~/.oac/build/microsandbox-provider/oac-microsandbox-provider`. Its separate Go module pins the microsandbox Go SDK v0.7.2 and embeds the matching FFI library; never build production with the SDK's `microsandbox_ffi_path` tag. Core itself stays a CGO-disabled build. The helper needs glibc and runs only on nodes. + +**microsandbox runtime.** The distribution uses the official [v0.7.2 release](https://github.com/superradcompany/microsandbox/releases/tag/v0.7.2) archive `microsandbox-linux-x86_64.tar.gz`, SHA256 `47c223e3ef5298abf05f47ed9f87981106e400d99bb3f1d042d4d6881346b18b` (`RUNTIME_ARCHIVE_SHA256` in `scripts/core-distribution-manifest.py`). The build verifies the checksum before extracting `msb` and `libkrunfw.so.5.6.1` and records both files' hashes. The helper checks those hashes on every call and never installs or upgrades them. ### Standalone Core builds -`make build-agents-api` builds `oac-core`, `oac-core-migrate`, `oac-core-device`, -`oac-core-environment-key` and `oac-node` into -`${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core` (`OAC_DEV_CORE_BUILD_DIR` selects -another absolute directory). The build copies only the source set listed in -`scripts/build-agents-api.sh` (the Core service, its contracts, the shared -packages it needs and the root Go module files) into a temporary context and -builds with CGO disabled, read-only modules and trimmed paths. It needs no Node, -Docker or other application. When Core gains a shared dependency, add that package -to the list; never copy the whole repository to make it compile. - -`make docker-build-agents-api` builds the image `oac-core:dev` (`OAC_DEV_CORE_IMAGE` -selects another name) from those five commands and the E2B helper. The base is the -digest-pinned `debian:bookworm-slim` with CA certificates and the glibc runtime the -helper needs; the default user is UID/GID 65532 and Core listens on `:8091`. The -image is Linux amd64 only and is not pushed to a registry. Changes to the image or -its build need `make check-agents-api-container` in addition to `make check`: it -runs the official-client suite against the image with a read-only root filesystem -and needs Linux Docker, a non-root user, `OAC_TEST_OFFICIAL_SDK_PYTHON` and a -dedicated `OAC_TEST_DATABASE_URL`. - -`make build-agents-api-release` packages the same five commands into -`oac-core--linux-amd64.tar.gz` and its `.sha256` under -`~/.oac/build/oac-core-release` (`OAC_DEV_RELEASE_DIR`). The archive holds the -[archive README](../services/agents-api/RELEASE.md), the license, `manifest.json` -(commit, tree, platform, Go version, upstream protocol and binary hashes) and -`SHA256SUMS`. The build needs clean committed source and Python 3.9 or newer, and -packages deterministically. It carries no configuration, credentials, Web or -Runtime. Test archive changes by extracting a fresh copy and running its commands. +`make build-agents-api` builds `oac-core`, `oac-core-migrate`, `oac-core-device`, `oac-core-environment-key` and `oac-node` into `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core` (`OAC_DEV_CORE_BUILD_DIR` selects another absolute directory). The build copies only the source set listed in `scripts/build-agents-api.sh` (the Core service, its contracts, the shared packages it needs and the root Go module files) into a temporary context and builds with CGO disabled, read-only modules and trimmed paths. It needs no Node, Docker or other application. When Core gains a shared dependency, add that package to the list; never copy the whole repository to make it compile. + +`make docker-build-agents-api` builds the image `oac-core:dev` (`OAC_DEV_CORE_IMAGE` selects another name) from those five commands and the E2B helper. The base is the digest-pinned `debian:bookworm-slim` with CA certificates and the glibc runtime the helper needs; the default user is UID/GID 65532 and Core listens on `:8091`. The image is Linux amd64 only and is not pushed to a registry. Changes to the image or its build need `make check-agents-api-container` in addition to `make check`: it runs the official-client suite against the image with a read-only root filesystem and needs Linux Docker, a non-root user, `OAC_TEST_OFFICIAL_SDK_PYTHON` and a dedicated `OAC_TEST_DATABASE_URL`. + +`make build-agents-api-release` packages the same five commands into `oac-core--linux-amd64.tar.gz` and its `.sha256` under `~/.oac/build/oac-core-release` (`OAC_DEV_RELEASE_DIR`). The archive holds the [archive README](../services/agents-api/RELEASE.md), the license, `manifest.json` (commit, tree, platform, Go version, upstream protocol and binary hashes) and `SHA256SUMS`. The build needs clean committed source and Python 3.9 or newer, and packages deterministically. It carries no configuration, credentials, Web or Runtime. Test archive changes by extracting a fresh copy and running its commands. ## Publish a version @@ -213,46 +122,19 @@ git tag -a v1.2.3 FULL_REVIEWED_COMMIT_SHA -m "OpenAgentCore v1.2.3" git push origin v1.2.3 ``` -Tags use `vMAJOR.MINOR.PATCH`, optionally with a prerelease suffix such as `-rc.1` -and build metadata such as `+build.1`. A prerelease suffix creates a GitHub -prerelease. Pushing the tag is the release decision. Automated checks establish -build and test results, not real-model qualification: assess live execution -evidence before you push the tag. Model credentials and private certificate -authorities never enter CI or release inputs, including acceptance images that -contain them. - -The workflow runs three jobs on the tagged commit: `check` (the full `make check` -workflow), `native` (the `native-check` matrix) and `build`, which starts after -`native` succeeds. `build` prepares the pinned Runtime inputs, assembles the native -catalog and builds the distribution with the offline archive, and adds -`deploy/install-release.sh` as `install.sh` with its checksum. The `release` job -runs only after `check` and `build` succeed. It is the only job with -`contents: write`. It verifies the archive checksums and the native installer -checksums against the catalog, refuses an existing Release or draft for the tag, -uploads everything to a new draft, confirms the tag still points at the built -commit, and publishes that draft by its ID. Images ship as archives; no registry is -pushed. Downloads are anonymous. - -`install.sh` resolves the latest stable release once, or the release named by -`--version`, verifies the control archive and runs that bundle's installer; the -[installation guide](getting-started/install.md#install) covers its use. - -The jobs share Go module and build caches under `~/.oac/cache/`, keyed by runner -OS and architecture, the Go module files and the commit. An older cache only seeds -downloads and compilation; every check still runs. New keys are saved only after a -successful job. - -Never move a release tag or overwrite published assets. If the `release` job -fails, inspect the Release first: publication may have completed despite a lost -response. Leave a complete published Release as it is. For an incomplete draft, -fix or delete only that draft, then rerun the failed `release` job, which reuses -the original Actions artifact. Do not rerun the build or recreate the tag to -recover a failed upload. +Tags use `vMAJOR.MINOR.PATCH`, optionally with a prerelease suffix such as `-rc.1` and build metadata such as `+build.1`. A prerelease suffix creates a GitHub prerelease. Pushing the tag is the release decision. Automated checks establish build and test results, not real-model qualification: assess live execution evidence before you push the tag. Model credentials and private certificate authorities never enter CI or release inputs, including acceptance images that contain them. + +The workflow runs three jobs on the tagged commit: `check` (the full `make check` workflow), `native` (the `native-check` matrix) and `build`, which starts after `native` succeeds. `build` prepares the pinned Runtime inputs, assembles the native catalog and builds the distribution with the offline archive, and adds `deploy/install-release.sh` as `install.sh` with its checksum. The `release` job runs only after `check` and `build` succeed. It is the only job with `contents: write`. It verifies the archive checksums and the native installer checksums against the catalog, refuses an existing Release or draft for the tag, uploads everything to a new draft, confirms the tag still points at the built commit, and publishes that draft by its ID. Images ship as archives; no registry is pushed. Downloads are anonymous. + +`install.sh` resolves the latest stable release once, or the release named by `--version`, verifies the control archive and runs that bundle's installer; the [installation guide](getting-started/install.md#install) covers its use. + +The jobs share Go module and build caches under `~/.oac/cache/`, keyed by runner OS and architecture, the Go module files and the commit. An older cache only seeds downloads and compilation; every check still runs. New keys are saved only after a successful job. + +Never move a release tag or overwrite published assets. If the `release` job fails, inspect the Release first: publication may have completed despite a lost response. Leave a complete published Release as it is. For an incomplete draft, fix or delete only that draft, then rerun the failed `release` job, which reuses the original Actions artifact. Do not rerun the build or recreate the tag to recover a failed upload. ### Build a candidate without publishing -A manual run takes a full commit SHA, runs the same checks and builds, defaults to -the offline archive, and never publishes: +A manual run takes a full commit SHA, runs the same checks and builds, defaults to the offline archive, and never publishes: ```sh revision=$(git rev-parse HEAD) @@ -260,12 +142,9 @@ gh workflow run core-release --repo MiniMax-AI/parsar-core --ref main \ -f ref="$revision" -f offline=true -f draft_release=true ``` -With `draft_release=true` the result is an unpublished `build-` draft -Release; with `draft_release=false` the files stay in the Actions artifact. Use the -exact matched asset set; never mix builds or resolve components through `latest`. +With `draft_release=true` the result is an unpublished `build-` draft Release; with `draft_release=false` the files stay in the Actions artifact. Use the exact matched asset set; never mix builds or resolve components through `latest`. -`scripts/promote-qualified-release.py` qualifies such a draft on a supervised host -and publishes it; its module docstring states the rules. +`scripts/promote-qualified-release.py` qualifies such a draft on a supervised host and publishes it; its module docstring states the rules. ## Continuous integration @@ -277,31 +156,16 @@ and publishes it; its module docstring states the rules. | `actionlint` | Changes to workflows | Workflow syntax | | `core-release` | Version tags and manual runs | See [Publish a version](#publish-a-version) | -Documentation-only and unrelated Web changes do not start `native-check`. A newer -`core-check`, `api-acceptance` or `native-check` run on the same branch or pull -request cancels the older one. +Documentation-only and unrelated Web changes do not start `native-check`. A newer `core-check`, `api-acceptance` or `native-check` run on the same branch or pull request cancels the older one. ## Run Core without the installer -The standalone archive and container give you Core alone: no Web, no `oac` -command and no `config.json`. They suit development, testing and operators who -supervise Core themselves. Core reads only its environment; the -[configuration appendix](configuration.md#appendix-core-environment-without-the-installer) -lists the variables. `OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are -required; set `OAC_PUBLIC_URL` to the origin machines use to reach Core, or Core -runs without the daemon transport. - -- The [archive README](../services/agents-api/RELEASE.md) covers the standalone - archive. -- The [service guide](../services/agents-api/README.md) covers building and running - Core from source. - -To run the container, create a private directory (mode 0700) with `api.env` -(`OAC_DATABASE_URL` for a dedicated database, reachable from the container, -`OAC_CORE_KEY_DIGESTS_FILE=/run/core-key-digests.json`, and `OAC_PUBLIC_URL`) and -`core-key-digests.json`, a JSON array with the lowercase hex SHA-256 digest of your -Core key. Keep both files mode 0600 and the Core key itself elsewhere. Run the -migrations, then start Core: +The standalone archive and container give you Core alone: no Web, no `oac` command and no `config.json`. They suit development, testing and operators who supervise Core themselves. Core reads only its environment; the [configuration appendix](configuration.md#appendix-core-environment-without-the-installer) lists the variables. `OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required; set `OAC_PUBLIC_URL` to the origin machines use to reach Core, or Core runs without the daemon transport. + +- The [archive README](../services/agents-api/RELEASE.md) covers the standalone archive. +- The [service guide](../services/agents-api/README.md) covers building and running Core from source. + +To run the container, create a private directory (mode 0700) with `api.env` (`OAC_DATABASE_URL` for a dedicated database, reachable from the container, `OAC_CORE_KEY_DIGESTS_FILE=/run/core-key-digests.json`, and `OAC_PUBLIC_URL`) and `core-key-digests.json`, a JSON array with the lowercase hex SHA-256 digest of your Core key. Keep both files mode 0600 and the Core key itself elsewhere. Run the migrations, then start Core: ```sh config_dir="$HOME/.oac/oac-core-deployment" @@ -318,16 +182,6 @@ docker run --name oac-core --detach --read-only \ curl --fail http://127.0.0.1:8091/healthz ``` -`--user` lets the container read the key digest file as your non-root host user; -alternatively grant UID 65532 read access and omit it. Put a TLS reverse proxy in -front for remote clients. `/healthz` reports liveness only. All state is in -PostgreSQL, so the container needs no writable volume; stop and start it with -`docker stop` and `docker start`, and never remove the database to replace it. One -Core process serves each database; replicas add no availability. After startup, -use the Core key with the [administrator API](../contracts/agents-api/admin-api.md) -to create Projects and issue application keys. - -The image also contains `oac-core-device` for an -[internal execution device](../services/agents-api/README.md#internal-execution-device-connection) -and `oac-core-environment-key`, the -[break-glass credential command](../contracts/agents-api/environment-executor-credentials.md#break-glass-command). +`--user` lets the container read the key digest file as your non-root host user; alternatively grant UID 65532 read access and omit it. Put a TLS reverse proxy in front for remote clients. `/healthz` reports liveness only. All state is in PostgreSQL, so the container needs no writable volume; stop and start it with `docker stop` and `docker start`, and never remove the database to replace it. One Core process serves each database; replicas add no availability. After startup, use the Core key with the [administrator API](../contracts/agents-api/admin-api.md) to create Projects and issue application keys. + +The image also contains `oac-core-device` for an [internal execution device](../services/agents-api/README.md#internal-execution-device-connection) and `oac-core-environment-key`, the [break-glass credential command](../contracts/agents-api/environment-executor-credentials.md#break-glass-command). diff --git a/scripts/name-allowlist.json b/scripts/name-allowlist.json index 9b7208f2a..8a13d8b23 100644 --- a/scripts/name-allowlist.json +++ b/scripts/name-allowlist.json @@ -571,7 +571,7 @@ }, { "path": "services/agents-api/README.md", - "regex": "Parsar's product|Parsar\\nworkspace|Parsar Skill/SP", + "regex": "Parsar's product|Parsar\\sworkspace|Parsar Skill/SP", "reason": "These exact phrases refer to the separate Parsar product, its ownership or historical source, not the OpenAgentCore brand." }, { diff --git a/services/agents-api/RELEASE.md b/services/agents-api/RELEASE.md index 948d4952e..b76b28cf9 100644 --- a/services/agents-api/RELEASE.md +++ b/services/agents-api/RELEASE.md @@ -1,17 +1,10 @@ # Standalone Core archive -This Linux amd64 archive holds Core alone: the API server `oac-core`, its migrator -`oac-core-migrate`, and the operator commands `oac-core-device`, -`oac-core-environment-key` and `oac-node`. It has no Web console, installer or -`oac` command, and it needs your own PostgreSQL. To install Core with Web and nodes, -use the -[installation guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/install.md). +This Linux amd64 archive holds Core alone: the API server `oac-core`, its migrator `oac-core-migrate`, and the operator commands `oac-core-device`, `oac-core-environment-key` and `oac-node`. It has no Web console, installer or `oac` command, and it needs your own PostgreSQL. To install Core with Web and nodes, use the [installation guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/install.md). ## Verify and extract -Verify the archive checksum supplied with the package, then extract it into a new -directory under `~/.oac/`. Keep configuration outside the extracted package so -that replacing the binaries does not replace credentials or state. +Verify the archive checksum supplied with the package, then extract it into a new directory under `~/.oac/`. Keep configuration outside the extracted package so that replacing the binaries does not replace credentials or state. ```sh sha256sum -c @ARCHIVE_NAME@.tar.gz.sha256 @@ -22,15 +15,11 @@ sha256sum -c SHA256SUMS core_bin_dir="$PWD/bin" ``` -`manifest.json` records the source commit and tree, the platform, the Go version, -the pinned upstream protocol and the binary hashes. Checksums detect changed -bytes; obtain the archive and its checksum from a trusted source. +`manifest.json` records the source commit and tree, the platform, the Go version, the pinned upstream protocol and the binary hashes. Checksums detect changed bytes; obtain the archive and its checksum from a trusted source. ## Configure and start -Create a dedicated PostgreSQL database and account. Generate a random Core key, -keep it in private storage, and write its lowercase hex SHA-256 digest as a JSON -array to `core-key-digests.json`: +Create a dedicated PostgreSQL database and account. Generate a random Core key, keep it in private storage, and write its lowercase hex SHA-256 digest as a JSON array to `core-key-digests.json`: ```sh umask 077 @@ -42,33 +31,17 @@ export OAC_ADDR=127.0.0.1:8091 export OAC_PUBLIC_URL=http://127.0.0.1:8091 ``` -`OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required. `OAC_PUBLIC_URL` -is the origin that applications and machines use to reach Core: an HTTPS origin, -or plain HTTP on loopback only. The -[configuration reference](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/configuration.md#appendix-core-environment-without-the-installer) -lists every variable. Run the migrations, then start Core in the foreground or -under your own supervisor: +`OAC_DATABASE_URL` and `OAC_CORE_KEY_DIGESTS_FILE` are required. `OAC_PUBLIC_URL` is the origin that applications and machines use to reach Core: an HTTPS origin, or plain HTTP on loopback only. The [configuration reference](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/configuration.md#appendix-core-environment-without-the-installer) lists every variable. Run the migrations, then start Core in the foreground or under your own supervisor: ```sh "$core_bin_dir/oac-core-migrate" "$core_bin_dir/oac-core" ``` -`GET /healthz` reports liveness. One Core process serves each database; replicas -add no availability. Keep the database when you replace the binaries. Put a TLS -reverse proxy in front for remote clients. +`GET /healthz` reports liveness. One Core process serves each database; replicas add no availability. Keep the database when you replace the binaries. Put a TLS reverse proxy in front for remote clients. ## Next steps -- Use the Core key with the - [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/admin-api.md) - to create a Project and issue its API key, then follow the - [quickstart](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/quickstart.md). -- To run Sessions on your own machines, follow the - [self-hosted guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/self-hosted.md). - The one-command installation needs this release's native installer catalog in - `OAC_NATIVE_INSTALLER_DIR`. The - [executor credential contract](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) - covers issuing credentials with the Core key and `oac-core-environment-key`. -- To add sandbox nodes with `oac-node`, see the - [node guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/nodes.md). +- Use the Core key with the [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/admin-api.md) to create a Project and issue its API key, then follow the [quickstart](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/quickstart.md). +- To run Sessions on your own machines, follow the [self-hosted guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/self-hosted.md). The one-command installation needs this release's native installer catalog in `OAC_NATIVE_INSTALLER_DIR`. The [executor credential contract](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) covers issuing credentials with the Core key and `oac-core-environment-key`. +- To add sandbox nodes with `oac-node`, see the [node guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/nodes.md). From f8aa0262a6a7d514d1c278dd12dddcf401266c4b Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 07:26:09 +0000 Subject: [PATCH 7/8] docs: fix review findings in build, release and self-hosted docs Describe the promotion path that works (flat candidate files; the command creates its own draft) and keep the docstring's paragraphs in --help. Correct the release recovery, host requirements, Claude and MiniMax build hosts, native-check triggers, cache scope, catalog contents and archive contents. Split rules from steps between the credential contract and the self-hosted guide, complete the break-glass command, and fix the installer rules on uninstall exceptions, root file removal, the download contract and native catalog handling. --- apps/docs/content/docs/public-api.mdx | 7 ++- .../content/docs/self-hosted-execution.mdx | 8 +-- apps/docs/content/guide-sources.json | 8 +-- .../environment-executor-credentials.md | 6 +- deploy/install/README.md | 16 ++--- docs/api/README.md | 7 ++- docs/getting-started/self-hosted.md | 8 +-- docs/maintainers.md | 36 +++++------ scripts/promote-qualified-release.py | 59 +++++++++++-------- services/agents-api/RELEASE.md | 2 +- 10 files changed, 87 insertions(+), 70 deletions(-) diff --git a/apps/docs/content/docs/public-api.mdx b/apps/docs/content/docs/public-api.mdx index 59c941a54..a33e83b6b 100644 --- a/apps/docs/content/docs/public-api.mdx +++ b/apps/docs/content/docs/public-api.mdx @@ -128,9 +128,10 @@ Creating or reading a `self_hosted` Session returns short-lived install commands `GET /core/v1/projects/{project_id}/environments/{environment_id}/installation`. Machine installers use `POST /api/v1/agent-daemon/installation` and its `/claim` subroute with the installation Bearer authorization. Qualified artifacts under -`/api/v1/agent-daemon/install/{version}/` are public, immutable release content. See -the [self-hosted guide](/self-hosted-execution) for expiry, retry, credential -ownership and platform rules. +`/api/v1/agent-daemon/install/{version}/` are public, immutable release content. The +[installation grant](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#installation-grant) +owns expiry, retry and credential ownership; the +[self-hosted guide](/self-hosted-execution#platforms) lists platforms. The console-local `GET`/`POST /console/installation/domain` surface uses the signed-in browser session and same-origin checks. It delegates only domain setup to the diff --git a/apps/docs/content/docs/self-hosted-execution.mdx b/apps/docs/content/docs/self-hosted-execution.mdx index 9c65e1233..a07ff1842 100644 --- a/apps/docs/content/docs/self-hosted-execution.mdx +++ b/apps/docs/content/docs/self-hosted-execution.mdx @@ -60,7 +60,7 @@ No administrator privileges or Docker are needed. In Web, open the Session and copy the command under **Connect a host**. -The command expires after 30 minutes. Read the Session again, or reload its page in Web, for a fresh one. Treat the command as a temporary secret: it can claim the machine's credential but cannot run work or read files. The [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#installation-grant) describes what invalidates it. +Keep the command private: it carries a short-lived [installation grant](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#installation-grant) that claims the machine's credential. When it has expired, read the Session again or copy a fresh command from Web. The installer reports three results: @@ -70,7 +70,7 @@ The installer reports three results: | **Daemon connection** | Core confirmed the daemon's authenticated connection | | **Model configuration** | Not checked; the first Turn uses the Session's model provider | -If the connection is not confirmed within 45 seconds, the installer prints the path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the same command again with the same installation directory: completed components and the credential are kept and a running daemon is reused. Do not remove the workspace or the Session to retry. +If the connection is not confirmed within 45 seconds, the installer prints the path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the install command again with the same installation directory, copying a fresh one from Web if it has expired: completed components and the credential are kept and a running daemon is reused. Do not remove the workspace or the Session to retry. ### Options for automation @@ -130,7 +130,7 @@ The installation's `bin/oac-daemon` finds its own installation. Use it for: If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the connection under **Host connection** on the Session's page in Web, or with the [connection status](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#connection-status). -To add a Harness, run the original install command again with the same connection options and the Harness to add. The installer checks the existing contents, adds only missing components and keeps the Harnesses already installed. Restart a running daemon afterwards so it discovers the new Harness. +To add a Harness, run the install command again (a fresh copy from Web if it has expired) with the same installation directory and the Harness to add. The installer checks the existing contents, adds only missing components and keeps the Harnesses already installed. Restart a running daemon afterwards so it discovers the new Harness. Stopping the daemon, cancelling a Turn or deleting the Session never removes the machine's workspace, native history or capability snapshot. An installation from another daemon version, or one whose files were changed, is refused. The installer never upgrades, repairs or migrates it; install into a separate directory. @@ -143,7 +143,7 @@ In Web, the Session's **Executor credentials** list the machine's credential: | **Rotate** | The credential gets a new secret; the old secret stops working at once. On a revoked credential the action is **Restore** | | **Revoke** | The credential stops working at once | -To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the JSON at its configured credential-file path with the new credential, and run `oac-daemon start`. Do not run `install` again over the existing installation, and do not issue a second credential: the Environment stays bound to the credential it first connected with. +To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the JSON at its configured credential-file path with the new credential, and run `oac-daemon start`. Do not run `install` again over the existing installation, and rotate the existing credential rather than issuing a second one, which [cannot connect](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#revoked-or-rotated-credential). In an archived Project, credentials cannot be issued or rotated; revocation remains available. Operators can manage credentials with the Core key; see the [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md#core-key-routes). diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json index 48a8be4af..76771d001 100644 --- a/apps/docs/content/guide-sources.json +++ b/apps/docs/content/guide-sources.json @@ -9,13 +9,13 @@ "docs/configuration.md": "7dc0031145bb5811bf22cab15270b511b1c43b1bd1ee2b71175d3bc848751bd6", "docs/web/core-connection.md": "861c75c32676fd17b84eb356b263096703af5750c1ceb71bb339e84607fffc56", "docs/getting-started/quickstart.md": "b989682ac2e58d59a794118fd371b37d1ea64c957d3512ff53739458a95d0f9a", - "docs/api/README.md": "3a633c6e9820ab15952d1361856951c9ce463ea9d4c3d32868d147d05ecd77fd", + "docs/api/README.md": "faada41906e584a2dfcd1efb0af893e6039cd456eff989f019e0dd816dc419ff", "contracts/agents-api/execution-tools.md": "fe1e3cf471fe9c7ef04afa7230e6b7742fbf2146c74bd944a7a22d5d4d4197da", "docs/api/public-agent-api.md": "e1111aaf5215392178246969e281b930374b22ee380d529b08b2482836d6333d", "docs/examples.md": "c12c34adc5b2fa57c81602b23d8ecc8317596f9c5a4aedbf1f1fe934a08a94f5", "contracts/agents-api/environments.md": "2f4d506d7353c3ab4734bd2c06216dbac231d951e95e397171e793d74dc535b0", "docs/getting-started/nodes.md": "7c1b7e364ce85b5b5916358cafc618f29042b2125ac45653be665ba07e772fdb", - "docs/getting-started/self-hosted.md": "6e77e3566309f51c6228d18648f33a9e7fe093715c03e6b930681314cd5a6de7", + "docs/getting-started/self-hosted.md": "653a9132ea6bbc39186f7917fa1596027d091071172e6a6afd9f618fc1dab725", "apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", "docs/web/README.md": "47159689b2488f0a94a1af8488bcf98b465e0cca4003d64a4699d7b4db24e086", "docs/api/web-management.md": "fc064c53874ca2aa6d17e5265763a4a4c744860062c27353cfafe74d3e6ac790", @@ -39,13 +39,13 @@ "content/docs/configure.mdx": "02d1eee789646fdf65ed2ec48b6fe954c81107d6ff5891536ec6ac2df0a8c4dc", "content/docs/bootstrap-projects-keys.mdx": "91a6f62cbe41737adfce9e8ec56e6dc3e2fb0ec8f6b677576f941cdfb378dc47", "content/docs/quickstart.mdx": "d0ab9537ab68f6dd3109353e937a29c5d58ec3894b56a52873c9a1d79dfa60a6", - "content/docs/public-api.mdx": "ca7b39e0ff296e82f5be012692a166fea9f278d7cfe02224f0f0fb9f17f6722d", + "content/docs/public-api.mdx": "cc0bad7611eb2c4f1d7ec231a3f77c37989e268b76423f292eee88ccf25eb892", "content/docs/agents-and-tools.mdx": "3ca16d2e2ff1c759251fbdf6e071ff09a93d5efc7acaca36a48854597d4f9576", "content/docs/sessions.mdx": "e8693563738f690c21be92e0ea09e925528bc85bea2c0fb4799c3f1c4074cfba", "content/docs/examples.mdx": "5f1dde8043695c40edf775345159d93b2444d5ce6a109829b78678b0b5de0d46", "content/docs/environments-and-files.mdx": "a51751d78649cbd5af3458890efd360a857f82538de889d6419a458d01b2a02d", "content/docs/hosted-providers.mdx": "9c241090709a9929ab6a34615db1e20a94c1f36649026281836060e81ac40b4c", - "content/docs/self-hosted-execution.mdx": "fea4a3dab34fcdb8f4ea31f1a9cb564a9e98889b5ab1057a1bea143a4a16952d", + "content/docs/self-hosted-execution.mdx": "a8e6500e41c666eacb2c75882b2b8ee0cf122fe19ea36f39ef89f5dcf17b68e1", "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", "content/docs/console.mdx": "a930ce36035de74f0c2bef71bed067fc2287ba1c70a485758bf100d484f6adfd", "content/docs/admin-api.mdx": "6fcfe4c5b5b70d5322c131ce4ab70f1a7ea8b0033a5f500b20cf27abb338236e", diff --git a/contracts/agents-api/environment-executor-credentials.md b/contracts/agents-api/environment-executor-credentials.md index efe946666..5ace025ee 100644 --- a/contracts/agents-api/environment-executor-credentials.md +++ b/contracts/agents-api/environment-executor-credentials.md @@ -20,7 +20,7 @@ Session create, retrieve and update responses of a `self_hosted` Session carry ` | `expires_at` | Unix time when the grant expires, 30 minutes after the response | | `commands.posix`, `commands.powershell` | The install command for Linux/macOS and for Windows PowerShell | -The grant is bound to the Environment, the Session creator's principal and the Core build. It stops working when it expires, when the Session is deleted, when the Project is archived or when Core runs a different build. Reading the Session again returns a fresh grant. Treat the command as a temporary secret: it can claim the credential, but it cannot run work or read files. +The grant is bound to the Environment, the Session creator's principal and the Core build. It stops working when it expires, when the Session is deleted, when the Project is archived or when Core runs a different build. Reading the Session again returns a fresh grant. Core stores no grant: each response signs a new one, and stored events never carry it. Treat the command as a temporary secret: it can claim the credential, but it cannot run work or read files. The installer generates the secret and saves it privately as `daemon/executor-credential.json` in the installation directory before it claims the key. Core stores the digest under the key ID equal to the Environment ID. A lost response is safe to retry: the retry must present the same secret. A grant never replaces or restores a credential. If the Environment already has a different, rotated or revoked credential, the claim fails with 409 `executor_credential_exists`. @@ -67,7 +67,7 @@ Issue, rotate and revoke each record an administrator audit entry (`resource_typ ### Break-glass command -`oac-core-environment-key` issues, rotates or revokes a credential directly in the database when the Core API is unavailable. It needs the private database configuration and the Project's execution principal: `--tenant` (the Project's tenant UUID in the `projects` table), `--organization core`, `--project proj_`, `--subject-kind service_account`, `--subject-id project:` and `--key-id`. `--environment` restricts a new credential to one Environment. The command bypasses the Core API: it skips the archived-Project check and writes no audit entry, so use the Core-key routes whenever Core is running. A credential issued without an Environment restriction cannot be managed through the routes above. +`oac-core-environment-key` issues, rotates or revokes a credential directly in the database when the Core API is unavailable. It reads Core's database settings (`OAC_DATABASE_URL`, and `OAC_DATABASE_PASSWORD_FILE` when set) and needs the Project's execution principal: `--tenant` (the Project's tenant UUID in the `projects` table), `--organization core`, `--project proj_`, `--subject-kind service_account`, `--subject-id project:` and `--key-id`. Without another flag it issues a new credential, and `--environment` restricts it to one Environment. `--rotate` replaces the secret of an existing credential, including a revoked one; `--revoke` revokes it without printing a secret. Rotation and revocation keep the stored restriction and refuse `--environment`, and the two flags are mutually exclusive. Issuance and rotation print the credential file once on standard output; redirect it to a new mode-0600 file. The command bypasses the Core API: it skips the archived-Project check and writes no audit entry, so use the Core-key routes whenever Core is running. A credential issued without an Environment restriction cannot be managed through the routes above. ## Connection status @@ -89,7 +89,7 @@ The installer derives this route from the returned `remote_url` and does not fol When Core permanently rejects the daemon (enrollment 401 or 409, a permanent WebSocket rejection, or a daemon version from another Core distribution), the daemon prints the reason once and makes no further requests until it is stopped; it then exits successfully, so a supervisor that restarts on exit does not loop. When started again, it tries enrollment once and parks again. Transient failures keep the normal reconnect behavior and never replay execution. -To reconnect, rotate the same `key_id` (**Rotate**, or **Restore** for a revoked credential, in the Session's **Executor credentials**), stop the daemon, replace the credential file at its configured path, and start the daemon again. A new `key_id` cannot reconnect an Environment that is already bound: issuing it succeeds, but enrollment with it returns 409. Rotation does not reinstall Harnesses, change the workspace or replace native history; never create a new Session history to recover a credential. +A machine reconnects only with its bound `key_id`, rotated to a new secret that replaces the credential file at its configured path; the [self-hosted guide](../../docs/getting-started/self-hosted.md#rotate-or-revoke) gives the steps. A new `key_id` cannot reconnect an Environment that is already bound: issuing it succeeds, but enrollment with it returns 409. Rotation does not reinstall Harnesses, change the workspace or replace native history; never create a new Session history to recover a credential. ## Model provider diff --git a/deploy/install/README.md b/deploy/install/README.md index a03dbc01a..75d9607e7 100644 --- a/deploy/install/README.md +++ b/deploy/install/README.md @@ -89,21 +89,21 @@ The node installer runs as root and prepares the host for one node per installat - Node configuration and identity live under `~/.oac/nodes//` in the node account's home (`/var/lib/oac-node`); microsandbox uses a separate short private Runtime home. - The node service owns its provider processes outside the Core container. `KillMode=process` keeps resident microVM and helper processes across a service restart. The service restarts after failures with no start limit, so a node outlasts a Core outage, and stops restarting when the node program exits 78 because Core answered 401 to its credential (a removed node). - It never installs Docker, KVM or packages and never changes device permissions. It refuses SELinux-enforcing hosts and changes nothing when a check fails. The enrollment token comes only on standard input, never in arguments or the environment. -- Files the service account owns are read, written and deleted only with that account's credentials, never by root. That work runs in a child that starts its own session with `/dev/null` as input, joins a new session keyring and dies with its parent; root shows its output only as plain text (terminal controls become `?`). SIGINT, SIGHUP and SIGTERM stop that child and what it started. +- Files the service account owns are read, written and deleted only with that account's credentials. The one exception is root removing the account's home after `userdel`, when no process can still run as that account. That work runs in a child that starts its own session with `/dev/null` as input, joins a new session keyring and dies with its parent; root shows its output only as plain text (terminal controls become `?`). SIGINT, SIGHUP and SIGTERM stop that child and what it started. - Root never runs a file the service account can write, opens a URL it wrote, or follows a link in its home. Capture the trusted bootstrap bytes before dropping to the service account and pass them through the fork; the service account writes its own retained generation helper. Never open the caller's private download directory to it or let root write into service-owned state. - The generated bootstrap passes only the six standard HTTP/HTTPS proxy and bypass variables through sudo and gives both spellings the lowercase value when present, even if empty, so curl, urllib and the Go registration command follow the same rules. The installation child keeps just those names beside its fixed environment. Proxy values stay out of arguments, saved configuration, service units and diagnostics; never use broad sudo environment inheritance. This covers installation downloads only, not the node service. -- `--uninstall` removes a node only after Core rejects its credential. It never touches sandboxes, volumes or images (the Runtime image and the microsandbox store stay), deletes the account only when the installer created it and no node remains, and otherwise removes only the groups it added. +- `--uninstall` removes a node only after Core rejects its credential, except for a node that never registered and with `--force`, which Web offers when the old Core address no longer responds. It never touches sandboxes, volumes or images (the Runtime image and the microsandbox store stay), deletes the account only when the installer created it and no node remains, and otherwise removes only the groups it added. - Refuse resources of an older product name for the same installation ID; never adopt them or remove another installation's resources. ## Download contract -The distribution manifest is the one download contract for the Core, node and self-hosted installers: flat versioned file names, and the compressed and unpacked size and SHA-256 of the Runtime. +The distribution manifest is the one download contract for the Core and node installers: flat versioned file names, and the compressed and unpacked size and SHA-256 of the Runtime. The self-hosted counterpart is the native `catalog.json`, which Core serves as one `.sha256` per installer archive. - The default installation downloads the Core, Web and PostgreSQL payloads, never the Runtime image or node execution artifacts. Core's image never acquires execution-only payloads. The offline archive stays an explicit option. - A node obtains bootstrap metadata from the console that generated its command, or from a local offline bundle. Web serves artifacts it has locally and redirects missing declared execution artifacts to the versioned HTTPS release base in the verified manifest. Web never downloads or caches those bytes. - Only artifact requests may follow HTTPS redirects, and only without credentials or cookies. Metadata and enrollment requests stay on the configured console. The console publishes only fixed non-secret files and declared artifact names. - Download into private temporary files, verify size and SHA-256 before an atomic rename, resume interrupted transfers, and reuse only verified cache entries or exact image identities. Never select a release other than the pinned one. -- Python zipapps bundle the shared resolver with each remote bootstrap. The node asset includes the `oac-node` binary. +- Python zipapps bundle the shared resolver with each remote bootstrap. The node asset is the `oac-node` binary. - Release downloads are anonymous. Never add repository credentials to installed node or Runtime configuration. - Manual builds use the `build-` release tag and tag builds the `v*` tag. The manifest's download base must match the release tag; artifact file names and source provenance keep the full source SHA. @@ -113,7 +113,7 @@ The manifest's `images` records each exported image's config digest, and `image_ Docker's classic image store identifies images by config digest, and its containerd store by the OCI descriptor. The build therefore takes the digest the local store resolves from BuildKit's build metadata, never the `--iidfile` config digest alone, and disables provenance attestations so each image and archive holds one platform manifest in both stores. For the same reason the default PostgreSQL image is pinned by its linux/amd64 platform manifest digest: a pulled multi-platform tag keeps its whole index in the containerd store, and its export holds every platform. -The Core, node and self-hosted installers share one resolver for these identities. It confirms Linux amd64 and the returned immutable local ID, and service and provider configuration and Runtime launches use that ID. Tags never replace identity verification. The microsandbox `runtime_ref` is independent of Docker's local store identity. +The Core and node installers share one resolver for these identities. It confirms Linux amd64 and the returned immutable local ID, and service and provider configuration and Runtime launches use that ID. Tags never replace identity verification. The microsandbox `runtime_ref` is independent of Docker's local store identity. ## Native daemon installer @@ -122,13 +122,13 @@ The Core, node and self-hosted installers share one resolver for these identitie - Interactive selection and CLI-only installation share one options and validation path. There is no installation-options file. The saved installation state and explicitly supplied credential and tool-variable files serve runtime operation, not a second configuration language. - Each release bundles pinned Node.js and npm, the native Harnesses and their adapter assets. Registration lives in the CLI, and native activation and readiness in each adapter's optional `agent.Installation` descriptor. Core never selects native paths or OS-specific steps. - Bootstrap scripts only download and extract the current platform's archive, after verifying the checksum Core provides. Installation, startup, connection verification and execution stay common. Native bundles must match Core's source revision and Runtime wire version. -- Neither Core installation nor repair downloads native payloads. Core serves the local offline archives or redirects to the catalog URL without proxying or caching; it verifies local archives before serving, and a corrupt local archive fails closed. +- Neither Core installation nor repair downloads native payloads. The Core installer keeps the catalog and any offline archives in the installation's private `native-installers/` directory, mounts it read-only into container Core and points native Core at the same files. Core serves the local offline archives or redirects to the catalog URL without proxying or caching; it verifies local archives when it starts, and a corrupt local archive stops Core from starting. - Every mutation holds the installation directory lock. Publish complete, checksum-verified components from staging, then commit the configuration after native readiness passes. A rerun with the same connection settings adds the selected Harnesses and validates existing contents. Never overwrite, upgrade, repair or migrate installed components; missing, modified, wrong-platform or incompatible content is an explicit error. A partial addition keeps the old configuration and reusable complete components and removes nothing. - Serialize background PID inspection and publication so concurrent starts cannot create two daemons. An installed daemon registers only the adapter kinds its verified installation manifest names; other Harness executables on `PATH` cannot extend it. Direct `connect` refuses an installed Runtime and points to `start`. - The installer runs as the current user in writable directories and never elevates. Subprocess diagnostics never expose sensitive parameters or environment values. Readiness checks take the installer's cancellation context and reap their processes before returning. - Report installation, authenticated connection and model configuration as separate results. Starting execution never downloads or installs Harnesses. Stop and reconnect keep capability snapshots and native Session state. -- `scripts/build-native-installer.mjs` validates pins and startup, hashes every component file, accepts only contained regular files and rejects escaping links. For the native bundle, Claude's frozen `pnpm deploy` export is reinstalled with the hoisted linker before contained links are flattened; the Runtime image's Claude archive is unchanged. The `native-check` workflow builds and tests installation, addition and reuse, missing arguments and the unsupported Windows MiniMax case on Linux, macOS and Windows. +- `scripts/build-native-installer.mjs` validates pins and startup, hashes every component file, accepts only contained regular files and rejects escaping links. Before it packages Claude, the `native-check` workflow reinstalls the frozen `pnpm deploy` export with the hoisted linker, so flattening contained links keeps Node's dependency resolution; the Runtime image's Claude archive is unchanged. The `native-check` workflow builds and tests installation, addition and reuse, missing arguments and the unsupported Windows MiniMax case on Linux, macOS and Windows. ## Validation -`make check-distribution` covers the production proxy, the installation rules, release metadata and native catalog assembly, including bundle manifests larger than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). A real bundle check covers default and provider selection, component modes, connecting to an existing Web, public native execution and restart retention. Diagnostics report observed service health, never fabricated model or environment readiness. Runtime observations belong to Core; do not add monitoring or lifecycle tracking to the installer or the landing site. +`make check-distribution` covers the production proxy, the installation rules, release metadata and native catalog assembly, including bundle manifests larger than Node's default subprocess buffer (catalog assembly reads up to 64 MiB). Live release qualification and its stages are in the `scripts/promote-qualified-release.py` docstring. Diagnostics report observed service health, never fabricated model or environment readiness. Runtime observations belong to Core; do not add monitoring or lifecycle tracking to the installer or the landing site. diff --git a/docs/api/README.md b/docs/api/README.md index d200afdc4..6f939f077 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -125,9 +125,10 @@ Creating or reading a `self_hosted` Session returns short-lived install commands `GET /core/v1/projects/{project_id}/environments/{environment_id}/installation`. Machine installers use `POST /api/v1/agent-daemon/installation` and its `/claim` subroute with the installation Bearer authorization. Qualified artifacts under -`/api/v1/agent-daemon/install/{version}/` are public, immutable release content. See -the [self-hosted guide](../getting-started/self-hosted.md) for expiry, retry, credential -ownership and platform rules. +`/api/v1/agent-daemon/install/{version}/` are public, immutable release content. The +[installation grant](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) +owns expiry, retry and credential ownership; the +[self-hosted guide](../getting-started/self-hosted.md#platforms) lists platforms. The console-local `GET`/`POST /console/installation/domain` surface uses the signed-in browser session and same-origin checks. It delegates only domain setup to the diff --git a/docs/getting-started/self-hosted.md b/docs/getting-started/self-hosted.md index 8a943115a..011c665f8 100644 --- a/docs/getting-started/self-hosted.md +++ b/docs/getting-started/self-hosted.md @@ -57,7 +57,7 @@ No administrator privileges or Docker are needed. In Web, open the Session and copy the command under **Connect a host**. -The command expires after 30 minutes. Read the Session again, or reload its page in Web, for a fresh one. Treat the command as a temporary secret: it can claim the machine's credential but cannot run work or read files. The [credential contract](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) describes what invalidates it. +Keep the command private: it carries a short-lived [installation grant](../../contracts/agents-api/environment-executor-credentials.md#installation-grant) that claims the machine's credential. When it has expired, read the Session again or copy a fresh command from Web. The installer reports three results: @@ -67,7 +67,7 @@ The installer reports three results: | **Daemon connection** | Core confirmed the daemon's authenticated connection | | **Model configuration** | Not checked; the first Turn uses the Session's model provider | -If the connection is not confirmed within 45 seconds, the installer prints the path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the same command again with the same installation directory: completed components and the credential are kept and a running daemon is reused. Do not remove the workspace or the Session to retry. +If the connection is not confirmed within 45 seconds, the installer prints the path of the daemon's log. The daemon keeps reconnecting. Fix the cause and run the install command again with the same installation directory, copying a fresh one from Web if it has expired: completed components and the credential are kept and a running daemon is reused. Do not remove the workspace or the Session to retry. ### Options for automation @@ -127,7 +127,7 @@ The installation's `bin/oac-daemon` finds its own installation. Use it for: If you set `OAC_RUNTIME_HOME`, use the same value for every command. Check the connection under **Host connection** on the Session's page in Web, or with the [connection status](../../contracts/agents-api/environment-executor-credentials.md#connection-status). -To add a Harness, run the original install command again with the same connection options and the Harness to add. The installer checks the existing contents, adds only missing components and keeps the Harnesses already installed. Restart a running daemon afterwards so it discovers the new Harness. +To add a Harness, run the install command again (a fresh copy from Web if it has expired) with the same installation directory and the Harness to add. The installer checks the existing contents, adds only missing components and keeps the Harnesses already installed. Restart a running daemon afterwards so it discovers the new Harness. Stopping the daemon, cancelling a Turn or deleting the Session never removes the machine's workspace, native history or capability snapshot. An installation from another daemon version, or one whose files were changed, is refused. The installer never upgrades, repairs or migrates it; install into a separate directory. @@ -140,7 +140,7 @@ In Web, the Session's **Executor credentials** list the machine's credential: | **Rotate** | The credential gets a new secret; the old secret stops working at once. On a revoked credential the action is **Restore** | | **Revoke** | The credential stops working at once | -To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the JSON at its configured credential-file path with the new credential, and run `oac-daemon start`. Do not run `install` again over the existing installation, and do not issue a second credential: the Environment stays bound to the credential it first connected with. +To reconnect after a rotation, stop the daemon with `oac-daemon stop`, replace the JSON at its configured credential-file path with the new credential, and run `oac-daemon start`. Do not run `install` again over the existing installation, and rotate the existing credential rather than issuing a second one, which [cannot connect](../../contracts/agents-api/environment-executor-credentials.md#revoked-or-rotated-credential). In an archived Project, credentials cannot be issued or rotated; revocation remains available. Operators can manage credentials with the Core key; see the [credential contract](../../contracts/agents-api/environment-executor-credentials.md#core-key-routes). diff --git a/docs/maintainers.md b/docs/maintainers.md index 8305f6a03..6bab30466 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -6,7 +6,7 @@ This guide is for maintainers who build and publish OpenAgentCore. To install Co A distribution is the matched set of Linux amd64 release assets built from one commit: the control archive (the installer, the `oac` command, and the Core, Web, gateway and PostgreSQL images), the Runtime image and node artifacts as separate files, and the native installers. -Build on Linux x86_64 with a glibc compatible with Debian 12, Docker, the Go version in `go.mod`, Node, pnpm, Python 3.9 or newer, curl and tar. The source must be clean and committed. First prepare the pinned Codex package and MiniMax Code companion, then build: +Build on Linux x86_64 with a glibc compatible with Debian 12, Docker, the Go version in `go.mod`, a C compiler (the microsandbox helper is a CGO build), Node, pnpm, Python 3.9 or newer, curl, tar and sha256sum. The source must be clean and committed. First prepare the pinned Codex package and MiniMax Code companion, then build: ```sh bash scripts/prepare-release-runtimes.sh @@ -17,36 +17,36 @@ export CORE_DISTRIBUTION_RELEASE_BASE_URL=https://github.com/MiniMax-AI/parsar-c make build-core-distribution ``` -`prepare-release-runtimes.sh` refuses an existing `~/.oac/build/release-inputs`; use a fresh build host or directory. +`prepare-release-runtimes.sh` refuses an existing `~/.oac/build/release-inputs`; use a fresh build host. | Variable | Effect | | --- | --- | | `CORE_DISTRIBUTION_RELEASE_BASE_URL` | Versioned HTTPS directory that will serve the generated asset file names (never `latest`). Required unless `CORE_DISTRIBUTION_OFFLINE=1` | | `CORE_DISTRIBUTION_OFFLINE` | `1` also builds the offline archive | | `AGENTS_RUNTIME_CODEX_PACKAGE`, `MCODE_HARNESS_BUILD_DIR` | Pinned Runtime inputs from `prepare-release-runtimes.sh` | -| `CORE_DISTRIBUTION_CODEX_IMAGE`, `CORE_DISTRIBUTION_CLAUDE_IMAGE`, `CORE_DISTRIBUTION_MCODE_IMAGE` | Use existing Harness images instead of building them; set all three or none. Each must contain the daemon built from this commit | +| `CORE_DISTRIBUTION_CODEX_IMAGE`, `CORE_DISTRIBUTION_CLAUDE_IMAGE`, `CORE_DISTRIBUTION_MCODE_IMAGE` | Use existing Harness images, given as immutable `sha256:` image IDs, instead of building them; set all three or none. Each must contain the daemon built from this commit | | `OAC_NATIVE_INSTALLER_BUILD_DIR` | Native installer catalog directory; see [Native installers](#native-installers) | | `CORE_DISTRIBUTION_BUILD_DIR` | Output directory under `~/.oac`. Default: `~/.oac/build/core-distribution` | | `CORE_DISTRIBUTION_BUILD_NETWORK` | Docker build network: `default`, `host` or `none` | | `CORE_DISTRIBUTION_MICROSANDBOX_ARCHIVE` | Cached microsandbox release archive. Default: `~/.oac/cache/microsandbox-v0.7.2-linux-x86_64.tar.gz`, downloaded when missing | | `CORE_DISTRIBUTION_DATABASE_IMAGE` | PostgreSQL 16 image; the default is pinned by its linux/amd64 manifest digest | -The build reuses the Core, Web, Runtime, SDK and helper builders. The manifest records the commit and source tree, image config and OCI manifest digests, the Runtime OCI manifest digest, the microsandbox runtime and firmware hashes, and the size and hash of every downloadable artifact. Output is the control archive and its `.sha256`, the optional offline archive, and the versioned Runtime, node and native installer assets. Nothing is published. Rebuilding into a directory that already holds this commit's distribution is refused. +The build reuses the Core, Web, Runtime, SDK and helper builders. The manifest records the commit and source tree, image config and OCI manifest digests, the Runtime OCI manifest digest, the microsandbox runtime and firmware hashes, and the size and SHA-256 of every Runtime and node artifact; native installers carry only their SHA-256 in the [catalog](#native-installers). Output is the control archive and its `.sha256`, the optional offline archive, and the versioned Runtime, node and native installer assets. Nothing is published. Rebuilding into a directory that already holds this commit's distribution is refused. -The control archive carries no Runtime image or node execution artifacts. Nodes fetch them from the Web that generated their command, which serves a local copy or redirects to the release base; the offline archive carries them instead. The [download contract](../deploy/install/README.md#download-contract) owns these rules. +The control archive carries no Runtime image or node execution artifacts; the offline archive carries them. The [download contract](../deploy/install/README.md#download-contract) describes how nodes obtain them. A distribution carries the docs listed in `BUNDLED_DOCS` in `scripts/core-distribution-manifest.py`. Links between bundled docs stay relative; every other relative link is rewritten to the same file on GitHub at the bundle's commit. The build fails when a link or anchor does not resolve, and `make check-distribution` runs the same check on the repository. Update the list when you add or move a doc that the installer or its output refers to. ### Native installers -Self-hosted machines install `oac-daemon` from per-platform native installers: Linux amd64, macOS arm64 and Windows amd64. Each is built on its own OS by the `native-check` workflow (`scripts/build-native-installer.mjs`, whose `pins` object fixes the Node.js and Harness versions) and uploaded as `oac-native-installer--.tar.gz`. For a local distribution, download the three artifacts from the native run for the same commit, then assemble the catalog from that checkout: +Self-hosted machines install `oac-daemon` from per-platform native installers: Linux amd64, macOS arm64 and Windows amd64. Each is built on its own OS by the `native-check` workflow (`scripts/build-native-installer.mjs`, whose `pins` object fixes the Node.js and Harness versions) and uploaded as `oac-native-installer--.tar.gz`. For a local distribution, download the three artifacts from a `native-check` run on that exact commit (a manual run or the release run; pull-request runs build the merge commit and do not match), then assemble the catalog from that checkout: ```sh node scripts/build-native-catalog.mjs INPUT_DIR OUTPUT_DIR export OAC_NATIVE_INSTALLER_BUILD_DIR=OUTPUT_DIR ``` -The catalog records the commit, the Runtime protocol version and each archive's checksum. The control archive and Core image carry only `native-installers/catalog.json`; the archives become separate `oac-native--.tar.gz` release assets, and the offline archive holds one copy of each outside the Core image. Without a catalog, Sessions report the install command as unavailable, and the release workflow refuses to publish. +The catalog records the commit, the Runtime protocol version, each archive's SHA-256 and, with a release base, its versioned URL. The control archive and Core image carry only `native-installers/catalog.json`; the archives become separate `oac-native--.tar.gz` release assets, and the offline archive holds one copy of each outside the Core image. Without a catalog, Sessions report the install command as unavailable, and the release workflow refuses to publish. ### Runtime images and helpers @@ -70,9 +70,9 @@ make build-claude-runtime docker build --platform linux/amd64 -t oac-runtime:claude "${OAC_DEV_HOME:-$HOME/.oac}/build/claude-runtime" ``` -The first step exports the adapter with the pinned Claude Agent SDK (`packages/claude-sdk-adapter/package.json`) as a checksummed archive; the second verifies it and adds the daemon. Keep the exported archive unchanged. +The first step exports the adapter with the pinned Claude Agent SDK (`packages/claude-sdk-adapter/package.json`) as a checksummed archive for the host platform; the second verifies it and adds the daemon. The image step needs the `linux-x64-glibc` archive, so build both on Linux x86_64 with glibc. Keep the exported archive unchanged. -**MiniMax Code Runtime image.** Build the companion from a checkout of the revision pinned in `packages/mcode-harness/source.json`, with the `@minimax-ai/code` npm package of the same version for native dependencies. It builds on Linux x86_64 or macOS arm64 into a new directory: +**MiniMax Code Runtime image.** Build the companion from a checkout of the revision pinned in `packages/mcode-harness/source.json`, with the `@minimax-ai/code` npm package of the same version for native dependencies. The companion build runs on Linux x86_64 or macOS arm64 into a new directory; for the Linux Runtime image, build it on Linux x86_64 (macOS arm64 serves only the native installer): ```sh MCODE_NATIVE_SOURCE=/absolute/minimax-code \ @@ -84,7 +84,7 @@ docker build --platform linux/amd64 -t oac-runtime:mcode "${OAC_DEV_HOME:-$HOME/ `scripts/prepare-release-runtimes.sh` runs the companion build from the pins. -The distribution combines the three Harness images into one Runtime image (`deploy/distribution/Runtime.Dockerfile`) that contains the daemon, the shared helpers and the three native Harness packages. It verifies that each image carries the daemon built from the same commit. +The distribution combines the three Harness images into one Runtime image (`deploy/distribution/Runtime.Dockerfile`): the MiniMax Code image, which carries the daemon, with the Codex executable and resources and the Claude SDK bundle copied in. It verifies that each image carries the daemon built from the same commit. **E2B helper.** @@ -109,9 +109,9 @@ The helper is written to `~/.oac/build/microsandbox-provider/oac-microsandbox-pr `make build-agents-api` builds `oac-core`, `oac-core-migrate`, `oac-core-device`, `oac-core-environment-key` and `oac-node` into `${OAC_DEV_HOME:-$HOME/.oac}/build/oac-core` (`OAC_DEV_CORE_BUILD_DIR` selects another absolute directory). The build copies only the source set listed in `scripts/build-agents-api.sh` (the Core service, its contracts, the shared packages it needs and the root Go module files) into a temporary context and builds with CGO disabled, read-only modules and trimmed paths. It needs no Node, Docker or other application. When Core gains a shared dependency, add that package to the list; never copy the whole repository to make it compile. -`make docker-build-agents-api` builds the image `oac-core:dev` (`OAC_DEV_CORE_IMAGE` selects another name) from those five commands and the E2B helper. The base is the digest-pinned `debian:bookworm-slim` with CA certificates and the glibc runtime the helper needs; the default user is UID/GID 65532 and Core listens on `:8091`. The image is Linux amd64 only and is not pushed to a registry. Changes to the image or its build need `make check-agents-api-container` in addition to `make check`: it runs the official-client suite against the image with a read-only root filesystem and needs Linux Docker, a non-root user, `OAC_TEST_OFFICIAL_SDK_PYTHON` and a dedicated `OAC_TEST_DATABASE_URL`. +`make docker-build-agents-api` builds the image `oac-core:dev` (`OAC_DEV_CORE_IMAGE` selects another name) from those five commands and the E2B helper. The base is the digest-pinned `debian:bookworm-slim` with CA certificates and the glibc runtime the helper needs; the default user is UID/GID 65532 and Core listens on `:8091`. The image is Linux amd64 only and is not pushed to a registry. Changes to the image or its build need `make check-agents-api-container` in addition to `make check`: it runs the official-client suite against the image with a read-only root filesystem and needs Linux Docker, a non-root user, and the [test database and pinned SDK](../services/agents-api/README.md#official-client-verification) of the service checks (`OAC_TEST_DATABASE_URL` naming an `oac_*_tests` database with the migrations applied, and `OAC_TEST_OFFICIAL_SDK_PYTHON`). -`make build-agents-api-release` packages the same five commands into `oac-core--linux-amd64.tar.gz` and its `.sha256` under `~/.oac/build/oac-core-release` (`OAC_DEV_RELEASE_DIR`). The archive holds the [archive README](../services/agents-api/RELEASE.md), the license, `manifest.json` (commit, tree, platform, Go version, upstream protocol and binary hashes) and `SHA256SUMS`. The build needs clean committed source and Python 3.9 or newer, and packages deterministically. It carries no configuration, credentials, Web or Runtime. Test archive changes by extracting a fresh copy and running its commands. +`make build-agents-api-release` packages the same five commands into `oac-core--linux-amd64.tar.gz` and its `.sha256` under `~/.oac/build/oac-core-release` (`OAC_DEV_RELEASE_DIR`). Beside `bin/`, the archive holds the [archive README](../services/agents-api/RELEASE.md), the license, `manifest.json` (commit, tree, platform, Go version, upstream protocol and binary hashes) and `SHA256SUMS`, which lists every packaged file. The build needs clean committed source and Python 3.9 or newer, and packages deterministically. It carries no configuration, credentials, Web or Runtime. Test archive changes by extracting a fresh copy and running its commands. ## Publish a version @@ -128,9 +128,9 @@ The workflow runs three jobs on the tagged commit: `check` (the full `make check `install.sh` resolves the latest stable release once, or the release named by `--version`, verifies the control archive and runs that bundle's installer; the [installation guide](getting-started/install.md#install) covers its use. -The jobs share Go module and build caches under `~/.oac/cache/`, keyed by runner OS and architecture, the Go module files and the commit. An older cache only seeds downloads and compilation; every check still runs. New keys are saved only after a successful job. +The `check` and `build` jobs share Go module and build caches under `~/.oac/cache/`, keyed by runner OS and architecture, the Go module files and the commit. An older cache only seeds downloads and compilation; every check still runs. New keys are saved only after a successful job. -Never move a release tag or overwrite published assets. If the `release` job fails, inspect the Release first: publication may have completed despite a lost response. Leave a complete published Release as it is. For an incomplete draft, fix or delete only that draft, then rerun the failed `release` job, which reuses the original Actions artifact. Do not rerun the build or recreate the tag to recover a failed upload. +Never move a release tag or overwrite published assets. If the `release` job fails, inspect the Release first: publication may have completed despite a lost response. Leave a complete published Release as it is. For an incomplete draft, delete that draft (the job refuses any existing Release or draft for the tag), then rerun the failed `release` job, which reuses the original Actions artifact. Do not rerun the build or recreate the tag to recover a failed upload. ### Build a candidate without publishing @@ -144,7 +144,9 @@ gh workflow run core-release --repo MiniMax-AI/parsar-core --ref main \ With `draft_release=true` the result is an unpublished `build-` draft Release; with `draft_release=false` the files stay in the Actions artifact. Use the exact matched asset set; never mix builds or resolve components through `latest`. -`scripts/promote-qualified-release.py` qualifies such a draft on a supervised host and publishes it; its module docstring states the rules. +### Promote a qualified candidate + +`scripts/promote-qualified-release.py` qualifies a candidate on a supervised host and publishes it once main reaches the reviewed promotion commit. Pass the candidate's flat files, built for the `build-` release base: the thin and offline archives and the native installers, each with its `.sha256`, and the Runtime and node assets. Take them from a local build with that release base and `CORE_DISTRIBUTION_OFFLINE=1`, or from the Actions artifact of a manual `core-release` run with `draft_release=false` after removing `install.sh` and `install.sh.sha256`. The command creates the draft Release itself and refuses any other file, so a draft created by `draft_release=true` cannot be promoted. Its module docstring lists the inputs, the qualification stages and the publication checks. ## Continuous integration @@ -156,7 +158,7 @@ With `draft_release=true` the result is an unpublished `build-` draft | `actionlint` | Changes to workflows | Workflow syntax | | `core-release` | Version tags and manual runs | See [Publish a version](#publish-a-version) | -Documentation-only and unrelated Web changes do not start `native-check`. A newer `core-check`, `api-acceptance` or `native-check` run on the same branch or pull request cancels the older one. +Changes limited to Web or to documentation outside `contracts/agents-api` do not start `native-check`. A newer `core-check`, `api-acceptance` or `native-check` run on the same branch or pull request cancels the older one. ## Run Core without the installer @@ -182,6 +184,6 @@ docker run --name oac-core --detach --read-only \ curl --fail http://127.0.0.1:8091/healthz ``` -`--user` lets the container read the key digest file as your non-root host user; alternatively grant UID 65532 read access and omit it. Put a TLS reverse proxy in front for remote clients. `/healthz` reports liveness only. All state is in PostgreSQL, so the container needs no writable volume; stop and start it with `docker stop` and `docker start`, and never remove the database to replace it. One Core process serves each database; replicas add no availability. After startup, use the Core key with the [administrator API](../contracts/agents-api/admin-api.md) to create Projects and issue application keys. +`--user` lets the container read the key digest file as your non-root host user; alternatively grant UID 65532 read access and omit it. Put a TLS reverse proxy in front for remote clients. `/healthz` reports liveness only. Keep credentials out of the image. All state is in PostgreSQL, so the container needs no writable volume; stop and start it with `docker stop` and `docker start`, and never remove the database to replace it. One Core process serves each database; replicas add no availability. After startup, use the Core key with the [administrator API](../contracts/agents-api/admin-api.md) to create Projects and issue application keys. The image also contains `oac-core-device` for an [internal execution device](../services/agents-api/README.md#internal-execution-device-connection) and `oac-core-environment-key`, the [break-glass credential command](../contracts/agents-api/environment-executor-credentials.md#break-glass-command). diff --git a/scripts/promote-qualified-release.py b/scripts/promote-qualified-release.py index 1875a5464..c4dbd137f 100644 --- a/scripts/promote-qualified-release.py +++ b/scripts/promote-qualified-release.py @@ -1,17 +1,25 @@ #!/usr/bin/env python3 -"""Qualify one draft candidate on a supervised host, then publish it after its batch lands. +"""Qualify one candidate on a supervised host, then publish it once its promotion commit lands. One invocation owns one explicit candidate and one qualification run, using the -existing gh authentication and SSH. Build the candidate with a manual core-release -run (draft_release=true); this command never builds. +existing gh authentication and SSH. It never builds: pass the candidate's flat +files, and it creates the unpublished build- draft itself. Produce the files +with make build-core-distribution for that commit, with +CORE_DISTRIBUTION_RELEASE_BASE_URL set to +https://github.com/MiniMax-AI/parsar-core/releases/download/build-, +CORE_DISTRIBUTION_OFFLINE=1 and the native installer catalog, or take the Actions +artifact of a manual core-release run with draft_release=false and remove +install.sh and install.sh.sha256. A draft that core-release created holds +install.sh and cannot be promoted. Inputs - --source: the full candidate commit, never inferred from latest. It binds the archive manifests and the build- release tag. -- --assets: only the generated flat candidate files: the thin and offline - archives with their checksums, the Runtime assets and the native installers with - their checksums. Every archive member and asset hash is verified, and the thin - and offline manifests and native catalogs must match. +- --assets: only the candidate's flat files: the thin and offline archives with + their checksums, the Runtime assets and the native installers with their + checksums. Any other file, including install.sh, is refused. Every archive + member and asset hash is verified, and the thin and offline manifests and native + catalogs must match. - --qualification-package, --qualification-manifest-sha256: a separately reviewed private package and its manifest digest. The manifest fixes the complete file inventory, ordered Python commands, bounded stage timeouts and private path and @@ -19,19 +27,21 @@ the remote supervisor; candidate assets cannot select or replace it. Keep host-specific scripts, user names and credential paths out of this repository, and never put credential values in either manifest. -- --promotion-commit: the independently reviewed tooling commit. The local - promotion scripts must equal their bytes there. It may differ from the candidate - only in PROMOTION_FILES, and the Makefile only by registering the promotion and +- --promotion-commit: the independently reviewed tooling commit; its tree is the + one main must reach. It must descend from the candidate, and the local promotion + scripts must equal their bytes there. It may differ from the candidate only in + PROMOTION_FILES, and the Makefile only by registering the promotion and control-channel tests. Product changes or a different main tree block promotion. - --host, --remote-root: an existing SSH host alias and an isolated remote parent. - --state: a new private local evidence directory; a previous one is never reused. -- --merge-wait-seconds: how long to wait for the batch to land, at most seven days. +- --merge-wait-seconds: how long to wait for main to reach the promotion commit's + tree; one day by default, at most seven days. Flow 1. Check the qualification adapter, the tooling bytes, the candidate files and - the source tree. Create the build- draft when none exists; refuse a - published Release, a draft for another commit or a conflicting tag. Download - every asset and compare its bytes. + the source tree. Refuse a conflicting tag, a published Release or a draft for + another commit, and create the build- draft with these files when none + exists. Download every asset and compare its bytes. 2. Copy the assets and the package to a fresh /. The reviewed adapter supervises fresh-install, current-lifecycle, managed-native-smoke, diagnostics-observations-smoke and node-runtime-smoke in that order: one fresh @@ -41,11 +51,13 @@ return this run's identity and its own owned resources; a supplied pass file, skipped check or old report never releases the candidate. Candidate bytes are verified again after the stages. -3. In the same process, wait until main's tree equals the promotion commit's tree. - A main that is behind waits; a conflicting main fails at once. Expiry or - cancellation keeps the evidence and grants no later permission to publish. -4. Recheck main, the tree, the tag and the draft ID, download every asset again, - then publish that Release ID (never a fresh tag lookup) as the latest release, +3. In the same process, wait until main's tree equals the promotion commit's tree + and main contains the candidate. A main that is behind waits; a conflicting + main fails at once. Expiry or cancellation keeps the evidence and grants no + later permission to publish. +4. Check the tag and that the same draft ID is still an unpublished draft, and + download every asset again. Then recheck main, the tree, the tag and the draft + ID, publish that Release ID (never a fresh tag lookup) as the latest release, and download once more to check the published bytes. The SSH stdin channel carries the request and then heartbeats; EOF, timeout, @@ -58,9 +70,10 @@ as live qualification. Never overwrite conflicting assets. Reconcile an interrupted run before invoking -again; stored results are evidence, not permission to publish. Never infer batch -membership from open pull requests or merge them from this command. No runner, -background service, GitHub secret or repository visibility change is needed. +again; stored results are evidence, not permission to publish. The command never +merges pull requests or decides what lands on main; it only waits for main. No +runner, background service, GitHub secret or repository visibility change is +needed. """ import argparse @@ -503,7 +516,7 @@ def promote(assets, state, host, remote_root, promotion_commit, *, source, packa def main(): - parser = argparse.ArgumentParser(description=__doc__) + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("--source", required=True, help="Full candidate source commit; never inferred from latest") parser.add_argument("--qualification-package", required=True, type=pathlib.Path, help="Separately reviewed private package directory, containing manifest.json") diff --git a/services/agents-api/RELEASE.md b/services/agents-api/RELEASE.md index b76b28cf9..308933236 100644 --- a/services/agents-api/RELEASE.md +++ b/services/agents-api/RELEASE.md @@ -43,5 +43,5 @@ export OAC_PUBLIC_URL=http://127.0.0.1:8091 ## Next steps - Use the Core key with the [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/admin-api.md) to create a Project and issue its API key, then follow the [quickstart](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/quickstart.md). -- To run Sessions on your own machines, follow the [self-hosted guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/self-hosted.md). The one-command installation needs this release's native installer catalog in `OAC_NATIVE_INSTALLER_DIR`. The [executor credential contract](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) covers issuing credentials with the Core key and `oac-core-environment-key`. +- To run Sessions on your own machines, follow the [self-hosted guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/self-hosted.md). The one-command installation needs the native installer catalog of the same commit: set `OAC_NATIVE_INSTALLER_DIR` to the `native-installers` directory of that commit's Core distribution, as the [configuration reference](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/configuration.md#appendix-core-environment-without-the-installer) describes. The [executor credential contract](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/contracts/agents-api/environment-executor-credentials.md) covers issuing credentials with the Core key and `oac-core-environment-key`. - To add sandbox nodes with `oac-node`, see the [node guide](https://github.com/MiniMax-AI/parsar-core/blob/@SOURCE_REVISION@/docs/getting-started/nodes.md). From e6419db59a959641d10b5d8e721c2813df32c5a5 Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Wed, 30 Sep 2026 07:29:58 +0000 Subject: [PATCH 8/8] docs: fold the release publisher's repository resolution into the maintainer guide --- docs/maintainers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maintainers.md b/docs/maintainers.md index 6bab30466..af2df5e8f 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -124,7 +124,7 @@ git push origin v1.2.3 Tags use `vMAJOR.MINOR.PATCH`, optionally with a prerelease suffix such as `-rc.1` and build metadata such as `+build.1`. A prerelease suffix creates a GitHub prerelease. Pushing the tag is the release decision. Automated checks establish build and test results, not real-model qualification: assess live execution evidence before you push the tag. Model credentials and private certificate authorities never enter CI or release inputs, including acceptance images that contain them. -The workflow runs three jobs on the tagged commit: `check` (the full `make check` workflow), `native` (the `native-check` matrix) and `build`, which starts after `native` succeeds. `build` prepares the pinned Runtime inputs, assembles the native catalog and builds the distribution with the offline archive, and adds `deploy/install-release.sh` as `install.sh` with its checksum. The `release` job runs only after `check` and `build` succeed. It is the only job with `contents: write`. It verifies the archive checksums and the native installer checksums against the catalog, refuses an existing Release or draft for the tag, uploads everything to a new draft, confirms the tag still points at the built commit, and publishes that draft by its ID. Images ship as archives; no registry is pushed. Downloads are anonymous. +The workflow runs three jobs on the tagged commit: `check` (the full `make check` workflow), `native` (the `native-check` matrix) and `build`, which starts after `native` succeeds. `build` prepares the pinned Runtime inputs, assembles the native catalog and builds the distribution with the offline archive, and adds `deploy/install-release.sh` as `install.sh` with its checksum. The `release` job runs only after `check` and `build` succeed. It is the only job with `contents: write`. It verifies the archive checksums and the native installer checksums against the catalog, resolves the repository's current name from GitHub before any write (Actions can keep an old name after a rename), refuses an existing Release or draft for the tag, uploads everything to a new draft on `uploads.github.com` bound to that draft's ID without retrying failed uploads, confirms the tag still points at the built commit, and publishes that draft by its ID. Images ship as archives; no registry is pushed. Downloads are anonymous. `install.sh` resolves the latest stable release once, or the release named by `--version`, verifies the control archive and runs that bundle's installer; the [installation guide](getting-started/install.md#install) covers its use.