Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
620 changes: 310 additions & 310 deletions contracts/agents-api/core.openapi.yaml

Large diffs are not rendered by default.

104 changes: 52 additions & 52 deletions contracts/agents-api/runtime.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,27 @@ definitions:
executor_token:
type: string
type: object
sandbox.DeploymentSpec:
properties:
resources:
$ref: '#/definitions/sandbox.Resources'
runtime:
$ref: '#/definitions/sandbox.RuntimeRelease'
type: object
sandbox.Resources:
properties:
cpus:
type: integer
environment_disk_mib:
type: integer
memory_mib:
type: integer
root_disk_mib:
type: integer
type: object
sandbox.RuntimeRelease:
deployment.Enrollment:
properties:
firmware_sha256:
backend_fingerprint:
type: string
image_id:
core_url:
description: The Core origin this node stores and connects to, such as https://core.example. It must equal the installation public URL; otherwise enrollment gets 409 sandbox_node_address_mismatch and the token stays unused.
type: string
image_manifest_digest:
credential:
type: string
microsandbox_ref:
deployment_generation:
type: integer
name:
type: string
runtime_sha256:
node_id:
type: string
source_commit:
provider:
type: string
specification_digest:
type: string
type: object
store.RuntimeNodeConfiguration:
deployment.NodeConfiguration:
properties:
core_url:
type: string
Expand All @@ -86,27 +73,7 @@ definitions:
specification_digest:
type: string
type: object
store.RuntimeNodeEnrollment:
properties:
backend_fingerprint:
type: string
core_url:
description: The Core origin this node stores and connects to, such as https://core.example. It must equal the installation public URL; otherwise enrollment gets 409 sandbox_node_address_mismatch and the token stays unused.
type: string
credential:
type: string
deployment_generation:
type: integer
name:
type: string
node_id:
type: string
provider:
type: string
specification_digest:
type: string
type: object
store.RuntimeNodeIdentity:
deployment.NodeIdentity:
properties:
deployment_generation:
type: integer
Expand All @@ -123,7 +90,7 @@ definitions:
specification_digest:
type: string
type: object
store.RuntimeNodeStatus:
deployment.NodeStatus:
properties:
connected:
type: boolean
Expand All @@ -144,6 +111,39 @@ definitions:
specification_digest:
type: string
type: object
sandbox.DeploymentSpec:
properties:
resources:
$ref: '#/definitions/sandbox.Resources'
runtime:
$ref: '#/definitions/sandbox.RuntimeRelease'
type: object
sandbox.Resources:
properties:
cpus:
type: integer
environment_disk_mib:
type: integer
memory_mib:
type: integer
root_disk_mib:
type: integer
type: object
sandbox.RuntimeRelease:
properties:
firmware_sha256:
type: string
image_id:
type: string
image_manifest_digest:
type: string
microsandbox_ref:
type: string
runtime_sha256:
type: string
source_commit:
type: string
type: object
v1.APIError:
properties:
code:
Expand Down Expand Up @@ -268,7 +268,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/store.RuntimeNodeConfiguration'
$ref: '#/definitions/deployment.NodeConfiguration'
"400":
description: Bad Request
schema:
Expand Down Expand Up @@ -305,14 +305,14 @@ paths:
name: body
required: true
schema:
$ref: '#/definitions/store.RuntimeNodeEnrollment'
$ref: '#/definitions/deployment.Enrollment'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/store.RuntimeNodeIdentity'
$ref: '#/definitions/deployment.NodeIdentity'
"400":
description: Bad Request
schema:
Expand Down Expand Up @@ -357,7 +357,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/store.RuntimeNodeStatus'
$ref: '#/definitions/deployment.NodeStatus'
"400":
description: Bad Request
schema:
Expand Down
2 changes: 1 addition & 1 deletion contracts/agents-api/sandbox-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ Some fields keep one name across providers but differ in meaning, or do not appl
| 409 | `sandbox_deployment_conflict` | Another state the change cannot apply to |
| 409 | `runtime_node_in_use` | Node removal while it holds resources |
| 503 | `execution_unavailable` | Provider preparation is unavailable |
| 503 | `sandbox_credential_unavailable` | The credential encryption key is unavailable |
| 503 | `credential_storage_unavailable` | Core has no credential encryption key |

