Files
browser/openspec/changes/replace-run-endpoints-with-redis-runtime-registry/design.md
T

92 lines
7.4 KiB
Markdown

## Context
Platform currently persists `RunEndpoint` rows and stores `ServerInstance.RunEndpointID` as if a server is bound to a durable machine endpoint. The same identifier is then reused for control registration, heartbeat status, job scheduling, UI availability, and component revocation. That model conflicts with the intended architecture: Platform is a registry/dispatcher, while Run is a server-scoped RPC worker that proves possession of a component token, registers a live session, heartbeats, and claims work only while that session lease is current.
The recent platform-side builder work already removed distribution builds from machine-side Run authority. This change continues that direction by removing durable run endpoint rows from first-party server runtime routing. Redis becomes the required runtime registry for local development, tests, and production so that all environments exercise the same lease and expiry behavior.
## Goals / Non-Goals
**Goals:**
- Replace durable run endpoint routing with Redis-backed runtime session leases.
- Route runtime jobs by logical target (`serverInstanceId`, `componentKind`, optional `componentKey`) instead of endpoint rows.
- Keep server creation, deployment definitions, component auth keys, jobs, logs, artifacts, and audits durable in the database.
- Make runtime online state, capabilities, capacity, session token hashes, and heartbeat freshness ephemeral and TTL-backed.
- Require Redis in development and tests; avoid memory-only behavior hiding lease/routing bugs.
- Preserve platform-owned distribution builds and prevent generated Runs from receiving build authority.
**Non-Goals:**
- Do not introduce cloud hosting, SSH provisioning, billing, or external marketplace workflows.
- Do not store raw credentials, host paths, sockets, or plaintext component keys in Redis.
- Do not move durable job history, audit events, artifacts, or server definitions out of the database.
- Do not re-add a `run/` source tree to this repository.
## Decisions
### 1. Redis is the only runtime registry for dev, test, and production
Platform SHALL require Redis for runtime registry behavior in every normal environment. Tests may start an isolated Redis instance or use an explicit test Redis database/prefix, but they SHALL NOT swap in a memory registry for ordinary execution.
This keeps expiration, reconnect, session loss, and multi-process behavior visible during development. An in-memory registry would be simpler, but it would let tests pass with semantics that fail once Platform runs more than one process or restarts.
### 2. Registry data is ephemeral and TTL-owned
Redis stores only live-session data:
- `runtime:v1:session:{serverInstanceId}:{componentKind}:{componentKey}`
- `runtime:v1:token:{tokenHash}`
- optional short-lived indexes for online summaries and capability snapshots
Every key has a TTL derived from the heartbeat interval plus a small grace window. Platform startup does not scan or clean Redis. Stale sessions expire naturally, and a Redis flush/restart is treated like all Runs temporarily went offline until they register again.
### 3. Component token identity replaces endpoint ownership
A generated Run hello includes `serverInstanceId`, `componentKind=run`, component key/generation, registration proof, version, target OS/arch, capabilities, and capacity. Platform authenticates the component key from durable database state, then writes a Redis session lease. The accepted session token is scoped to that server/component and is used for heartbeat, claim, ack, progress, result, logs, and artifact operations.
The system does not need a pre-existing endpoint row or a server-to-endpoint foreign key. If two Runs present the same component identity, the later valid registration supersedes the previous session by rotating the token lease.
### 4. Jobs target logical components, not endpoints
Durable jobs use a logical target:
- `serverInstanceId`
- `targetComponentKind` such as `run`, `client-manager`, or `platform-builder`
- `targetComponentKey` for keyed components, empty for the server Run
Run job claim authenticates the session token, derives the server/component target from Redis, and returns only eligible jobs for that target. Leases remain durable on the job record so retries and audit history survive Platform restarts.
### 5. UI shows runtime connection health, not endpoint selection
Platform Web replaces endpoint selection/status surfaces with server runtime connection health. The user sees whether the generated Run is registered, heartbeat freshness, version, target OS/arch, capabilities, current capacity, and safe unavailable reasons. The UI does not ask the owner to choose a run endpoint when creating, editing, or deploying a server.
### 6. Compatibility is staged but not permanent
Existing code paths that accept `runEndpointId` become compatibility shims during migration. They should either translate to logical targets where safe or return a clear deprecation validation error in first-party workflows. New code must not create `RunEndpoint` rows for machine-side generated Runs.
## Risks / Trade-offs
- [Redis unavailable blocks runtime routing] -> Treat Redis as required platform infrastructure; startup/health checks must report runtime registry unavailable and runtime operations must fail safely without dispatching jobs.
- [Redis restart marks healthy Runs offline until reconnect] -> Runs already heartbeat frequently; clients retry registration when heartbeat fails or receives an unknown-session response.
- [Large migration surface] -> Move in phases: registry interface first, job target fields second, UI/API cleanup third, repository deletion last.
- [Old endpoint-based packages reconnect] -> Compatibility can accept legacy registration only behind explicit migration rules; generated packages should be rebuilt with server/component identity.
- [Tests become slower with Redis] -> Use a test Redis prefix/database and cleanup by prefix in test setup, while still relying on TTL behavior for session expiry scenarios.
## Migration Plan
1. Add Redis configuration, health checks, and test harness support; fail fast when Redis is unavailable.
2. Introduce `RuntimeSessionRegistry` backed by Redis and move Run hello/heartbeat/token validation onto it.
3. Add durable job logical target fields while temporarily writing both logical target and legacy `runEndpointId`.
4. Change job claim, ack, progress, result, log, artifact, config, file, and lifecycle dispatch to authorize through registry-derived server/component targets.
5. Remove first-party server creation/edit/deployment references to run endpoint selection and project runtime connection health from Redis.
6. Migrate or deprecate legacy endpoint-based records; stop creating `RunEndpoint` rows for generated Runs.
7. Remove obsolete repository methods, DTO fields, tests, and documentation once compatibility paths are no longer used.
Rollback is limited to keeping the compatibility shim and disabling new runtime dispatch. Durable server definitions and jobs remain in the database; Redis contains only disposable session leases.
## Open Questions
- Should Redis be required at Platform process startup, or can only runtime routes fail health checks while non-runtime admin pages stay available?
- Which Redis deployment profile should local scripts use by default: Docker Compose service, existing local Redis, or a repo-managed test container?
- How long should the legacy `runEndpointId` compatibility window remain before API fields are removed?