Worker Fleet Migration
Worker fleet upgrade for on-prem deployments.
Upgrade your on-premises worker fleet from the legacy shared-key passthrough model to the platform-owned execution-capacity model with project-scoped authentication, short-lived runtime JWTs, and structured sandbox orchestration.
Architecture overview
The current execution-capacity model replaces the legacy projects.sandbox_* columns and global WORKER_API_KEY with a structured hierarchy:
Org
└── Execution Provider Pool (local | docker | kubernetes | e2b | daytona | modal | vercel)
└── Worker Host (durable capacity, heartbeats)
└── Worker (per-session or host-spawned process)
Project
└── Execution Route (allowed / preferred / forbidden / fallback pools)The diagram below shows how a worker authenticates and claims work under the new model:
sequenceDiagram
participant W as Worker daemon
participant P as Platform API
participant R as Redis queue
W->>P: POST /api/workers/register<br/>Authorization: Bearer rsp_live_...
P-->>W: { workerId, runtimeJwt, expiresAt }
loop Every 30 s
W->>P: POST /api/workers/{id}/heartbeat<br/>Bearer runtimeJwt
end
loop Poll for work
W->>P: GET /api/workers/{id}/poll<br/>Bearer runtimeJwt
P->>R: ZPOPMIN (atomic claim)
R-->>P: QueuedWork item
P-->>W: { sessionId, resolvedProfile, ... }
W->>P: POST /api/sessions/{id}/claim
endLegacy projects.sandbox_* columns remain readable and writable during the transition. The compatibility layer translates them into an org execution pool and project route automatically - no immediate action required for existing projects. New code should use /api/execution/* and /api/projects/:projectId/execution-route.
Migration phases
Phases 1a through 2a are already landed in production. Phase 2b and beyond are in progress. The upgrade steps below reflect the current state.
Phase summary
| Phase | Status | Description |
|---|---|---|
| 1a - backend foundation | Complete | Schema, registration tokens, runtime JWTs, worker/session route port |
| 1b - platform-owned worker routes | Complete | All worker + session HTTP endpoints owned by platform |
| 1c - project settings UI | Complete | Execution settings at /[org]/[project]/settings/execution/ |
| 2a - Linear client in platform | Complete | All deferred session handlers ported; Linear OAuth in platform |
2b - drop @renseiai/agentfactory-nextjs | In progress | Port remaining webhook/OAuth passthroughs |
| 2c - multi-tracker + multi-SCM | Planned | IssueTrackerProvider / GitCredentialProvider interfaces |
| 3 - sandbox provider interface | Planned | SandboxProvider.provision(spec) for on-demand workers |
| 4 - cloud sandbox providers | Planned | Daytona, E2B, Modal on-demand provisioning |
| 6 - on-prem portability | Planned | Docker image, Helm chart, swap-WorkOS-for-Keycloak |
Upgrade your workers to runtime JWT auth
Legacy workers authenticate with a global WORKER_API_KEY. Platform-managed workers use short-lived runtime JWTs issued by the platform's /api/workers/register endpoint. The runtime JWT carries proj, org, sub, reg, and scope claims.
Create a registration token
In the platform UI, navigate to your project's Settings → Execution tab, or use the CLI:
rensei project tokens create \
--project <project-slug> \
--type registration \
--name "my-worker-host"The token is a rsp_live_… prefixed string. It is accepted only by /api/workers/register and cannot be used for any other endpoint.
Register the worker
Your worker daemon calls POST /api/workers/register with the registration token in the Authorization header:
curl -X POST https://app.rensei.ai/api/workers/register \
-H "Authorization: Bearer rsp_live_..." \
-H "Content-Type: application/json" \
-d '{
"version": "0.5.0",
"capabilities": ["local"],
"maxConcurrency": 4
}'The response includes a short-lived runtimeJwt and a workerId:
{
"workerId": "wkr_abc123",
"runtimeJwt": "eyJ...",
"expiresAt": "2026-06-02T02:00:00Z"
}Poll and heartbeat
Use the runtimeJwt as the bearer token for all subsequent worker calls:
# Poll for work
GET /api/workers/{workerId}/poll
Authorization: Bearer eyJ...
# Heartbeat (every 30s)
POST /api/workers/{workerId}/heartbeat
Authorization: Bearer eyJ...The runtime JWT is short-lived. The worker SDK handles refresh automatically via /api/workers/{workerId}/lock-refresh.
Verify project routing
After the worker registers, confirm it appears under the correct project in the Admin UI at Workers or via:
rensei fleet status --project <project-slug>The worker should appear with runtime: go or runtime: ts and a recent heartbeat.
Configure execution provider pools
Provider pools are org-scoped. Configure them at Settings → Execution at the org level (not per-project). Each pool has a providerId, mode, credential binding, and capacity policy.
Supported providers
| Provider | Mode | Notes |
|---|---|---|
local | host-session / local | Daemon workers on the operator's own hardware |
docker | On-demand | dockerode-based container provisioning |
kubernetes | On-demand | Job-based pod scheduling |
e2b | On-demand | Pause/resume support |
daytona | On-demand | REST-based workspace |
modal | On-demand | GPU workloads, 24h session ceiling |
vercel | On-demand | Firecracker microVM, no execCommand |
Project execution routes
Each project declares which org pools it uses via a route policy. Set this at Settings → Execution → Route or via the API:
PUT /api/projects/{projectId}/execution-route
Content-Type: application/json
Authorization: Bearer rsk_live_...
{
"allowed": ["pool_local_01"],
"preferred": ["pool_local_01"],
"forbidden": [],
"fallback": null
}preferred pools are tried first; fallback is used if all preferred and allowed pools are at capacity. The platform resolves the final pool on each dispatch and stamps it onto the queued session.
Auth modes and pool pinning
Two auth modes are pinned to local-capacity pools and cannot use cloud sandboxes:
host-session- requiresexecutionProviderPools.providerId = 'local'local- same constraint
Attempting to dispatch with these auth modes against a cloud pool (e2b, modal, etc.) will fail at pre-flight validation. See Auth modes for the full capability matrix.
Legacy compatibility layer
During the migration window, the legacy projects.sandbox_* columns continue to work. The compatibility layer translates them:
| Legacy field | New model |
|---|---|
projects.sandbox_provider | org pool with matching providerId |
projects.sandbox_mode | pool mode setting |
projects.sandbox_credential_id | pool credential binding |
Do not write new code against these columns. Use /api/execution/* and /api/projects/:projectId/execution-route for all new integrations.
Legacy worker key (transitional fallback)
The global WORKER_API_KEY fallback is transitional and will be removed when Phase 2b completes. Migrate your workers to registration tokens and runtime JWTs before that window closes.
Six session endpoints are still passthrough to the legacy @renseiai/agentfactory-nextjs package because they depend on the platform's Linear OAuth client, which was only brought in-platform during Phase 2a. These endpoints accept WORKER_API_KEY only - they will return 401 if you send a runtime JWT:
| Endpoint | Why still passthrough until Phase 2b |
|---|---|
POST /api/sessions/{id}/status | Terminal transitions emit a Linear agent-session response activity to mark the session complete |
POST /api/sessions/{id}/activity | Uses createAgentActivity from the Linear plugin |
POST /api/sessions/{id}/completion | Builds completion comments via Linear plugin helpers |
POST /api/sessions/{id}/progress | Combines the in-process ring buffer with Linear createAgentActivity |
POST /api/sessions/{id}/external-urls | Calls linearClient.updateAgentSession - canonical storage is Linear itself |
POST /api/sessions/{id}/tool-error | Uses reportEnvironmentIssue() from the Linear plugin |
During the transition window: workers using runtime JWTs for all other endpoints must still send WORKER_API_KEY for these six. Both auth paths work simultaneously - this is intentional, not a gap.
Phase 2b will port all six endpoints to platform-native auth, after which the WORKER_API_KEY fallback will be removed from all handlers.
Ported vs. passthrough endpoint summary
Phases 1a-2a landed platform-owned versions of all worker and most session endpoints. Use this table to determine which auth token each endpoint accepts today:
| Endpoint | Auth accepted | Platform-owned? |
|---|---|---|
POST /api/workers/register | rsp_live_* registration token | Yes |
GET /api/workers | Runtime JWT | Yes |
GET /api/workers/{id} | Runtime JWT | Yes |
DELETE /api/workers/{id} | Runtime JWT | Yes |
POST /api/workers/{id}/heartbeat | Runtime JWT | Yes |
GET /api/workers/{id}/poll | Runtime JWT | Yes |
POST /api/sessions/{id}/claim | Runtime JWT | Yes |
POST /api/sessions/{id}/lock-refresh | Runtime JWT | Yes |
POST /api/sessions/{id}/transfer-ownership | Runtime JWT | Yes |
GET /api/sessions/{id}/status | Runtime JWT | Yes |
POST /api/sessions/{id}/status | WORKER_API_KEY only | Phase 2b |
POST /api/sessions/{id}/activity | WORKER_API_KEY only | Phase 2b |
POST /api/sessions/{id}/completion | WORKER_API_KEY only | Phase 2b |
POST /api/sessions/{id}/progress | WORKER_API_KEY only | Phase 2b |
POST /api/sessions/{id}/external-urls | WORKER_API_KEY only | Phase 2b |
POST /api/sessions/{id}/tool-error | WORKER_API_KEY only | Phase 2b |
Verify a clean migration
Run through this checklist after upgrading your fleet:
[ ] All workers appear in /admin/workers with a recent heartbeat
[ ] No worker is using a WORKER_API_KEY-only auth path (check worker logs)
[ ] At least one session has been successfully dispatched and completed under the new fleet
[ ] Project execution routes are configured at Settings → Execution for every active project
[ ] shape census for SDLC workflows shows v1: 0 (separate migration - see SDLC v1→v2)Source references
| File | Role |
|---|---|
platform/src/lib/project-runtime/ | Phase 1a: registration tokens, runtime JWTs |
platform/src/app/api/workers/register/route.ts | Worker registration endpoint |
platform/src/app/api/workers/[id]/poll/route.ts | Poll endpoint (atomic ZPOPMIN + project filter) |
platform/src/lib/providers/linear/client-resolver.ts | Per-org Linear client (Phase 2a) |
platform/src/lib/activity-buffer/index.ts | In-process ring buffer for session activities |
platform/docs/worker-fleet-migration.md | Full internal phased design doc |
Related
- Runner migration - opt in the Go agent runner per org
- Local sandbox provider - configuring daemon workers
- Capacity pools - pool substrate resolution and
runtime_providesoverride - Auth modes -
host-session/localcapacity constraints - CLI: fleet -
rensei fleetmulti-machine management commands - CLI: host daemon - install/uninstall/run/status/logs/doctor
- API: worker protocol registration - full register endpoint reference