NylorunDocsBeta
Get startedBuildRunDeployReferenceMore

App server

Build and configure your app, connect its executor, and serve agents to signed-in users.

Your app server is your own application. In production it does two jobs:

  1. Runs your tools. connectAgents registers agents and executes their tools when the Runtime asks.
  2. Serves your users. It signs people in and calls the Runtime for each person.

The starter project does the first job. Add the second when people use your agents.

Build and start

npm run build
npm start          # node dist/src/main.js

Set three variables, or print them for the linked project with eval "$(npx @nylorun/cli env)":

VariableValue
NYLORUN_RUNTIME_URLWhere your app reaches the Runtime. See Docker.
NYLORUN_TENANTThe Tenant id.
NYLORUN_SERVER_KEYThe Tenant's application key. Server-side only.

The app does not start the stack, Studio, or a file watcher. Start the Runtime first (npx nylorun up), under the same supervisor if you use one. You can also create Tenants with @nylorun/admin.

How the connection is found

createClient() and connectAgents() use exactly one complete source, in this order:

  1. Explicit { url, key, tenant } options.
  2. NYLORUN_RUNTIME_URL, NYLORUN_SERVER_KEY or NYLORUN_EXECUTOR_KEY, and NYLORUN_TENANT.
  3. The nearest .nylorun/link.json plus credentials.json.

Sources never mix. If any variable is set, all of them must be, or the call fails with connection_missing.

Executors

The compiled app connects every exported agent and workflow:

src/main.ts
import { connectAgents } from "@nylorun/agents";
import { agents } from "../agents/index.js";

const connection = connectAgents({
  agents,
  implementationVersion: process.env.NYLORUN_IMPLEMENTATION_VERSION ?? "app-1",
  runtime: {
    url: process.env.NYLORUN_RUNTIME_URL!,
    tenant: process.env.NYLORUN_TENANT!,
    key: process.env.NYLORUN_SERVER_KEY!,
  },
  onError: console.error,
});

await connection.ready;

Restarts and replicas re-register idempotently. Credentials, retries, and uncertain work are on Run → Executors.

Serve people

Your server signs people in and keeps the Tenant key. For each request it names the person with client.as(). The Runtime then limits the call to that person's sessions and vaults:

import { createClient } from "@nylorun/agents";

const app = createClient(); // the Tenant key, on the server only

// Per request, after your own sign-in:
const person = app.as(`app:${user.id}`, { scopes: ["sessions:own", "vaults:own"] });
const session = await person.createSession({ agentId: "support", ownerUserId: `app:${user.id}` });
await person.listSessions(); // only this person's sessions

as() sends Nylorun-Subject and Nylorun-Scopes on every call, event streams included. Nothing is minted or cached, so calling it per request is cheap. Another person's session answers 404, like a missing one; a route outside the scopes answers 403 (scope_required).

ScopeAllows
sessions:ownThe person's own sessions: create, list, read, stream, message, approve, respond, cancel
vaults:ownThe person's own vaults and credentials
agents:readListing the Tenant's agents
agents:writeSaving agents; listing agents, models, and providers
tenant:settingsThe Tenant's status, model provider, and sandbox settings

The default is ["sessions:own"]. Full options are in Reference → client.

Serve a chat UI with AG-UI

For a web or desktop chat UI, mount the AG-UI handler. It maps each AG-UI thread to one session per person, agent, and thread, and calls the Runtime as that person:

import { createServer } from "node:http";
import { createAgUiHandler, toNodeListener } from "@nylorun/agents/ag-ui";
import support from "./agents/support.js";

const agui = createAgUiHandler({
  basePath: "/api/agui",
  agents: [support], // nothing else is reachable
  subject: async (request) => (await getSignedInUser(request))?.id, // undefined → 401
});

createServer(toNodeListener(agui)).listen(3000);
// Next.js, Hono, Bun, Deno, Workers: export or mount agui.fetch directly.

Point CopilotKit or @ag-ui/client's HttpAgent at /api/agui/support. A complete web backend with a test is in the AG-UI example. Routes, approvals, and limitations are in Reference → ag-ui.

Rules for an app server

  • Browsers always go through your server. Never let one call the Runtime.
  • Drop every Nylorun-* header your own clients send. Never forward Origin; the Runtime refuses browser requests.
  • Terminate TLS for your clients.
  • Keep the admin key and application keys on the server. A server that holds the admin key can derive its Tenant key instead of storing one (deriveTenantKey(adminKey, tenantId, principalId) from @nylorun/admin, for a Tenant created with that derived principal).
  • Removing a person is your decision: stop acting for them and close their open streams. There is no per-person credential to revoke.
  • Keep the Runtime off the network. How your server reaches it is on Docker.

Next step

On this page