Skip to content
Guide Reference Download app

@claxedo/workspace-runtime

@claxedo/workspace-runtime is the per-workspace host service — one process per workspace. It owns workspace host wiring around the runner adapters (which live in @claxedo/agent-sdk-runtime), the PTY and process managers, the file/diff/git surface, and the relay-host tunnel to @claxedo/workspace-relay. UI never imports this package directly; browsers talk to the control plane, which proxies to a runtime instance.

Terminal window
npm install @claxedo/workspace-runtime
Terminal window
pnpm add @claxedo/workspace-runtime
Terminal window
bun add @claxedo/workspace-runtime

Every deployment shape requires an explicit exposure declaration. The simplest is a loopback runtime over a directory:

import {
startServer,
loopbackWorkspaceRuntimeExposure,
} from "@claxedo/workspace-runtime"
await startServer(4200, {
exposure: loopbackWorkspaceRuntimeExposure(),
})

To back the runtime’s relay boundary with a custom identity provider, pass a TokenVerifier to the relay-host middleware:

import { Hono } from "hono"
import {
createStaticTokenVerifier,
type RelayHostVerifierClaims,
} from "@claxedo/workspace-relay-protocol"
import {
createRelayHostAuthMiddleware,
type RelayHostAuthOptions,
} from "@claxedo/workspace-runtime/relay"
// `key` is the relay's JWT verification key (a PEM public key or a JWKS
// resolver). The options type requires it, but it is bypassed whenever
// `verifier` is set.
declare const relayKey: RelayHostAuthOptions["key"]
const now = Math.floor(Date.now() / 1000)
const verifier = createStaticTokenVerifier<RelayHostVerifierClaims>({
tokens: {
"tok-tenant-1": {
subject: "u1",
scopes: ["workspace:write"],
// The middleware re-validates the verifier's claims against the
// relay-host token contract. Every field below is required — a
// missing or mismatched one rejects the request.
claims: {
iss: "workspace-relay", // fixed relay-host token issuer
aud: "workspace-host-service", // fixed relay-host token audience
sub: "u1",
org_id: "org_1",
workspace_id: "ws_1", // must equal the middleware's workspaceId
host_id: "host_1", // must equal the middleware's hostId
access: "cloud", // "cloud" + "cloud-vm", or
backing: "cloud-vm", // "user-hosted" + "local-worktree"
exp: now + 60,
iat: now,
jti: "jti-1",
},
},
},
})
const app = new Hono()
app.use("*", createRelayHostAuthMiddleware({
key: relayKey,
workspaceId: "ws_1",
hostId: "host_1",
verifier,
}))

The verifier is only the crypto/introspection authority. The middleware still validates its output into RelayHostTokenClaims — issuer, audience, sub, org_id, workspace_id, host_id, the access/backing pair, exp, iat, and jti are all mandatory, and the workspace/host ids must match the middleware’s own. Note that role is not a relay-host claim (it belongs to the relay’s Runtime Access Token).

  • Supported shapes — loopback, private-VM, relay-attached, embedded Hono app, and low-level host object. They share the /api/wr/* route surface but not the same trust boundary.
  • Subpaths/client, /host, /exposure, /relay, /config, /routes, /testing for focused, lower-level helpers.
  • Routes — health, capabilities, config apply, events, files, diff/git, PTY (WebSocket), and process surfaces, each gated by exposure-dependent runtime auth.
  • DurabilityRuntimeStore treats per-session JSONL journals as the source of truth with SQLite as a derived projection.
  • Path containment — session/file/PTY routes are pinned to the workspace target; absolute paths, .. escapes, null bytes, and out-of-workspace symlinks are rejected.

The root surface is checked against docs/api-manifest.json and does not export control-plane clients or agent-extension materializers — those compose outside the OSS runtime boundary.

See the README on GitHub for the full public-surface table, route table, event contract, relay-attachment env vars, and lifecycle sequence.