Skip to main content
An agent service runs your agent as its own process, independent of the app server. Use it when you need a separate process boundary, direct control-plane registration, remote MCP tools, or deployment-owned service telemetry. Use a normal in-app route for everything else. Veryfront Cloud can invoke a push runtime directly against an agent service, which is the main reason to deploy one even when the app and the agent share a host. Shared and managed dedicated servers use the framework-owned veryfront serve runtime instead. That runtime discovers all project agents and tools, then routes each signed control-plane request by agentId. Projects on a managed dedicated server do not require a service.ts entrypoint. Add one only when you intentionally run the standalone Agent Service process described in this guide. Managed runtimes can execute a connected agent from another project’s immutable release. The control plane authorizes the connection before dispatch: run.project identifies the project that owns conversations, runs, tools, and billing, while sourceProject identifies the project and environment supplying agent definitions. The signed request audience remains the source runtime’s project. Registration alone does not authorize another project or relax the runtime’s signature checks. Source reads use a separate, read-only credentials.sourceAuthToken. The source project’s environment variables and runtime token are not passed to the connected execution. Platform tool calls and inference use the consuming run’s credentials. Child runs must inherit the authorized source connection through the control plane. Connected execution evaluates project modules in an invocation-specific registry namespace, including discovery under the consuming project’s credential context. This prevents reuse of closures containing the source project’s secrets or a previous invocation’s credentials. Framework registries and evaluated-module cache entries are retired when the response completes, fails, or is cancelled. Deno’s ESM module map itself lasts for the process lifetime. The managed shared renderer pool bounds this retention with memory-triggered, graceful pod recycling. Dedicated runtime deployments need their own equivalent recycling policy before enabling repeated connected runs; the shared pool’s policy does not cover those deployments. Connected agents and their child agent runs are supported. Starting, resuming, or retrying a source-defined durable workflow from connected execution is rejected before accessing the workflow backend. Durable workflow recovery needs a separate persisted source authorization and credential-renewal contract; ordinary same-project workflows and explicit calls to the consuming project’s existing APIs keep their existing behavior.

Prerequisites

  • At least one agent in agents/ that the service should expose (see Agents).
  • A deployment target you can run a long-running Node process on.
  • For Veryfront Cloud registration: VERYFRONT_API_TOKEN, VERYFRONT_PROJECT_ID or VERYFRONT_PROJECT_SLUG, and a publicly reachable VERYFRONT_AGENT_SERVICE_URL. See Configuration for the full list.
  • Immutable deployment metadata for runtimeSource when the control plane invokes the service.

Create a service entrypoint

Create a process entrypoint that starts the default Veryfront Cloud agent service runtime:
startNodeVeryfrontCloudAgentService() starts the runtime from the environment that is already loaded. It does not load local .env files or initialize process-wide telemetry. Load standalone service environment files through the trusted deployment wrapper before it imports service.ts. Project modules cannot mutate the shared process environment through the public agent API. Initialize service-level OpenTelemetry in the trusted deployment wrapper before it loads service.ts. Do not let project code select process-wide exporters, trace hooks, or application-error reporters. The framework-owned veryfront serve runtime owns this setup on shared and managed dedicated servers. The standalone service shares a process with the Agent code it loads. Use it for trusted code. Captured request accessors protect specific ingress operations; they do not provide process isolation for request bodies or credentials. Custom host route handlers receive the original request and remain responsible for authentication and credential handling. Dispatch visits the host route table and matched path segments by index so a replaced array iterator cannot inject a handler before host authentication. CORS allowlist membership, response header writes, and route path parsing also use captured operations. Sparse route, origin, method, and header arrays ignore inherited entries. Route handlers retain ordinary-object params; decoded keys bypass inherited setters. Framework responses supply explicit status, status text, and empty-header defaults to native constructors. These defaults override writable inherited data properties, including CORS headers for a denied origin, and apply only to omitted values. If Object.prototype defines an accessor for headers, status, or statusText, response construction throws a TypeError before native option conversion can invoke it. On Node, non-writable inherited data properties for these fields also cause native construction to throw a TypeError. The runtime does not remove these properties, return an empty response, or retry with weaker CORS rules. Invalid response values still fail native validation, and host handler errors propagate unchanged. The service discovers the same project primitives as the app runtime:
  • agents/
  • tools/
  • skills/
  • resources/
  • prompts/
  • workflows/
  • tasks/
When exactly one code or markdown agent is discovered, that agent becomes the default for direct /api/runs requests. Pass agentId when the service exposes multiple agents and direct requests need a predictable default. Hosted tool execution preserves the selected agent ID and durable run ID. Remote tool calls carry these identities so they match the run authorization claims. If you customize createTaskContext, return the resolved agentId in that context. Hosted execution uses that identity, including any normalization your hook applies.

