These are the code-level rules of services/core that no contract states. Contracts own behavior: the coverage ledger and its linked contracts own the public and administrator APIs, the machine connection API the /api/v1 routes, the Core–Runtime protocol the daemon wire, and the Sandbox Provider guide the managed compute lifecycle. When code changes one of these rules, change the rule here in the same branch.
api.NewHandler takes one api.Dependencies value, built only in cmd/server. Each application area is one field typed as an interface declared in api beside its handlers, listing exactly the methods they call. Every field is required and NewHandler rejects a missing one, except the optional groups whose comments say what nil means: Execution is nil without an execution Worker, Sandboxes is nil without a managed sandbox installation and requires Execution, and Execution.NativeInstaller is nil for a build without a source revision. Handlers never discover a capability by type assertion or fall back to another implementation. API tests use one strict fake per area, fake<Area>, which fails the test on any call the test did not set.
internal/persistence/postgres/pgunit owns the PostgreSQL mechanics that adapters share: pooled read-write and snapshot transactions; pool-bound queries, used only for a single-statement read that needs no transaction, such as the per-request key lookup; the execution lease (its dedicated connection and gate, the ownership check, the cancellation fence, close, and the execution deadline); identifier parsing (ParseID, PathID, LookupCursor); and detection of text PostgreSQL cannot store (IsUnstorableText). Persistence code runs every transaction through it. Outside persistence and store, only cmd/server, which acquires the lease and builds the adapters' pool, and test fixtures import it. internal/persistence/postgres/pgtest is test support: it opens the dedicated test database under the oac_*_tests guard, applies the migrations, and creates isolated databases for database-wide state such as the execution lease. Only test files import it.
internal/persistence/postgres/auditpg is the one adapter that other adapters call directly. Audit rows are written inside the business transaction, so each adapter calls RecordWriteAudit, RecordAdminMutation or RecordDeploymentMutation with its own transaction's queries; the audit provenance travels in the context. writeaudit and adminaudit own the sources, their validation, ErrInvalidSource and the read models, and auditpg.Store serves the audit reads.
The adapter that stores a secret seals and opens it with the credential key cmd/server builds it with: domain storage interfaces carry plaintext, domain service constructors never take the key, a missing key is credentialcrypto.ErrUnavailable, and a ciphertext that fails to open or authenticate is an internal error, never a missing key, value or row.
Two errors are shared across domains, each with one api helper: textvalue.ErrUnstorable (400, writeTextValueError) for text PostgreSQL cannot store, and credentialcrypto.ErrUnavailable (503, writeCredentialUnavailableError) for a missing credential key. The audit ErrInvalidSource errors pass through adapters unchanged, and writeAuditSourceError maps both to 400.
Shared vocabulary has one owner each, and domains use it rather than copy it. internal/environmentconfig owns Environment setup, Skills, Plugins and initial files with their validation and public metadata; Setup.Validate checks requested configuration, where a Skill may be an unresolved reference, and Setup.ValidateInstalled checks frozen, installable configuration. internal/skills owns ParseVersion, the canonical positive decimal Skill version. internal/metadata owns the metadata rules: Validate for the pair, key and value limits and U+0000, ValidateStorable for U+0000 alone, and Encode with its 64 KiB bound. internal/jsonobject owns Normalize, the stable encoding of stored JSON objects that snapshots and retry identities compare. These packages import no persistence.
internal/sessions owns the Session vocabulary: Sessions, Turns, inputs, Environments and their provisioning failures, function calls, Item and Artifact reads, executor credentials, and the errors Session operations return, which api maps in writeSessionsError. It also owns the Session change vocabulary and its decisions: the public changes that report Turn and Session transitions, what a Turn that ends settles, measured Turn usage and the Session activity a change reports. Sequences that several Session operations share are sessions procedures, each over the transaction interface it declares: CancelTurn and CancelWork cancel work, TrackInputActivity reports the input activity a write changes, FailEnvironment and TerminateEnvironment settle an Environment that failed or whose managed compute ended, CreateEnvironmentDevice creates and binds a hosted Environment's dedicated Runtime device, CheckComputeAdmission, CheckFileWriteGate and CheckInputStart gate new work, AdmitFunctionResult saves a submitted function result for its Turn's call, and LoadRequiredActions lists the calls a Turn waits on. AppendTurnEvents records a batch of a Turn's execution observations, which NewJournalBatch validates, and AppendTurnEvent records its terminal outcome; both project the new entries. ProjectSource projects one journal entry or admitted input into Items, the root Turn's usage and Subagents, and ProjectInput projects an admitted input. ValidTransition decides the Turn status transitions, and the Subagent projection decides each Subagent's identity, lifecycle, child Turn and child Item changes. sessions.ExecutionOperations runs the Session writes only the execution owner makes; each function-call operation loads the Turn and its call in one leased Session transaction, decides in sessions, then applies in sessionpg, and AppendTurnEvents records and projects a Turn's journal batch in one leased Session transaction. internal/items owns public Items: it projects observations, merges them into stored Items and builds the ordered events that report each Item change. Neither imports persistence. internal/persistence/postgres/sessionpg loads the facts those decisions read and applies them inside the caller's Session transaction, under the Session lock: it allocates event sequence positions, event IDs, Item positions, output indexes and Subagent IDs, writes the change journal, each Turn's execution journal and its counters, Items, Turn usage, Subagent bindings, child Turns and child Items, and Artifact settlement, and prunes the change journal. It decides nothing. WithSession runs a Session operation's transaction on the pool or the lease: it locks the Session, applies the operation, then prunes the change journal. BindSession binds one Session to a transaction's queries as a SessionTx, which implements the procedures' interfaces.
Other adapters call only these sessionpg participants, on their own transaction's queries: LockSession, which orders a write against the Session's Turns; BindSession, which runs a Session procedure, such as CreateEnvironmentDevice, in that transaction; LoadEnvironment, which reads the tenant's Environment of a live Session; and CreateSessionEnvironment, which stores the Environment of a Session being created. The other sessionpg exports serve store until the Session operations leave it. tests/fixtures is test tooling that composes procedures over a pooled WithSession, which is why WithSession and BindSession stay exported after store goes; no production code may do this. deploymentpg may import sessionpg, and sessionpg never imports deploymentpg; placementpg, which loads the placement facts and reserves placements, sits below both, so either may call it and it imports neither. Likewise deployment may compose the sessions procedures, and sessions never imports deployment.
store is transitional. store.New builds a pooled Store, and store.NewExecution(s, lease) builds the execution writer on a lease it borrows. An execution-only operation on a pooled Store fails with store.ErrExecutionAuthority. New adapters do not copy that check: their execution repositories require a *pgunit.Lease at construction, their public repositories expose no execution operation, and the check goes away with store.
cmd/server owns the execution lease. It acquires one pgunit.Lease, builds every lease-bound adapter on it, and passes the lease and those adapters together as one execution.Owner to execution.StartWorker, which binds the Dispatcher to the Owner's execution operations through Dispatcher.Bind. If anything fails before that call, cmd/server closes the lease. From that call the Worker owns cleanup: a failed start closes the lease before it returns, and a started Worker closes it after Run has cancelled and drained its work. Each close runs under its own bounded deadline, independent of the cancelled request or run. Lease-bound adapters and the store writer borrow the lease and never close it, and the Worker uses the lease only through Owner.Lease, never through an adapter. Store integration tests start the Worker the same way through startWorker, and bind a Dispatcher that runs Turns without a Worker through Dispatcher.Bind.
Domain owners, each with its PostgreSQL adapter under internal/persistence/postgres:
agents(agentpg): saved Agents, their configuration merge and bounds, and the encrypted model-provider bundle bound to each Agent.files(filepg): source Files.projects(projectpg): Projects, Project API keys and key resolution for authentication.projectsvalidates Project and key names by rune count, separately from the byte limits on node names, and decides key issuance over the share-locked Project so an archive and an issuance never both commit.vaults(vaultpg): Vaults and Credentials, the encryption of Credential secrets, OAuth access-token refresh, and the MCP credential selection that Session creation freezes and the Dispatcher'sCredentialsresolves into a bearer token.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 itsstoretransaction with theskillsrules.deploymentand its subpackagedeployment/placement(deploymentpg,placementpg): 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, host history, reset, and the counts of nodes and sandboxes bound to the public address; and hosted runtime allocations: reservation with the dedicated device, compute ownership and settlement, observation diagnostics, activity, compute phases, wake receipts and cleanup, with the reads that schedule, discover and authorize them. It interprets Sandbox Provider declarations through theproviders.Registryit is given, whose lookups return typed errors.cmd/serverbuilds 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 theSetupthatdeploymentreturns.deployment/placementowns hosted admission and placement:cmd/serverbuilds oneplacement.Rulesfrom the registry and the public URL, both fixed while Core runs, and gives it todeployment.Serviceand tostore; its pure decisions admit a hosted Session, choose its node, and admit an allocation's reserved node and a restore on it, and its errors keep one status, code and message throughwriteStoreErrorandwriteDeploymentError.placementpgloads the facts those decisions read and applies them on the caller's transaction-bound queries; it has no Store or transaction runner. The only other reader isstore: Session creation decides admission and placement with those rules andplacementpginside its own transaction. Deployment changes and allocation writes run throughdeployment.ExecutionOperationsondeploymentpg.NewExecution(lease, …), which the Worker receives asexecution.Owner.Deployment. Each allocation write locks the owning Session, decides indeployment, applies indeploymentpgand prunes the Session's change journal in the same leased transaction; cleanup settles the Session through thesessionsprocedures on asessionpg.SessionTxbound to that transaction. The Worker reads the deployment, prepares a selection's setup and records live-compute activity through the pooleddeployment.Serviceinexecution.Dispatcher.Deployment, and lists the Sessions a reset still has to archive, schedules node lifecycles and reads allocations through the pooleddeployment.Readerinexecution.Dispatcher.DeploymentReader; a pooled read carries no lease, and the leased write that follows it rechecks the allocation's owner. Node management and reads use the pooleddeploymentpg.Store, whichcmd/serveralso 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. The reset's Session archive stays instore.sessions(sessionpg): Session use cases and reads as they leavestore. The pooledsessions.Serviceruns the use cases onsessionpg.Store, which implementssessions.Storage, and plain reads usesessions.Reader, whichsessionpg.Storealso implements, directly. So far these cover root Items and Subagents (their reads), Session Artifacts (their reads, deletion and the staging of a Turn's export), Environments (their reads, the initialization list and the frozen setup and initial files, whichsessionpg.Storeopens with the credential key), devices (their reads, creation, revocation, heartbeats and Runtime enrollment), executor credentials (authentication and a Project's credential state throughsessions.ExecutorCredentialReader, and issuance, rotation and revocation; the Core-key Project operations record their audit entry in the same transaction) and native installation authorization and claims, whose tokenssessionpg.Storesigns with the credential key.cmd/serverbuilds onesessionpg.Storewith the credential key and wires the Service and the Store intoexecution.Dispatcher.SessionsandSessionsReader, into the api fields, into Runtime enrollment and into the daemon gateway; the Worker stages Artifacts through the Service;cmd/environment-keybuilds one without the key for its credential commands. Function calls and their application receipts, the Turn execution journal, Environment initialization, connection observations and their reconciliation, Session device binding, and file-write reservation and settlement run throughsessions.ExecutionOperationsonsessionpg.NewExecution(lease), which the Worker receives asexecution.Owner.Sessions.
Every Agents API JSON route reads its body through readJSONObject before decoding, validation or lookup. The gate requires a JSON Content-Type, applies the route's body limit and rejects invalid UTF-8, malformed JSON (including unpaired surrogate escapes), repeated keys and non-object roots with the official messages; an empty body or null becomes {}. DELETE, multipart, Core extension and internal routes keep their own readers. Member names match exactly: decode request objects with decodeInputObject, or check inexactMember before another decoder, so encoding/json never matches a case variant.
Report a validation failure that has official evidence through the typed field error, which emits invalid_request_error with the observed param and message; keep other local codes until their official fields are sampled. Saved and inline Agent configuration pass one path-tracking validator of the pinned shapes before their parsers and harness admission; do not grow it into a JSON Schema engine. A malformed path identifier must produce exactly the response of a well-formed missing one on that route, including for invalid bodies, queries and storage availability: the handler passes it unchanged, the storage call resolves it with pgunit.PathID to the never-assigned maximum UUID, and the missing path runs, or reject it directly only where the lookup is the next check. An after cursor that does not resolve inside its already resolved parent, malformed ones included, returns that list family's observed error, and foreign and missing cursors stay identical. U+0000 is rejected explicitly only in metadata (metadata.<key>), by the metadata package; other stored strings rely on the PostgreSQL error mapping, so keep each request's writes in one transaction.
List queries reuse the shared parser and error serializer while keeping each family's limit bounds and error fields. The Environment Files list keeps its own path and cursor parsing but follows the same unknown-key and duplicate-key rules, and still rejects malformed query encoding that the shared lists drop. Change page bounds, cursor ownership or parent lookup order only with evidence for that family, and never reproduce an observed upstream server failure as compatibility behavior.
Requests are served on their canonical path and never redirected. api.CanonicalPaths wraps the complete server handler in both configurations (the daemon ServeMux and the API router alone), so every route group, middleware, authentication check and handler sees one path. It starts from the request's own spelling, never a path re-escaped from its decoded form: invalid bytes are percent-encoded, unreserved escapes decoded, empty and dot segments resolved with ServeMux semantics and the trailing slash kept, while other escapes such as %2F and %5C stay encoded; Path and RawPath stay consistent for chi and the ServeMux. Never route or authorize on a path outside that wrapper.
On the Beta group, the OpenAI-Beta check (exactly one agents=v1 value) runs before authentication, and authentication precedes every Beta handler, 404 and 405. The API router's own middleware, not the shared log middleware, sets a fresh X-Request-Id (also in the log context), OpenAI-Version, OpenAI-Processing-Ms and nosniff. HEAD runs GET routes, except that streaming, content-download, live directory, Runtime observation and Runtime history routes register an explicit HEAD 405. Every 405 of the API router, unknown methods included, has the JSON body and lists the route's methods in Allow.
- Stored strings other than metadata rely on PostgreSQL rejecting U+0000 and invalid UTF-8:
pgunit.IsUnstorableTextdetects SQLSTATE22021(text parameter) and22P05(\u0000in jsonb), and the adapter returnstextvalue.ErrUnstorable, the 400 unstorable-text error, including for query filters such asagent_id. The failing statement aborts its transaction, so keep each request's writes in one transaction. - An
aftercursor that cannot name a resource on a lookup list (Agents, Sessions, Turns, Templates, Vaults, Credentials) resolves throughpgunit.LookupCursorto the never-assigned maximum UUID and runs the normal lookup, so storage failures and missing rows behave as for a well-formed cursor. Resolve every cursor only inside its already resolved parent and tenant. - Lists whose parent and cursor lookups are separate statements (Artifacts in
sessionpg, and Skill versions inskills) re-check the parent before reporting a cursor 400, so a parent deleted in between still returns its 404.sessionpgreads Item and Subagent lists and their cursors inside one locked Session transaction.skillpglooks up a Skill version cursor tenant-wide soskillscan tell another Skill's version from a missing one; another tenant's version stays missing.
Source Files are Project resources with a lifecycle independent of copied workspace files. files owns their vocabulary, the upload envelope and 512 MiB content bound, the list rules and the create and delete use cases; filepg stores immutable source metadata and PostgreSQL large objects in Core's database with the pinned pgx driver. Upload validation, metadata insertion, the bytes and the write audit commit atomically; deletion removes the metadata, unlinks the object and records the audit in one transaction. Keep OIDs private and authorize every metadata, content and delete lookup by tenant before opening a body. Write content through pgunit's large-object writer, which streams bounded chunks and reports the size and SHA-256 digest; never hold an entire upload in memory or use a filename as a filesystem path. A direct download of the user_data purpose is rejected after the tenant-scoped metadata lookup, while workspace copies keep their authorized filepg read and Session initialization copies read the object inside Session creation's store transaction. A read-only repeatable-read transaction preserves an admitted source across concurrent deletion; resolve that snapshot before entering the Environment write path, and a later deletion never undoes a completed workspace copy. Bound request and transaction lifetimes, roll back incomplete bodies and never retry an ambiguous commit automatically. Backups must include PostgreSQL large objects, and a schema rollback must not orphan them.
Session Artifacts are immutable published copies, separate from live workspace files and source Files. The private output exporter reuses the authorized workspace path boundary and streams bounded bytes; publication requires complete capture and confirmed helper and transport success, not merely valid archive syntax. The daemon owns and drains the exporter's stdout pipe separately from child reaping, so pull-transport backpressure cannot consume the process-exit I/O deadline; after helper exit, each pipe read has one second, reset after consumer delays, which rejects inherited pipes that never close. Cancellation closes the owned reader and the dispatch consumer, then waits for the child. Never extract an output archive into Core's filesystem or hold the execution lease through a large transfer.
Capture bytes into private large objects without a Session admission lock. Before capture, seal native input under that lock with the private capture marker; later messages reuse the Environment input reservation and wait for the next Turn, and the public Turn stays in progress until publication settles. Directory reads during capture use an independent read-only preparation, not the released native Run. sessions.StageTurnArtifacts stages a confirmed export as a pooled use case, never on the execution lease: it authorizes the Environment before reading the export, admits only distinct regular files under outputs/ within the capture limits followed by nothing but zero padding, and after the transfer takes the Session lock through sessionpg.LockSession and rechecks that the Turn is in progress without a cancellation request. sessionpg streams each file into a large object in that one transaction, so a rejected or failed export rolls back its content. The metadata is published in the same transaction as the Turn's completion. sessions.EndTurn decides Artifact publication and sessionpg applies it in the Turn's terminal transaction, not the capture transaction: capture commits and releases the Session lock before the Turn completes, and the terminal transaction holds the Session lock that also orders Artifact deletion. Drop staged rows whose SHA-256 equals the newest remaining published Artifact for the path, ordered by the producing Turn's creation time and then ID (publication time can come from the Runtime and does not order Turns), and unlink their large objects in the same transaction. Published rows are never modified. Failed and cancelled Turns discard private objects, and Session deletion removes private and published copies. The exporter skips output symlinks by their lstat type without following them; hard links, other special files, device crossings and concurrent changes reject the capture. sessionpg serves stored reads authorized independently of Environment availability, so published Artifacts outlive the Environment, and reads content from one snapshot, so a read admitted before a deletion finishes. Deletion takes the Session lock, unlinks the content and records the write audit in one transaction.
- A malformed
environment_idArtifact filter resolves to the never-assigned maximum UUID, so it matches nothing without a text comparison.
- Environment file list tokens bind a digest of the tenant, Environment, requested directory, effective order and limit, plus a fingerprint of the full sorted regular-file path and size list and an offset that is a multiple of the limit. Every page rereads the directory; there is no cursor registry, cache or snapshot. Reject every token mismatch with the single official token message.
- The directory helper checks each requested path component with
Root.Lstatbelowos.OpenRoot(workspace)and opens the final directory withO_NOFOLLOW. A missing component, a regular file or a symbolic link maps to the distinctnot_directoryresult, which the daemon and gateway carry only for directory reads; Core turns it into an empty page.not_found(Claude SDK adapter reader), permission, transport and uncertain results keep their errors. - A Files.create write intent stores a digest of the path, size and content, not a path ledger, so Core cannot tell a file an earlier Files.create wrote from any other file; an existing regular file therefore gets the untracked-file message. Reserve the intent under the Session lock before dispatch. The daemon verifies the complete body's SHA-256 before calling the writer, and the writer creates parents with
Root.MkdirAll(0700), writes.oac-write-<uuid>in the workspace root and publishes it withRoot.Link, which never replaces an existing entry. Known refusals returnwrite_rejectedwithreasondestination_directoryorunsafe_destination; Core settles the intent asrejected, which leaves no committed receipt and releases the mutation owner. Only an exact committed or rejected receipt settles an intent; nothing settles an unknown one automatically. - Check the 5 MiB inline bound after path validation and Base64 decoding, and before the pending-hosted check, the source File lookup and execution. The JSON body limit still admits the Base64 form of 50 MiB so that oversized inline bodies up to that size get the official message.
skillpgserializes Skill version uploads, default changes and version deletion on the owning Skill row lock, andskillsdecides a version deletion over the locked Skill. Deleting the default version deletes the Skill only when no other version row exists, through the same cascade as Skill deletion, so every encrypted version row goes in the same commit.next_versiononly increases.
The leased Worker's common preparation scheduler owns Environment initialization, independently of any allocation; managed and enrolled connections use the same frozen snapshots and typed Runtime operations. Preparation has bounded concurrency separate from Turn scheduling, and a blocked Runtime never blocks cleanup or other connections. Check Harness availability before installing. A persisted running initialization whose owner is lost fails without replaying side effects; completion requires the same authorized Environment and device binding, and failure settles pending input while keeping compute ownership and user files. A connection observation never implies completion.
A committed input sends a coalesced hint to the Worker scheduler, which keeps its lease, capacity, cursor fairness and per-Session ownership checks; a hint admits nothing by itself. If capacity is occupied, keep one rescan for completion without turning a failed preparation into a busy retry loop. HTTP readiness waits and active input delivery subscribe before reading state, wake after committed promotion or input and recheck storage after each hint. Polling remains the fallback for external writers, expiry and lost hints; notifications carry no execution authority and no input data.
Core readiness, Start acknowledgement and input-to-first-text logs use one process monotonic clock; a Start acknowledgement confirms adapter ownership, not model input consumption. Runtime logs report Executor creation, reuse, idle and close separately from Turn completion. Never call these durations model latency or subtract clocks of different machines.
The Dispatcher prepares pending Environment input only on its exact enrolled or managed device, and keeps the same physical peer and preparation handle through readiness, atomic promotion and claim, and the first non-replay Start. Workspace and Environment identity come from store ownership, never caller-selected paths. The initial prompt and cursor come from the reserved batch; later messages use ordinary steering. Never hold a database lock during native preparation. While waiting for readiness, observe the original deadline, cancellation, deletion and peer loss. A preparation failure leaves pending input and its deadline intact unless storage settled it, and creates no failed Turn or input history. During Start, consume preparation controls alongside the Run stream so a control-only rejection or a pending-start cancellation settles promptly; once cancellation is sent, preparation errors cannot replace its receipt or timeout path, and a missing or unconfirmed outcome fails conservatively without a fabricated cancellation outcome. The connection owner spans preparation and the transferred Run, and every exit releases it.
The Worker scans pending inputs with the same scheduling slots, Session locks, durable deadlines and engine capability checks: once a second, at most 100 candidates per scan. At the end of the queue after a nonempty cursor it refills the first page once in the same scan, and an empty queue never spins. The cursor advances before readiness checks so an unavailable Runtime cannot starve later candidates. Execution concurrency (core.execution_concurrency) bounds simultaneous work, not attempt frequency, and the Worker alternates between ordinary Turns and Environment inputs. A self-hosted Session waits for its dedicated enrolled device; it never selects another device of the tenant or migrates a binding. Managed lifecycle polling keeps its separate five-second interval. Input HTTP response budgets follow the persisted Environment type, independently of operator switches.
OAC_HARNESSES adds deployment-supported engines to the default engine and the managed profiles; a daemon's heartbeat alone never enables an engine.
services/core/internal/runtimegateway is Core's daemon connection implementation; its persistence interfaces use services/core/internal/runtimedevice, and the frames and validators live in the shared internal/agentdaemon/proto. It is a single-process registry: connectivity comes from the live registry, never a persisted online flag, and last_seen_at is diagnostic only. Session-to-device bindings are tenant-scoped and immutable. Revocation denies new connections and binding reads at once, and an open connection closes at its next heartbeat.
A dedicated self-hosted device is bound to exactly one Environment's Session and is excluded from general device selection, even within the tenant. Enrollment creates or recovers the device and binding atomically under the Session lock; the frozen workspace and capability directories come from the Session configuration and must match the local binding. Core rechecks the persisted Environment and device binding for preparation and active reads; capability discovery never selects or authorizes a device for this placement.
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 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.
Turn writes serialize on the tenant-scoped Session row. Input requests are ordered batches committed under the same lock: a retry key identifies the complete batch, a changed length, order or content conflicts, and a failed transaction leaves no partial input or cancellation. A single-event request keeps its identity at batch position zero. Internal admission limits are 64 events and 512 KiB of payload per request; the API still validates the upstream event schema. Cancellation keeps its first target, including an idle no-op, so a retry never stops later work. Terminal states and outcomes are never overwritten.
Session deletion takes its decision and commits the deleted_at marker under the tenant Session lock that orders Turn and input admission, so either an admission commits first and deletion conflicts, or admission observes the deletion. Admission checks visibility under that lock before its retry lookup. Internal Turn, receipt, finalization and restart reconciliation keep access to deleted Sessions so that work settles under the execution lease; queued work of a deleted Session is never claimed, and Runtime cleanup still cancels pending work. Deletion never revokes a shared device or removes a saved Agent. Deleted Session records are retained, and a migration rollback refuses to drop the column while any exist.
Every new Session needs an explicit typed creator at the store boundary, internal callers included; public creation derives it from the authenticated principal only. Creator kind and ID persist in the creation transaction and never change on retry, update or source mutation. Both the early saved-reference recovery and the authoritative creation upsert require a matching creator before returning a Session or event cursor. Tenant scope always comes from the authenticated identity before any store call; metadata grants nothing. Tests supply explicit synthetic creators.
New saved-reference Sessions, and inline requests with Vault attachments or credential references, record a separate caller-intent hash: the source ID (empty for inline), supplied overrides with field presence, Environment and Vaults, original metadata and normalized initial input, excluding response streaming and resolved source values. Compare that same-tenant retry identity before looking up the source; a match returns the existing Session without input admission or source revalidation. Recheck after a source resolution failure for a concurrently committed creator, without holding a lock across resolution; the unique creation upsert stays authoritative. Credential-bound retries recover before reading mutable Vault contents. Every new hosted Session also records caller intent, before deployment defaults are resolved; the resolved-configuration hash of other inline Sessions leaves the deployment default out. Provider keys enter retry hashes only as keyed fingerprints.
Session listing by agent_id filters on the immutable root configuration.agent.id with the tenant, Agent and creation index, before pagination and activity projection.
Session status and last activity use the public projection in internal/api/session_response.go; Session diagnostics describes its failure precedence.
Reusable Agents are tenant-scoped rows independent of Session snapshots and engine bindings. agents accepts caller-validated configuration without applying harness restrictions or model defaults, with internal limits of 512 KiB for configuration and the metadata.Encode bound of 64 KiB for metadata. An update runs inside agentpg's Agent row lock: agents merges the supplied fields over the locked Agent and enforces the configuration bound, then agentpg commits configuration, metadata and update time together, so a stale full snapshot never overwrites another update. An empty update preserves the saved fields and advances updated_at through the same SQL update. Deletion is one tenant-scoped DELETE … RETURNING id. A Session copies the saved configuration into its immutable snapshot and never looks up its source again.
Saved execution defaults keep a model-provider bundle whole at every replacement boundary: endpoint, key, protocol and limits are never inherited separately. Agent JSON holds only the safe provider fields and an output-only configured flag; the complete bundle is encrypted separately with a tenant and Agent binding and its own purpose, and configuration and secret changes commit together under the Agent row lock. Model-only edits need no key. Merged harness, protocol and limits are validated without reading keys. Session creation reads safe defaults and ciphertext in one snapshot, and a complete Session override does not decrypt the inherited bundle. The resolved bundle is frozen in an encrypted Session-owned row, and dispatch fails closed when that snapshot is missing or cannot be decrypted; later Agent edits, default changes, restarts and suspension never resolve it again.
modelconfiguration owns each Harness's deployment default: its service validates a replacement through the Harness declaration, and modelconfigurationpg seals the complete bundle to the Harness and stores it under a private revision UUID generated on every PUT, identical replacements included, with its audit row in the same transaction. Session creation resolves the opened bundle and its revision together and freezes them; retries and older Sessions never gain or replace revision metadata. After a successful root terminal commit, the Dispatcher's required modelconfiguration.Observer runs one independent pool operation with at most one second to update the matching current revision. Only completed Turns and native provider failures with engine_failed count; cancelled work, Core or Runtime errors and input-policy classifications never do. ShouldObserveProvider skips the round trip for outcomes that cannot count, and the SQL stays authoritative: it verifies the tenant, root Turn and committed outcome. Both check the same shared cases. The metadata-only transaction sets statement and lock timeouts within the remaining budget and issues one UPDATE that locks only the default and samples database time after the lock. Errors throttle for 30 seconds, ordinary successes throttle for 30 seconds with one immediate recovery write after each accepted error, and an unchanged revision has at most three effective writes in any 30-second window of nondecreasing database time. Observations never change updated_at, readiness or execution truth, and can be lost or stale; there is no queue, retry, probe or backfill.
Session execution-configuration reads use a separate immutable safe projection written with its provenance in the Session's creation transaction. It reads no ciphertext, never recomputes sources from current Agents or defaults, never touches activity or wakes a sandbox, and does not affect retry identity.
Provider input validation uses the adapter rules in internal/harnessconfig: one internal registry for protocol and token-limit validation, while Core owns credential environment and endpoint admission policy.
- Saved Agents parse tools with the saved-form parser, which keeps every pinned
web_searchmode. Session admission re-resolves the effective tools with the execution parser, which admits only disabled search; Worker device selection and the final preclaim also refuse a non-disabled search control.
Vaults and credentials describes the resources, selection rules, refresh and deletion. vaults implements them, with vaultpg as its storage, under these rules:
-
Credentials are children of tenant-owned Vaults. Creation admits the owner in the same SQL statement as the insert; retrieval joins the owning Vault; listing enforces Project and Vault ownership on the parent, cursor and row query. Metadata queries never select ciphertext and need no encryption key.
-
vaultpgseals secret values before they reach SQL, with Core's separately configured random 32-byte key and the standard library's random-nonce AES-GCM. The versioned authenticated binding covers tenant, Vault, Credential, authentication purpose and exact destination. Never reuse daemon transport encryption for this storage. A missing key disables credential writes withcredentialcrypto.ErrUnavailable; a malformed configured key fails startup. -
A static replacement is one SQL mutation scoped by tenant, Vault, Credential, auth type and destination, reusing the safe metadata for the immutable binding; it never decrypts the previous token, and a failed write keeps the old row.
-
OAuth refresh and replacement serialize on the Credential row lock, authenticate the stored grant metadata against its encrypted copy before using an endpoint, and persist the refreshed grant before returning an access token, so a stale refresh cannot undo a deletion or Vault cascade.
-
Credential deletion is one mutation checked by tenant, Vault and ID; Vault deletion removes the parent and its Credentials through the foreign-key cascade in one SQL statement, without decrypting, needing the key or calling providers.
-
At dispatch, Core rechecks the tenant, attached Vault, selected ID, frozen auth type and exact URL before scoped decryption, and the token enters only the transient daemon request. A missing key or binding failure never falls back to anonymous execution.
-
credentialcryptociphertext is a format version byte followed by the standard AEAD nonce, ciphertext and tag. The authenticated data holds a fixed domain and version plus the binding (tenant, Vault, Credential, auth type, exact destination). Keep the domain string unchanged: existing rows must still decrypt. -
Random-nonce GCM allows at most 2^32 encryptions per key.
secrets/credential.keyalso seals model providers, the E2B key, Skills, initial files and environment setup, so every sealed write counts toward that bound; there is no rotation or re-encryption path. -
OAuth dispatch refresh holds the Credential row lock and the external exchange under one 20-second context (
vaults.oauthRefreshTimeout). The refresh HTTP client has a 10-second overall timeout and 5-second TLS handshake and response-header timeouts, uses no proxy and treats any redirect as failure.
Accepted public MCP credential profiles are declared centrally by the service, separately from private adapter capabilities; a daemon capability alone never opens a profile. Frozen binding validation runs at admission and later input, and the same capability and placement checks run at device selection, the final preclaim check and request construction, before scoped decryption. Native configuration and token injection stay in the adapters; the Codex adapter and the Claude SDK adapter describe theirs. The shared resolver keeps omitted or null allowed_tools as unrestricted and an explicit empty list as deny-all. Saved HTTP transport output includes headers: {} while the effective Session transport omits headers, matching the two pinned resource types. An omitted or null connection_origin on HTTP transport is stored as service before the other checks.
- Admission and the Claude bridge share one whitespace set, the union of Go
unicode.IsSpaceand ECMAScriptString.prototype.trim(blankTextRuneinservices/core/internal/execution/message_support.go). A shared table test keeps both sides equal; change them together. - Whitespace-only admission is a field of the engine profile (
WhitespaceOnlyText) checked with the image profile during Worker admission. Never branch on the harness name in handlers. MessageInput.Validatetreats any non-empty text part as content and never trims. It runs in Core admission, Worker delivery, daemon steering and prepared start, and in the Codex and MiniMax adapters.- Resolve a tool result's target under the tenant Session lock, after the Session lookup: a well-formed
turn_idis looked up in that Session and must own the call; otherwise the Session's own calls decide between "unknown call" and "different Turn". Never reject a malformedturn_idbefore the Session lookup, so missing, malformed and foreign Sessions keep one 404, and read only the caller's Session. - The recorded Environment failure reason is composed only from a fixed step label and integers (setup command index, exit status 1-255), so commands, environment values, package names, paths and process output cannot reach the reason, events, logs or responses.
services/core/internal/execution claims a Turn from queued to in_progress before subscribing or sending, and never replays a claimed or interrupted Turn. Extra inputs require native receipts. The terminal outcome and the native Session ID commit together under the admission lock, and unapplied messages prevent a successful completion. Credentials are resolved separately from the immutable non-secret snapshot. A peer that lacks a required capability is rejected before the claim, and a failed strict resume never starts unrelated history. Native continuation needs the device's persisted engine files; a native Session ID alone cannot restore deleted history.
Core replaces a Turn's usage with each complete valid token breakdown, which sessions.MeasuredUsage decides, and keeps the last committed measurement when execution is interrupted. It never infers tokens from context occupancy or costs and never parses native raw payloads. When done also carries usage that was already reported, the counters are not added again.
Subagent observations use the common types in internal/agentdaemon/proto/subagents.go. sessions projects them and sessionpg assigns their public IDs, under the Session lock of the leased execution journal; native names, history parsing and outcome proof stay in the adapters. Child Turns have their own table, separate from Core's queue, and Session Turn reads and the Session event stream carry root work only. The migration-defined public_execution_turns view has no public reader; do not reintroduce mixed Session Turn pages. Repeated effects are idempotent, root output freezes first, and child Items are delivered before their terminal Turn snapshot.
Function calls are stored per tenant, Session and Turn with immutable public and executor call identities and arguments. Result admission and application receipts serialize on the Session lock with cancellation and terminal transitions. sessions.AdmitFunctionResult keeps the complete caller-validated result, including omitted versus null error and output and ordered text and image parts; identical retries return the saved decision and changed results conflict. Recording a call through sessions.ExecutionOperations moves the Turn to waiting, and the last application receipt resumes it. A function result may join a message or cancel batch: its explicit Turn and call identity select an existing call, admission never creates a Turn for it, and the result and its input retry record commit in one transaction. Targets resolve only after the tenant Session lookup; an unknown call or a call of another Turn is 400 invalid_request_error. The input cursor skips function results, whose own receipts decide application. A Session with functions waits for a device that declares function_tools, and Core delivers each saved result once per live dispatch, appending a non-null error as a final text part because the native result has no error field.
Neutral tool and message observations go into the journal before sessions.ProjectSource projects public Items; the Item projector in items validates the shared observation contract and never decodes engine-native snapshots. command_output fragments update Items only for a command already indexed in the same Turn, and commit with their agent.output.command_execution_output.delta events. Completion output replaces accumulated drafts, terminal Items ignore late fragments, and cancellation keeps partial output without inventing a successful completion.
Execution observations are written to tenant-scoped turn_events in ordered, idempotent batches through sessions.AppendTurnEvents before they back recovery or publication, with daemon payloads intact. Flush at least every 100 ms while consuming events and before terminal persistence; the terminal outcome, its journal entry and native continuity commit together. sessions enforces the journal limits: 64 observations, 512 KiB per payload and 1 MiB per batch, and 65,536 observations and 32 MiB per Turn, with one extra entry reserved for the terminal outcome. Never infer a successful completion after a persistence error or stream overflow.
Live Session SSE reads session_events committed with the corresponding input, Item or lifecycle change under the Session lock, as immutable transition snapshots. The notification buffer keeps at most 256 events and 64 MiB per Session after each transaction (the sessions retention bounds, which sessionpg applies), and read batches at most 32 events or 1 MiB, each keeping a single oversized event. GET polls committed events every 100 ms from the committed high-water mark; a missing sequence position ends the stream with a safe error. Socket writes have a five-second deadline and hold no database connection. Rebuilding historical indexes emits no live events.
A streaming Session creation reuses atomic input admission and the live event loop. The creation upsert returns its event cursor under the Session lock, before the initial inputs; never replace it with a post-commit cursor lookup. The settled marker on sessions.SessionChange and the pending-input flag the stream uses to end are internal, never wire fields. The stream rereads the Session projection after it sends a Session status event and otherwise at most once a second.
Public Items read a projection updated in the same Session transaction as admitted messages and journal batches. Item IDs derive from the Turn and source identity, and the first-observation timestamp and tie breakers never change when content or status does. Each new Item's Session position is allocated under the Session lock, preserving observation order for equal timestamps, and each Turn allocates its own zero-based output_index, which inputs do not consume; updates and retries keep both.
Item merging never mutates the incoming observation or the previous snapshot: public text delta events read the original fragment after merging, while the Item keeps the accumulated text, and the content slice is copied before its text pointer is replaced. A first observation without its own fragment carries its unchanged text in one delta. Wire-only explicit nulls come from response marshalling, while stored Item payloads keep their original encoding through Item.MarshalStored, so replayed child Items compare equal. Structured tool JSON is kept without float conversion, and an unfinished call never becomes a successful result. When a Turn ends, sessions.EndTurn makes its unfinished Items incomplete with their partial content and reports them in Session position order before the Turn's event and its settled Session activity; sessionpg gives them one shared settlement time. Function results are Session input Items: they emit item.added with a null output_index and never item.done, whose upstream union allows only agent output, and their public output and error come from the saved submission.
Enabling the daemon gateway with OAC_PUBLIC_URL also starts the execution Worker. One Worker owns an execution database through a pgunit.Lease: the PostgreSQL advisory lock held by one dedicated connection. A second Core on the same database cannot acquire the lease and starts no Worker. The Worker's execution writer runs every Session transaction on that connection: binding, claim and reconciliation, journal, Items and usage, function callbacks and receipts, and terminal state. The lease gate serializes these short transactions and the ownership pings, and pgunit.ExecutionTimeout (five seconds) bounds each one, including its gate and Session-lock waits. Cancelling an in-flight pgx operation can close the connection that owns the lock, so coordinator-owned work is cancelled only through the lease's cancellation fence, between leased operations. Never hold a transaction across daemon or model work, reconnect the writer or fall back to the pool after losing the lease. Public admission and device maintenance use pooled connections, and pooled reads grant no write authority. Transactions state their isolation: read committed for writes, read-only repeatable read for snapshots.
Sandbox reset snapshots bind the deployment relation explicitly to its single row before joining resources, so that even on a fresh database without statistics an inflated join estimate cannot trigger JIT compilation inside the lease deadline.
At startup the Worker fails previously claimed work, keeps queued input and never replays uncertain execution. Shutdown cancels active dispatch and attempts terminal persistence before releasing the lease; a lost owner cannot commit. Closing the lease invalidates its writer and waits for pgx cleanup within the caller's deadline; a later close can resume that wait. Tests that transfer ownership immediately use pgtest.ObserveExecutionLeaseRelease to observe the previous owner's advisory lock disappear before starting the next, with a bounded wait that fails on query errors. The lease fences database writes, not already queued daemon commands or native effects.
The managed lifecycle describes publication, allocation, per-node workers, placement, suspension, reset and archive; the sandbox node protocol the node connection. In code:
- Each allocation's immutable specification and the current credential resolve in one
deploymentpgsnapshot, 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 error details are scoped by the
/core/v1router's writer mark, never by a request path test. Write Core errors withwriteCoreErrorand typedCoreErrorDetailsvalues (string, number, null and string-array constructors); invalid or empty details are omitted as a whole. The mark preserves error observation, flushing andhttp.ResponseControlleraccess. 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 incontracts/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
deployment.ConfigurationErrorwithout 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_unavailableat 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-ldflagsintomain.buildRevision. Keep the response shape aligned withpackages/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.RecordWriteAuditin 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, orRecordDeploymentMutationfor a deployment-wide write, in the same transaction. Administrator provenance takes precedence over a key's.