136 lines
11 KiB
Markdown
136 lines
11 KiB
Markdown
# Run Protocol Contracts
|
|
|
|
The platform side of run communication is split into independent contracts.
|
|
|
|
## Control
|
|
|
|
Implemented HTTP JSON routes:
|
|
|
|
- `POST /api/v1/run/control/hello`
|
|
- `POST /api/v1/run/control/heartbeat`
|
|
- `POST /api/v1/run/control/events`
|
|
|
|
Named control DTOs:
|
|
|
|
- `RunHelloRequest`
|
|
- `RunHelloResponse`
|
|
- `RunHeartbeatRequest`
|
|
- `RunHeartbeatResponse`
|
|
- `RunControlStreamRequest`
|
|
- `RunControlEvent`
|
|
- `RunCapabilityReport`
|
|
- `RunCapacityReport`
|
|
|
|
Control payloads must remain small and must not include logs, artifact chunks, host paths, raw credentials, direct sockets, job assignments, execution input, or long task results. Hello creates or updates run endpoint metadata and issues an in-memory platform session token. A server-scoped generated Run must use the endpoint identity reserved for its server; Platform rejects a valid component key presented for another endpoint. Registration is binding/authentication only for generated Run bootstrap and must not enqueue lifecycle or status jobs merely because Run appeared. Heartbeat requires that active session token and may request capability refresh when the fingerprint changes. The control event stream is a signed Run-only `text/event-stream` wake channel; events such as `job.changed` only tell Run to claim durable work through `/run/jobs/claim`.
|
|
|
|
Capacity reports include bounded `maxJobs`, `runningJobs`, `queuedJobs`, `logBacklogBatches`, `artifactBacklogChunks`, and enumerated pressure codes. They report queue and spool counts only, never log bodies, artifact chunks, machine paths, PIDs, sockets, credentials, or transport endpoints.
|
|
|
|
Control is the highest-priority run/platform path. Artifact/file transfer load must not delay heartbeat acceptance or mutate heartbeat capacity state through heavy payload fields.
|
|
|
|
## Job
|
|
|
|
Implemented HTTP JSON routes:
|
|
|
|
- `POST /api/v1/run/jobs/claim`
|
|
- `POST /api/v1/run/jobs/ack`
|
|
- `POST /api/v1/run/jobs/progress`
|
|
- `POST /api/v1/run/jobs/result`
|
|
- `POST /api/v1/run/jobs/cancel`
|
|
- `POST /api/v1/run/jobs/reconcile`
|
|
|
|
Named job DTOs:
|
|
|
|
- `RunJobClaimRequest`
|
|
- `RunJobClaimResponse`
|
|
- `RunJobAckRequest`
|
|
- `RunJobProgressRequest`
|
|
- `RunJobResultRequest`
|
|
- `RunJobCancelPollRequest`
|
|
- `RunJobReconcileRequest`
|
|
- `RunJobReconcileResponse`
|
|
|
|
Jobs must carry bounded metadata such as `jobId`, `runEndpointId`, `serverInstanceId`, `capability`, `idempotencyKey`, lease token, attempt, progress, terminal state, message, error code, and result reference. Job payloads must not carry logs, artifact chunks, host paths, raw credentials, direct sockets, or large inline result bodies. Current Run workers use the persistent control event stream for wakeups and keep claim/ack/progress/result as the durable lease and execution channel; bounded claim long-poll remains a compatibility fallback for older workers.
|
|
|
|
Plugin lifecycle assignments may add only a validated plugin identifier, enumerated lifecycle operation, target version, and logical workspace scope. Explicit operator-requested install, enable, disable, upgrade, rollback, retire, dependency-check, and bounded lifecycle commands remain Platform-authorized jobs. Generated Run package startup is not dependent on registration-time job assignment; it is driven by the autonomous lifecycle plan embedded by the platform builder. Assignments cannot carry arbitrary shell, provider configuration, raw credentials, host paths, PIDs, sockets, DSNs, or RCON secrets.
|
|
|
|
Approved `config.write` and bounded `files.list`/`files.read`/`files.write` assignments carry logical directory keys, file keys, scoped refs, and compare-and-swap revision/checksum inputs. Run executes them inside its scoped workspace with atomic writes and returns bounded logical result metadata; resolved machine paths remain Run-local.
|
|
|
|
Job ack, progress, cancellation polling, reconciliation, and terminal result calls are lightweight lifecycle metadata. They must remain valid while artifact chunks or log retries are pending, and duplicate equivalent terminal results remain idempotent under channel pressure.
|
|
|
|
## Generated Run autonomous lifecycle plan
|
|
|
|
Platform-owned Run distribution builds embed an autonomous lifecycle plan for the server-scoped generated Run. The plan carries the server/plugin identity, selected runtime profile, target OS/architecture/release, plugin lifecycle action refs, dependency probes/install plans, plugin-declared runtime log sources such as supervised process channels and file tails, optional DLL extension plans, runtime bindings, and redacted deployment inputs. The builder includes the same JSON as internal build input and as `.platform/autonomous-lifecycle-plan.json` in the generated workspace seed. Run reads this package-local plan on startup and performs plugin-declared init, dependency verification/install, install-if-needed, readiness/status, and start behavior locally before reporting observed state back to Platform.
|
|
|
|
The plan is build input for the generated package, not a machine-side job-channel payload. Generated Run registration must not be treated as a trigger to enqueue `process.start`, `process.install`, or `process.status` work; Platform state converges from Run heartbeats, logs, lifecycle reports, supervised process facts, and terminal job/report messages. Platform and Run must not add game-specific hardcoding to interpret the plan.
|
|
|
|
Autonomous lifecycle reports use `POST /api/v1/run/lifecycle/report` with the active Run session and signed envelope when required. The route accepts only bounded terminal lifecycle facts for `process.install`, `process.start`, `process.stop`, or `process.status`; it validates the server/run binding, records bounded evidence, and projects server state from Run-reported process facts without creating or completing a Platform job. A managed-process report includes an opaque `managedProcessId`, monotonic `observationSeq`, and `observedAt`; retries are idempotent and a lower sequence cannot regress a newer fact for that process.
|
|
|
|
## Log Ingest
|
|
|
|
|
|
Implemented HTTP JSON routes:
|
|
|
|
- `POST /api/v1/run/logs/batches`
|
|
- `POST /api/v1/log-streams/query`
|
|
|
|
Named log DTOs:
|
|
|
|
- `LogBatchIngestRequest`
|
|
- `LogBatchIngestResponse`
|
|
- `RunLogStreamProgressRequest` / `RunLogStreamProgressResponse`: signed Run-only sequence recovery for a server-bound `run.<endpoint>.<server>.*` stream. The response contains only the latest acknowledged sequence.
|
|
- `LogEntry`
|
|
- `LogStreamCursorRequest`
|
|
- `LogStreamCursorResponse`
|
|
- `LogStreamEventResponse`
|
|
|
|
Log ingest supports bounded batches, sequence ranges, checksum validation, retry-safe duplicate acknowledgement, latest sequence tracking, cursor query, and browser SSE fan-out from already-ingested platform logs. Log payloads must not carry artifact chunks, host paths, raw credentials, direct sockets, or unbounded inline data.
|
|
|
|
Run-assigned Platform jobs use `job.<jobId>.<streamKey>` log stream IDs. Autonomous lifecycle bootstrap is Run-owned machine execution rather than a Platform job, so its durable process logs use `run.<runEndpointId>.<serverInstanceId>.<streamKey>`. Platform may auto-create those streams only after validating the active Run session and the server-to-Run binding. For retry compatibility, legacy spooled `job.autonomous-*.<streamKey>` batches are accepted as Run-owned autonomous streams without creating or completing a Platform job.
|
|
|
|
Log ingest is durable and independently retried. Artifact/file transfer backlog must not prevent log batch acknowledgement, duplicate acknowledgement, cursor state updates, or spool cleanup.
|
|
|
|
The platform stores log stream metadata through `repo.Store` and stores log bodies through the configured `LogBodyStore`. The default `file` backend persists platform metadata to `PLATFORM_METADATA_PATH`, keeps high-frequency Run endpoint/session runtime state in a lightweight sibling runtime snapshot, and appends log entries to segmented JSONL files under `PLATFORM_LOG_DIR`; the `memory` backend is only for tests and disposable local development. MySQL/Postgres are appropriate for platform metadata, stream state, retention policy, indexes, and operational records, but should not be the primary row-per-log-line store for hundreds or thousands of servers. Production log bodies should move behind the same boundary to append/query backends such as ClickHouse, Loki, OpenSearch/Elasticsearch, or object-storage segments with compact indexes.
|
|
|
|
## Artifact
|
|
|
|
Implemented HTTP routes:
|
|
|
|
- `POST /api/v1/run/artifacts/open`
|
|
- `POST /api/v1/run/artifacts/chunks`
|
|
- `POST /api/v1/run/artifacts/status`
|
|
- `POST /api/v1/run/artifacts/complete`
|
|
|
|
Named artifact DTOs:
|
|
|
|
- `ArtifactTransferOpenRequest`
|
|
- `ArtifactTransferOpenResponse`
|
|
- `ArtifactChunkUploadRequest`
|
|
- `ArtifactChunkUploadResponse`
|
|
- `ArtifactTransferStatusRequest`
|
|
- `ArtifactTransferStatusResponse`
|
|
- `ArtifactTransferCompleteRequest`
|
|
- `ArtifactTransferCompleteResponse`
|
|
- `ArtifactResponse`
|
|
|
|
Artifact upload supports active run session validation, job/server-instance owner scoping, bounded octet-stream chunk bodies, per-chunk checksum validation, duplicate chunk acknowledgement, resume status, and final checksum verification before an artifact becomes available. Artifact transport is separate from control, job result, log ingest, plugin bridge, and browser file APIs.
|
|
|
|
Artifact/file transfer is the lower-priority heavy channel. Chunk upload and completion must not block control heartbeat, job ack/result delivery, cancellation/reconcile calls, or log ingest acknowledgement. Lightweight routes must reject heavy transfer payloads instead of accepting or storing them.
|
|
|
|
## Server File Manager Transfer
|
|
|
|
Implemented HTTP JSON routes:
|
|
|
|
- `POST /api/v1/run/files/input-chunk`
|
|
|
|
Browser-facing file management uses server-instance scoped routes on Platform for workspace, list, read, write, upload, and download preparation. The browser only sends plugin-declared logical directory keys, logical file keys, relative names, inline text for small edits, or platform-owned `artifact://` input refs. It never receives or sends host paths, direct Run sockets, Run sessions, job lease tokens, storage credentials, or raw machine endpoints.
|
|
|
|
`files.list` jobs return a bounded `file.list` execution result containing logical entries. `files.read` jobs may return a bounded inline `file.read` result for editable text or a `resultRef` pointing to a platform artifact for larger content. Browser downloads are prepared through Platform and then read in bounded chunks using the platform artifact download contract.
|
|
|
|
Browser uploads are first staged as server-instance artifacts. Platform then queues a `files.write` job whose `inputRef` is `artifact://<id>` and whose execution input names the dedicated `run-file-transfer` channel. Run pulls those bytes through `POST /api/v1/run/files/input-chunk` while proving the active endpoint session plus job attempt and lease. The chunk route is fenced to the active file-write job, validates artifact ownership/checksum, and returns bounded byte ranges only.
|
|
|
|
File-manager transfer is a separate, low-priority heavy path. Slow uploads, downloads, retries, or file input chunk pulls must not block control heartbeat, job claim/ack/progress/result/cancel/reconcile, durable log batch ingest, or artifact upload acknowledgements. Control, jobs, logs, artifacts, file transfer, and optional game-client bridge remain independently backpressured channels.
|
|
|
|
## Game Client Bridge
|
|
|
|
The optional game client bridge is separate from run lifecycle, control registration, job handling, log ingest, and artifact transport. Platform exposes operator-scoped command queues, snapshots, query templates, and plugin-owned typed records; it does not expose a component-session companion channel.
|