Keep agent behavior in project files

Define the agent in agents/ and keep service startup separate from agent behavior:
Markdown agents use the file path as the agent id:
For non-standard project layouts, configure discovery paths in veryfront.config.ts under ai.<primitive>.discovery.paths.

Configure registration

Control-plane registration is convention-first. In auto mode, the service registers only when VERYFRONT_API_TOKEN and VERYFRONT_AGENT_SERVICE_URL are present. Registration also requires the immutable runtimeSource binding described below.
When an agent service keeps control-plane traffic on an internal HTTP service URL, set VERYFRONT_PUBLIC_API_BASE_URL to the environment’s HTTPS API origin. Run-scoped inference credentials use that public origin for provider gateway requests and never travel over the internal HTTP connection. Use VERYFRONT_AGENT_SERVICE_REGISTRATION=enabled when startup must fail if the service cannot register. Use disabled when the service must run without control-plane registration. The service name resolves from VERYFRONT_AGENT_SERVICE_NAME, then the nearest package.json or deno.json name, then veryfront-agent-service. Pass serviceName only when code should override that convention.

Bind control-plane runs to the deployed source

A standalone agent service discovers one local project snapshot at startup. It cannot select another project branch or release for an individual request. Bind the service to deployment-owned immutable metadata when it accepts signed control-plane runtime invocations:
The service accepts a control-plane invocation only when its agentSource exactly matches runtimeSource. An unbound service returns CONTROL_PLANE_AGENT_SOURCE_UNBOUND. A different release or environment returns CONTROL_PLANE_AGENT_SOURCE_MISMATCH. Branch sources are mutable and return CONTROL_PLANE_AGENT_SOURCE_UNSUPPORTED. Do not resolve runtimeSource from the latest deployment at request time. Pass the environment and release identifiers that produced the running service artifact. Direct /api/runs requests do not select project source and do not require this binding.

Add remote MCP tools

Use mcpServers when the service needs remote tools. Use veryfrontApiMcpServer() and veryfrontStudioMcpServer() for Veryfront-owned control-plane MCP servers and normal MCP server config objects for third-party servers. This service startup config uses endpoint and headers. Per-agent config in agent({ mcpServers }) uses transport, auth, and toolPolicy.
If mcpServers is omitted, the Veryfront Cloud preset includes veryfrontApiMcpServer() by default. Pass mcpServers: [] to run without remote MCP tools.

Reach trusted deployment-local MCP servers

The default remote MCP source uses guarded outbound networking. Keep that default for third-party, request-derived, and tenant-configured endpoints. A separately deployed agent service may need to reach a trusted MCP server on a private cluster address. In that case, capture the host transport and the exact allowed endpoints once at startup. Use the host transport only for those immutable endpoints and preserve the guarded source for everything else:
The framework rejects invalid allowlist entries at startup and uses the host transport only for an exact normalized URL match. Unmatched, invalid, and resolver-based endpoints retain guarded outbound networking. http: is appropriate only for private deployment-local networking; use https: for public networks. Never put a callback endpoint or a per-request URL in the trusted endpoint list.

Refresh runtime state

Use resolveRuntimeState when a long-lived service run must refresh instructions, context, or available tools at a model step boundary.
Services that use Veryfront Cloud project steering can reuse fetchDefaultAgentServiceProjectSteering() for the initial fetch and createDefaultAgentServiceProjectSteeringRefresh() for step-boundary refresh. Managed brokers can use createManagedBrokerProjectState() from veryfront/agent/managed-broker for project instructions and skill catalogs. The executor sends its effective tool selection on each steering refresh. The broker validates that selection against the installed grant before constructing instructions, and includes skills only when load_skill remains available. Refreshes without a tool selection omit the skill catalog. Managed broker ingress checks application strings and property names for known credentials after URI decoding. Malformed escapes do not suppress checks on valid encoded segments. Decoding is limited to 16 passes; ingress rejects strings that still require decoding after that limit. Keep credentials out of executor data, including encoded URLs and metadata. Closing the managed broker handler or aborting its service signal returns 503 BROKER_UNAVAILABLE for pending admission. Request-only cancellation returns 499 BROKER_INGRESS_ABORTED.

Keep inference authority separate