Storage and credential failures stay errors: an empty or failed read never proves cleanup. The [machine connection API](machine-api.md#node-route-errors) lists the errors of the node routes.

Expand Down
2 changes: 1 addition & 1 deletion deploy/install/config_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def lookup(config, key):

# Checks named by x-oac.check. Core stays the authority for its own semantic rules.
def _origin(value, https_only=False):
"""Core's ValidateSandboxCoreURL rule, through the installer's one implementation of it."""
"""Core's deployment.ValidateCoreURL rule, through the installer's one implementation of it."""
from configuration import valid_core_origin # configuration imports this module at load time
return valid_core_origin(value) and (not https_only or value.startswith("https://"))

Expand Down
4 changes: 2 additions & 2 deletions deploy/install/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@


def valid_core_origin(value):
"""Accept exactly the origins Core's ValidateSandboxCoreURL accepts
(services/core/internal/store/sandbox_deployment_setup.go), so an
"""Accept exactly the origins Core's deployment.ValidateCoreURL accepts
(services/core/internal/deployment/public_url.go), so an
installer value never fails Core's OAC_PUBLIC_URL check at startup."""
if not isinstance(value, str) or any(char in value for char in "?#@\\% \t\r\n"):
return False
Expand Down
2 changes: 1 addition & 1 deletion packages/agents-client/src/sandbox-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import nodeDiagnosticFixture from "../../../services/core/internal/sandbox/testd

function response(value: unknown, status = 200) { return new Response(JSON.stringify(value), { status }); }

// Shapes as Core serializes them (store.RuntimeNode, RuntimeNodeDetail, RuntimeNodeAllocation, RuntimeDeploymentView).
// Shapes as Core serializes them (deployment.Node, deployment.NodeDetail, store.RuntimeNodeAllocation, deployment.View).
const created = "2026-09-25T08:00:00.123456789Z";
/** A ready node: Core omits `diagnostic`. */
const node = {
Expand Down
2 changes: 1 addition & 1 deletion packages/agents-client/src/sandbox-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ function invalidSandboxResponse(): never {

/**
* The object has every required member, optional ones only where listed, and nothing else. The projections below
* follow Core's store.RuntimeDeploymentView, RuntimeNode, RuntimeNodeDetail and RuntimeNodeAllocation serialization.
* follow Core's deployment.View, deployment.Node, deployment.NodeDetail and store.RuntimeNodeAllocation serialization.
*/
function members(value: unknown, required: readonly string[], optional: readonly string[] = []): Record<string, unknown> {
if (!isRecord(value) || !required.every((field) => hasOwn(value, field)) || !onlyFields(value, new Set([...required, ...optional]))) return invalidSandboxResponse();
Expand Down
7 changes: 4 additions & 3 deletions services/core/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Domain owners, each with its PostgreSQL adapter under `internal/persistence/post
- `environmenttemplates` (`templatepg`): Environment Templates, their validation and default network, their sealed setup, initial files, Skills and Plugins, and the resolved Template that Session creation composes into its Environment.
- `modelconfiguration` (`modelconfigurationpg`): each Harness's deployment default model configuration and its last-use observations.
- `skills` (`skillpg`): Skills and their immutable versions: archive checks, the default and latest pointers, version selection and deletion, and each version's sealed archive. Session creation freezes selected versions inside its `store` transaction with the `skills` rules.
- `deployment` (`deploymentpg`): the sandbox deployment and its nodes: provider configuration and the sealed credential, the specification and retained generations, setup, update and switch, node enrollment, identity and authentication, generation configuration, capacity, presence, status and host history. It interprets Sandbox Provider declarations through the `providers.Registry` it is given, whose lookups return typed errors. `cmd/server` builds that registry and calls it only to build a direct Provider and to discover a Provider's configuration; it takes each setup's mode and declared operations from the `Setup` that `deployment` returns. The only other reader is `store`, whose own `providers.Builtin()` validates the stored specification at Session admission and reads the public-origin requirement at placement. Deployment changes run through `deployment.ExecutionOperations` on `deploymentpg.NewExecution(lease, …)`, which the Worker receives as `execution.Owner.Deployment`; the Worker reads the deployment and prepares a selection's setup through the pooled `deployment.Service` in `execution.Dispatcher.Deployment`, and node management and reads use the pooled `deploymentpg.Store`, which `cmd/server` also reads the owner epoch from and runs the host-history sampler on. Provider calls run outside transactions, and the final transaction rechecks the expected generation. Allocations, placement and reset stay in `store`.

## Request handling

Expand Down Expand Up @@ -88,7 +89,7 @@ A dedicated self-hosted device is bound to exactly one Environment's Session and

Connection observations use the execution lease and the Session lock. A separate `environment_connections` row holds the current generation and revision, and `environments.status` commits together with its Session Environment event. The producer serializes replacements and numbers socket observations within each generation; duplicate or older revisions and superseded generations are inert, and a replacement retires the previous connected observation before registering the new one. Registration alone creates no `connected` event. Event payloads carry only public Environment identity, type, status and nullable error, never configuration, credentials, registration IDs or revisions, and have no Turn association. `connected` and `disconnected` are distinct from native readiness: never cast resource `expired` into this vocabulary or emit `ready` for a self-hosted connection. On restart the Worker reconciles old observations before admitting new ones, and a failed or stale observation never establishes a connection.

The sandbox node Hub's global mutex protects only in-memory connection state. Authentication, ownership and store callbacks run synchronously outside it, respect cancellation and have five seconds; database writes are never detached. Closing the Hub cancels opening and live connections without waiting for database callbacks, and each node reservation lasts until its fenced disconnect cleanup finishes. Presence is registered in an explicit transaction, so a canceled statement cannot publish it later through autocommit. Disconnect cleanup first locks the node row, then applies the connection and epoch fence with a fresh READ COMMITTED statement so an in-flight commit is not missed. These transactions never take the deployment-wide manager lock, and online-state writes compare the handshake epoch atomically so a stale Core cannot publish readiness for a new owner.
The sandbox node Hub's global mutex protects only in-memory connection state. Authentication, ownership and `deployment` callbacks run synchronously outside it, respect cancellation and have five seconds; database writes are never detached. Closing the Hub cancels opening and live connections without waiting for database callbacks, and each node reservation lasts until its fenced disconnect cleanup finishes. `deploymentpg` registers presence in an explicit transaction, so a canceled statement cannot publish it later through autocommit. Disconnect cleanup first locks the node row, then applies the connection and epoch fence with a fresh READ COMMITTED statement so an in-flight commit is not missed. These transactions never take the deployment-wide manager lock, and online-state writes compare the handshake epoch atomically so a stale Core cannot publish readiness for a new owner.

## Sessions, Turns and input

Expand Down Expand Up @@ -183,15 +184,15 @@ At startup the Worker fails previously claimed work, keeps queued input and neve

The [managed lifecycle](../../docs/sandbox-provider.md#managed-lifecycle) describes publication, allocation, per-node workers, placement, suspension, reset and archive; the [sandbox node protocol](../../contracts/agents-api/node-generation-protocol.md) the node connection. In code:

- Each allocation's immutable specification and the current credential resolve in one snapshot, with no stale-credential cache, no fallback to the current specification and no unloading of a provider map that retained allocations need.
- Each allocation's immutable specification and the current credential resolve in one `deploymentpg` snapshot, with no stale-credential cache, no fallback to the current specification and no unloading of a provider map that retained allocations need.
- Credential replacement fences provider calls and waits for helper subprocesses to exit, even after cancellation, before verifying again and committing; the audit entry never carries secret fields.
- Observations of offline, missing or unconfirmed resources persist only bounded, sanitized codes, separately from the lifecycle, and a host restart never fabricates a running observation.
- Runtime observation of a node allocation goes through its immutable placement as one bounded read-only Provider operation; an absent capability or transport returns unavailable, never a Core-local fallback.

## Core administration errors, metrics and write provenance

- Core error details are scoped by the `/core/v1` router's writer mark, never by a request path test. Write Core errors with `writeCoreError` and typed `CoreErrorDetails` values (string, number, null and string-array constructors); invalid or empty details are omitted as a whole. The mark preserves error observation, flushing and `http.ResponseController` access. A shared handler or a Core-looking path alone never changes a public or machine error envelope. Core authentication runs before operation configuration checks, and unknown paths keep their status and admission rules. When adding a code with details, document its fixed keys in `contracts/agents-api/core-errors.md`, and pass only safe Core-owned facts: never submitted values, secrets, native text or provider bodies.
- Operation validators keep their original error text, sentinel identity and validation precedence. Package-owned typed errors carry fixed field metadata; only the marked Core error mapper translates it into operation codes and safe bound or catalog details. Sandbox validation metadata travels through its store wrapper without changing transaction or provider authority. Public Session provider validation stays byte-for-byte unchanged; cover it with handler-level golden responses. The Core clients ignore malformed optional details and never retry a write.
- Operation validators keep their original error text, sentinel identity and validation precedence. Package-owned typed errors carry fixed field metadata; only the marked Core error mapper translates it into operation codes and safe bound or catalog details. Sandbox validation metadata travels through `deployment.ConfigurationError` without changing transaction or provider authority. Public Session provider validation stays byte-for-byte unchanged; cover it with handler-level golden responses. The Core clients ignore malformed optional details and never retry a write.
- Core metrics instrument the existing worker and job owners without changing scheduling, lease or retention behavior. Count `execution_unavailable` at the HTTP error writer, once per rejected response; never capture request or response bodies and never infer the count from other 503s or failed Turns. Process CPU, RSS and cgroup limits are sampled by the 30-second Core metrics loop into the same bounded in-memory ring; the first CPU interval and restart gaps stay null, and host usage never substitutes for process usage. Root Turn history is queried read-only from PostgreSQL with native timestamps. Builds inject the source commit with `-ldflags` into `main.buildRevision`. Keep the response shape aligned with `packages/agents-client/src/core-metrics.ts`.
- Public resource writes carry the authenticated key's provenance separately from the execution principal. Record the operation and any creation ownership with `auditpg.RecordWriteAudit` in the business transaction, never in response middleware or an asynchronous queue; a failed record rolls back the write. Internal lifecycle and refresh work never acquires public provenance, and retries never replace ownership. Environment uploads persist the safe request origin before dispatch and record success with the confirmed Runtime receipt, not the native filesystem call. Never put payloads, paths or secrets in audit metadata. Do not confuse key identity with the Session creator identity used for retries.
- Administrator writes reuse the public resource deletion and serialization code and record their administrator audit entry with `auditpg.RecordAdminMutation`, or `RecordDeploymentMutation` for a deployment-wide write, in the same transaction. Administrator provenance takes precedence over a key's.
2 changes: 1 addition & 1 deletion services/core/cmd/provider-artifacts/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
func main() {
write := flag.Bool("write", false, "write generated declarations from the repository root")
flag.Parse()
catalog, err := providers.ArtifactCatalog()
catalog, err := providers.Builtin().ArtifactCatalog()
if err != nil {
panic(err)
}
Expand Down
Loading
Loading