Signed runtime invocations may include an optional credentials.inferenceAuthToken alongside the broader credentials.authToken. The inference credential is bound to the exact run and agent and is intended only for attributed Veryfront Cloud model requests. It is size-bounded to 16 KB, uses visible ASCII token characters, and remains optional so existing producers and consumers stay compatible. Treat this as trusted-host authority. Do not copy it into project context, tools, logs, durable request payloads, or a general API client. Framework-managed agent services bind it only after the signed invocation and run-event credential have been verified, bypass project model overrides for Veryfront Cloud models, and send it only to an HTTPS (or loopback development) gateway. Custom runtime adapters should follow the same boundary: use authToken for project and control-plane operations, and expose inferenceAuthToken only to the model subprocess’s Veryfront Cloud gateway configuration.

Use lower-level helpers

Use startNodeVeryfrontCloudAgentService() for the standard service shape. Use lower-level helpers only when the service needs a custom server adapter, custom execution preparation, or custom infrastructure.

Migrate custom durable child event writers

This migration applies to custom hosted runtimes that call the lower-level durable child helpers. Framework-managed startNodeVeryfrontCloudAgentService() runtimes create and scope writer capabilities internally. Raw authToken, apiUrl, and runEventAppendToken fields no longer grant durable child event-writer authority. The parsed hosted request also excludes the writer credential. Keep the credential inside trusted ingress and replace the removed fields with an opaque HostedRunEventWriterCapability. Apply the change at every integration point your custom runtime implements: The generated veryfront/agent reference lists the complete properties for these contracts.
  1. After trusted ingress verifies an exact root-run append credential, create the root capability. Do not pass a general user API token.
  2. Pass that exact-parent capability to helpers that own child persistence and capability delegation. Do not pre-mint for these helpers.
  3. For lower-level helpers that receive an already-persisted durableChildRun, mint and pass an exact-child capability:
  4. Update detached starter callbacks to accept the isolated request:
A durable execution without authority bound to the expected run fails before provider dispatch. Token exchange failures are bounded, sanitized, and fail closed; callers must not retry by falling back to a user API token.

Managed executor startup

startExecutorRuntimeEntrypoint is available from veryfront/agent/executor-runtime. It starts the executor side of a managed broker/executor deployment on Node.js 22 or newer. Your trusted image launcher calls initializeExecutorRuntimeContracts() from the same export to initialize the first-party schema validator, bundler, module lexer, and Skill document parser, then supplies the Operator allocation environment and image manifest. Missing runtime contracts fail startup. The executor accepts one authenticated runtime.install message bound to its allocation, invocation, generation, owner, and immutable source. Discovery remains unavailable until installation succeeds. The trusted image launcher selects the profile at startup; channel messages cannot change it.

Installation profiles

The full-runtime profile uses a separate bounded stream for initial checkpoint state, so durable replay state can exceed the installation message limit. The project-tools profile is selected by startExecutorRuntimeEntrypoint({ mode: "project-tools" }). Its installation has the following shape; the broker supplies the actual allocation and source identities:
allowedToolNames contains unique canonical names, with at most 1024 entries. maxCalls is an integer from 1 to 4096; maxConcurrent is an integer from 1 to 32. The fixed context binds tool calls to the admitted canonical run. Correlation IDs, cancellation and progress use the authenticated channel. This payload accepts no runtime grants, private credentials or host capability IDs, and rejects unknown fields. This profile exposes neither runtime preparation nor agent streaming; the trusted broker owns the agent loop and privileged operations. The fixed context also accepts optional userId and projectSlug from the approved execution grant. Project tools receive those captured values; caller conflicts fail. For canonical execution using a globally owned source, context.projectId may be explicitly null. The tool receives no project ID, while its canonical run ID remains bound. A projectless context must not include a project slug, and a project-owned source still requires a project ID. Omitting projectId does not select global execution. Global steering may be provided as an explicit granted capability; it is not inferred from a placeholder project. This support does not enable ephemeral execution or project transitions in the trusted broker. An explicitly enabled project source can receive the current call’s activeSkillId and bounded activeSkillToolAvailability. Omitted skill fields clear prior values. Credentials and other caller context fields do not cross the project channel. Unknown startup mode values fail before bootstrap configuration or artifact access. Selected inline tools and discovered tools are combined under the exact project source policy, including metadata access and later execution. Framework-generated agent runtime tools are excluded from the project-only catalog, even when their names appear in a grant. The full-runtime profile applies the same source-policy scope while extracting inline tools and reading their metadata during preparation. The optional installation limits object contains every field below. Each value is a positive integer no greater than its default. Omission selects the defaults; partial overrides are accepted by the trusted broker’s tools.limits option, which normalizes a complete object for installation. maxCalls counts source enumeration, listing and execution, including admitted failures. The trusted broker shares call and concurrency accounting across host and project tools. Cancellation holds an admitted slot until original work settles. maxMetadataBytes includes the validated project alias frame plus the combined host and project source/definition frames. Repeated listings keep consuming the same invocation budget. An ordinary project-tool failure releases its shared slot after the failure frame and normal channel end are confirmed. Cancellation, malformed or incomplete responses, and unknown transport failures retain that slot until allocation retirement, even when the caller’s cancellation signal remains live.

Broker composition

The hosted service’s DELETE /api/runs/:runId route requires verified cancellation authority for that exact run before changing execution state or remembering a delayed-start cancellation. The default runtime verifies the API’s existing signed cancellation bearer with its configured public key. Two claim shapes are accepted, one for each shape the API mints: a project- and run-scoped veryfront-server service-account bearer carrying tokenUse: project_scoped_service_account, and, for a run with no project, the requester’s own session bearer, which carries no tokenUse at all. Both shapes are pinned by a contract fixture holding payloads captured from the API’s minting code, so a claim change on either side fails a test instead of rejecting every cancellation in production. A general user token without a matching run claim is insufficient, and development decode-only authentication does not grant cancellation. Custom route sets must supply verifyRunCancellationToken; omission returns HTTP 403. The API retains actor and collaborator authorization before minting the runtime credential. Request-owned AG-UI streams still support cancellation through their request signal. The requirement sits at the effect, not only at that route. createAgUiCancelHandler and createAgUiResumeHandler both require an authorizeRunControl decision, and the session manager takes verified authority rather than a run id for the cancel-and-tombstone and resume paths. The run the effect reaches is read out of that authority, so a surface that mounts either handler cannot reach the registry without a decision, and authority granted for one run or operation cannot be redirected to another. Run control the runtime itself performs, such as a session or waiting timeout, still cancels by run id and cannot create a delayed-start cancellation. Resume carries the same contract. The default runtime verifies the bearer the API signs for POST /api/control-plane/runs/:runId/resume against the same public key. The default route set also serves POST /api/runs/:runId/resume with the same verification and signal handler. Both routes refuse an invalid bearer before changing the waiting run. Resume has no separate mint: the API signs it with the run-bound service credential it already uses for the stream, so two claim shapes are accepted, both bound to one run and one project. A run whose record names an actor service account and an agent access grant carries tokenUse: run_scoped_service_account with that grant recorded in the token; every other project-bound run carries the veryfront-server service-account bearer with tokenUse: project_scoped_service_account. The API refuses to resume a run with no project before it mints anything, so no projectless resume shape exists. Both shapes are pinned by a contract fixture holding payloads captured from the API’s signing path, including the scope ordering, which differs between the two mints. The grant relation is recorded rather than re-derived, so the API’s collaborator policy stays the API’s decision. Custom route sets that serve resume must supply verifyRunResumeToken; omission returns HTTP 403. Both resume routes enforce the 1 MiB request-body limit on the incoming stream before calling custom authentication. Authentication receives a separate request with the bounded original body, so it may read JSON without consuming the signal handler’s input. Oversized declared or chunked bodies return HTTP 413 before authentication and cancel the remaining stream. Standalone resume and cancel handlers apply the same bound before run-id resolvers and authorizers receive a body. Cancellation without a body remains supported. Cancellation claims and request bindings must be own data properties. The route also rejects inherited then properties before authentication and rechecks after asynchronous verification. This prevents known thenable pollution from replacing JWT verification results. These checks do not isolate arbitrary project code running in the service process; use a separate trusted broker process for that boundary. The broker owns HTTP authentication, credentials, model and tool authorization, and durable persistence. Executor facades call these capabilities through the authenticated channel. Closing a runtime revokes its facades; admission remains held until the original work and cleanup settle. This entrypoint requires the broker and isolation infrastructure to be configured separately. Use veryfront/agent/managed-broker for the broker composition and signed control-plane HTTP adapter. createManagedDurableBrokerHandler handles direct POST /api/runs requests with detached execution and requires run-event token verification and output persistence. createManagedAgUiBrokerHandler handles direct POST /api/ag-ui requests with request-owned SSE. Its ingress must supply verifyProjectAccess for requests containing a project ID. Without that verifier, project requests return HTTP 403 before preparation or allocation; projectless requests can still proceed. Configure each direct adapter’s owner in trusted service configuration; project owners reject requests for other projects before preparation or allocation. Their prepare callbacks receive private broker authority and bounded executor data separately. Resolve immutable sources and capabilities in the broker before returning the executor start input. These adapters share admission, duplicate handling, and retirement with the signed adapter. The broker installs invocation grants, describes the selected agent, and prepares the executor before accepting a run. It keeps model and tool execution unavailable during preparation. Configure detached 202 responses or request-owned SSE responses in trusted service configuration. Signed, direct durable, and direct AG-UI ingress reject application strings or property names containing a known broker credential, including credentials embedded in messages or attachment URLs. Detached runs require output persistence callbacks; their finalization remains part of the session’s owned work until all writes settle. Canonical runs must pass the persistence adapter’s bindSessionOwnedWork callback in ManagedExecutorStartInput. The broker binds it after reserving a session and before installation or preparation. Scheduled, retry, and explicit event-queue writes use that session owner. A persistence timeout can return promptly while pool capacity stays reserved until the original write settles. createManagedBrokerPersistenceFromCapability accepts the opaque writer returned by managed ingress’s createRunEventWriterCapability, the canonical run projection, and an opaque terminal capability created by createManagedBrokerTerminal. The writer pins event append to its run, API endpoint and transport. The terminal factory takes independent completion credentials, the run projection, model and provider resolution, and trusted API transport. It keeps the run binding and completion operation in private state. Relabeled, cloned, fabricated, and other-run terminal handles are rejected before writes. An event-writer token cannot authenticate the API’s /complete route. The terminal factory uses that route with independent application authority; it does not accept arbitrary completion callbacks or implement the detached-completion protocol. The raw-token createManagedBrokerPersistence constructor requires both runEventToken and a distinct completionAuthToken accepted by the API completion route. Existing callers must add that completion credential; the constructor rejects omission or reuse of the append token. Provider resolution is called without exposing private adapter options as its receiver. Both constructors require the same session binding and cleanup. Tokens and terminal capabilities remain in the broker. startNodeManagedAgentBroker binds the signed stream, durable start, AG-UI, and cancel/resume handlers to a Node server. Supply every handler, the broker pool, and a readiness check explicitly. It preserves /liveness and /readiness; shutdown stops admission before waiting for handlers and broker work to retire. This server adapter does not configure product policy, registration, credentials, or executor images. Managed run routes return HTTP 400 with BROKER_INGRESS_TARGET_MISMATCH when a run ID contains malformed URL encoding or fails the canonical run ID schema after decoding. Run IDs contain 1 to 128 ASCII letters, digits, underscores, or hyphens. Valid encoded run IDs are decoded before the handler receives them. Signed stream requests must sign the original encoded request path. To verify the packaged broker and executor locally, use Node.js 22.3.0 or newer:
This suite installs the built packages and exercises signed HTTP ingress, a separate executor over TLS, model and tool calls, SSE and detached responses, executor termination, client cancellation, delayed terminal persistence, and steering refresh with provider-native tools. Empty replay snapshots remain valid when provider replay is disabled. Project-controlled hooks use synthetic credential canaries with positive controls. The npm smoke jobs run the suite on the minimum supported Node version and the current CI version. These checks use local synthetic services. Verify the deployed artifact and isolation configuration separately before traffic cutover.

Verify it worked

Start the service entrypoint and call the run route directly. The default port is 3001; override with PORT if needed.
A working service streams AG-UI events back. If Veryfront Cloud registration is enabled, the service should also appear in the cloud dashboard’s agent service list after the first heartbeat (VERYFRONT_AGENT_SERVICE_HEARTBEAT_INTERVAL_MS). Broker model output limits and provider-tool descriptors must stay within the installed model grant. Each broker tool capability must also stay within the installed tool allowlist. Startup rejects broader broker authority before allocating an executor. Preparation uses the narrower broker model output limits and provider-tool list, so its default model requests fit the broker policy. Anthropic thinking with an additive token budget reserves that budget from the total allowance before preparation chooses the completion limit. Adaptive thinking uses the total allowance without an additive reservation. Preparation rejects an explicit completion limit that exceeds the remainder. Source IDs must belong to the installed host-facade or remote-source grants. Owner-scoped tool selectors use the same canonical names for capability checks and steering refreshes. Trusted ingress must provide tools.catalog with the complete tool inventory and ownership metadata, including project-local tools that have no broker capability. The broker resolves short selectors to owned tools first, then validates source capabilities against those exact IDs. Preparation and steering refreshes receive the same resolved grant. A shadowed global tool does not gain authority from an owned tool’s short selector. The catalog must come from trusted source metadata before executor discovery. For selected host tools, the broker includes the catalog’s owner and short-name mapping in the validated installation. Rebuilt executor facades retain that mapping, so an agent’s short selector continues to select the same canonical tool. Remote source listings do not supply ownership authority.