Files
browser/platform/api/routes.md
T
npc0-hue 05f5a97ba9 Add graceful SCUM stop, restart, and version update flow
SCUM 停止/重启/更新以前只有“结束进程”这一条路,插件没有声明任何优雅关闭方式,
平台也没有把停止后重新启动串起来。现在插件声明自己的关闭脚本,run 先执行它,
平台在停止或更新成功后再自动拉起服务。

run:
- lifecycle stop 支持插件声明的 gracefulStop(可执行文件、参数、环境、超时、
  fallback=report|terminate);关闭命令超时且声明 report 时任务失败,不再默默杀进程。
- 新增 steam.update 依赖探针:调用 steamcmd +app_info_print 获取公开分支 buildid,
  与本地 steamapps/appmanifest_<appid>.acf 的 buildid 比较,输出
  installed/latest/update=yes|no|unknown。

platform:
- 新增 POST /api/v1/server-instances/{id}/restart 与 /update。
- restart 派发插件 stop 动作(走优雅关闭),终态成功后入队 start 作业。
- update 派发插件 install 动作;插件在更新前必须先优雅关闭 SCUM,关闭失败直接拒绝
  SteamCMD 更新,成功后平台再拉起服务。
- 依赖检查输入带上插件声明的服务器安装根目录,供 steam.update 读取 appmanifest。

plugin (SCUM server plugin 0.1.16):
- bin/scum-stop.cmd:解析已声明的可执行文件路径,定位同路径正在运行的 SCUMServer.exe,
  通过本地 RCON 公告并发送关闭命令,等待进程自行退出;不再使用 taskkill。
- bin/scum-rcon.ps1:插件自有的 Source RCON 客户端,从 UE4SS mod config.ini 读取
  密码/端口,密钥不离开本机。
- actions/stop.json 声明 gracefulStop;actions/install.json 更新前先执行同一关闭脚本。

platform_web:
- 服务器详情新增“重启”按钮和“SCUM 版本更新”面板;点“检查更新”查询公开分支版本,
  只有检测到更新时“更新版本”按钮才会置为可用并高亮,点击后先确认再派发更新任务。
2026-09-15 13:30:15 +08:00

36 KiB

Platform API Route Catalog

Route declarations and handler comments live in platform/api. Request, response, list, and error contracts live in platform/dto; handlers must call platform/service.Core rather than repositories directly.

Implemented Core Resource Routes

Routes use JSON request and response bodies unless a route explicitly accepts file upload multipart form data. Collection routes support GET for lists and POST for create. Detail routes support GET by ID. Unsupported methods return dto.ErrorResponse with 405.

Resource Collection Detail DTO contracts
Users GET /api/v1/users, POST /api/v1/users GET /api/v1/users/{id}, PUT /api/v1/users/{id} UserCreateRequest, UserUpdateRequest, UserResponse, UserListResponse
AI providers GET /api/v1/ai-providers, POST /api/v1/ai-providers, POST /api/v1/ai/invocations, POST /api/v1/ai/config-suggestions GET /api/v1/ai-providers/{id}, PUT /api/v1/ai-providers/{id} AIProviderCreateRequest, AIProviderUpdateRequest, redacted AIProviderResponse, AIProviderListResponse, AIInvocationRequest, AIInvocationResponse, LlmConfigSuggestionRequest, LlmConfigSuggestionResponse
Game plugins GET /api/v1/game-plugins, POST /api/v1/game-plugins GET /api/v1/game-plugins/{id} GamePluginCreateRequest, GamePluginResponse, GamePluginListResponse
Plugin marketplace GET /api/v1/plugin-marketplace/plugins GET /api/v1/plugin-marketplace/plugins/{id}, POST /api/v1/plugin-marketplace/plugins/{id}/state MarketplacePluginResponse, MarketplacePluginListResponse, MarketplacePluginStateRequest
Plugin bridge POST /api/v1/plugin-bridge/authorize, POST /api/v1/plugin-bridge/execute n/a PluginBridgeAuthorizeRequest, PluginBridgeAuthorizeResponse, PluginBridgeExecuteRequest, PluginBridgeExecuteResponse
Server instances GET /api/v1/server-instances, POST /api/v1/server-instances GET /api/v1/server-instances/{id}, PUT /api/v1/server-instances/{id}, DELETE /api/v1/server-instances/{id} ServerInstanceCreateRequest, ServerInstanceUpdateRequest, ServerInstanceResponse, ServerInstanceListResponse
Server runtime distribution n/a GET /api/v1/server-instances/{id}/runtime/actions, POST /api/v1/server-instances/{id}/run/generate, POST /api/v1/server-instances/{id}/run/download, POST /api/v1/server-instances/{id}/run/key/reset, POST /api/v1/server-instances/{id}/run/update, GET /api/v1/server-instances/{id}/run/update, GET /api/v1/server-instances/{id}/dependencies, POST /api/v1/server-instances/{id}/dependencies/check, POST /api/v1/server-instances/{id}/dependencies/install ServerRuntimeActionsResponse, RunDistributionGenerateRequest, RunDistributionResponse, RunUpdateRequest, RunUpdateJobResponse/RunUpdateJobListResponse, ComponentKeyResetRequest, ComponentKeyResponse, DependencyCatalogResponse, DependencyJobRequest
Metrics GET /api/v1/metrics/platform, GET /api/v1/metrics/server-instances n/a PlatformResourceUsageResponse, ServerMetricsResponse, ServerMetricsListResponse
File operations POST /api/v1/file-operations/dispatch n/a FileOperationDispatchRequest, FileOperationDispatchResponse
Server file manager GET /api/v1/server-instances/{id}/files/workspace, POST /api/v1/server-instances/{id}/files/browse, GET /api/v1/server-instances/{id}/files/list, POST /api/v1/server-instances/{id}/files/refresh, POST /api/v1/server-instances/{id}/files/read, POST /api/v1/server-instances/{id}/files/write, POST /api/v1/server-instances/{id}/files/upload, POST /api/v1/server-instances/{id}/files/download GET /api/v1/server-instances/{id}/files/read-snapshot ServerFileWorkspaceResponse, ServerFileListResponse, DeclaredFileReadSnapshotResponse, ServerFileReadRequest, ServerFileWriteRequest, ServerFileUploadResponse, ServerFileDownloadRequest, ServerFileDownloadResponse
Plugin-owned data n/a GET/PUT/DELETE /api/v1/server-instances/{id}/plugin-data/{collection}, POST .../plugin-data/{collection}/transaction PluginDataPutRequest, PluginDataTransactionRequest, PluginDataRecordResponse, PluginDataListResponse
Server administrators GET /api/v1/server-instances/{id}/administrators/candidates, POST /api/v1/server-instances/{id}/administrators DELETE /api/v1/server-instances/{id}/administrators/{userId} ServerMemberRequest, ServerMemberResponse, ServerMemberListResponse, ServerInstanceResponse
Run endpoints GET /api/v1/run/endpoints, POST /api/v1/run/endpoints GET /api/v1/run/endpoints/{id} RunEndpointCreateRequest, RunEndpointResponse, RunEndpointListResponse
Jobs GET /api/v1/jobs, POST /api/v1/jobs GET /api/v1/jobs/{id} JobCreateRequest, JobResponse, JobListResponse
Artifacts GET /api/v1/artifacts, POST /api/v1/artifacts GET /api/v1/artifacts/{id}, POST /api/v1/artifacts/{id}/download, GET /api/v1/artifacts/{id}/content ArtifactCreateRequest, ArtifactResponse, ArtifactListResponse, ArtifactDownloadReferenceResponse, ArtifactContentRequest
Log streams GET /api/v1/log-streams, POST /api/v1/log-streams GET /api/v1/log-streams/{id} LogStreamCreateRequest, LogStreamResponse, LogStreamListResponse

Plugin-owned data is an independent, server-scoped plugin store. It is not a projection of the game-server database and never aliases platform user/auth storage.

Implemented Query Filters

  • GET /api/v1/users?status=active
  • GET /api/v1/ai-providers?kind=openai&status=active
  • GET /api/v1/game-plugins?serverType=scum&status=installed
  • GET /api/v1/plugin-marketplace/plugins?serverType=scum&status=installed&capability=logs.read&keyword=scum
  • GET /api/v1/server-instances?pluginId=server.scum&runEndpointId=run-local&state=draft
  • GET /api/v1/server-instances?state=deleted
  • GET /api/v1/metrics/server-instances
  • GET /api/v1/run/endpoints?status=online
  • GET /api/v1/jobs?serverInstanceId=server-1&runEndpointId=run-local&state=queued
  • GET /api/v1/jobs?serverInstanceId=server-1&states=queued,running,failed&limit=100
  • GET /api/v1/artifacts?ownerKind=job&ownerId=job-1&state=uploading
  • GET /api/v1/log-streams?serverInstanceId=server-1&streamKey=stdout

Implemented Authentication And Current User Actions

  • POST /api/v1/auth/register: accept RegisterRequest; the first registered account becomes an active platform administrator with an authenticated session, while later registrations create pending low-privilege users and return AuthSessionResponse with status=pending and no session token.
  • POST /api/v1/auth/login: accept LoginRequest and authenticate an active user by ID or email. Strict production routes set an HttpOnly SameSite cookie and omit the raw token from JSON; explicit CLI callers may request a bearer response with X-Auth-Token-Response: bearer.
  • POST /api/v1/auth/logout: invalidate the active bearer session token and return 204.
  • POST /api/v1/auth/rotate: durably revoke the current bearer generation and return a new bounded session token and expiry.
  • GET /api/v1/users/current: return CurrentUserResponse for the bearer session.
  • PUT /api/v1/users/current/profile: update bounded current-user profile fields using UserProfileBody.
  • PUT /api/v1/users/current/theme: persist current-user console theme preferences using UserThemePreferenceRequest.

Bearer sessions are stored as SHA-256 verifiers with issued/expiry/revocation timestamps and rotation generation; raw tokens are never written to FileStore/MySQLStore snapshots. Browser sessions use HttpOnly SameSite cookies, while explicit CLI bearer mode returns the token once. Production router construction requires authentication for sensitive API paths, reserves user/provider/plugin install/Run endpoint/global create operations for platform administrators, and repeats server/job/log/artifact ownership checks in services. After the first account exists, public registration defaults to pending plus server-scoped roles and does not grant platform administrator privileges. A bootstrap administrator is created only when PLATFORM_BOOTSTRAP_ADMIN_PASSWORD is explicitly configured; local debug scripts provide their own development-only value.

Implemented Role-Scoped Server Access

  • User-facing server instance list, detail, create, and lifecycle routes require a bearer session.
  • Platform administrators can view and manage all server instances.
  • Server owners and server administrators can only view and manage server instances they own or administer.
  • Server instance responses include bounded ownerUserId and adminUserIds membership metadata.
  • GET /api/v1/server-instances/{id}/administrators/candidates: lets the server owner list active non-platform-admin users that can be invited.
  • POST /api/v1/server-instances/{id}/administrators: lets the server owner invite an active non-platform-admin user using ServerMemberRequest.
  • DELETE /api/v1/server-instances/{id}/administrators/{userId}: lets the server owner remove a server-scoped administrator. The route never deletes the user account.

Server owner membership actions hide and reject platform administrators. Server administrators cannot invite or remove administrators unless they also own the target server.

Implemented Observability And Config Read Actions

  • GET /api/v1/metrics/platform: returns bounded platform CPU, memory, disk, source, and timestamp metadata for platform administrators.
  • GET /api/v1/metrics/server-instances: returns bounded per-server metrics only for server instances visible to the authenticated user. Server-scoped raw config routes are intentionally not registered as product APIs. AI config assistance returns reviewable typed diff metadata through AI config-diff approval flows rather than raw config text.

Observability responses are read-only. They do not expose host filesystem paths, raw credentials, direct run sockets, storage backend credentials, raw AI provider keys, or run session tokens.

Implemented File Dispatch Actions

  • POST /api/v1/file-operations/dispatch: accepts FileOperationDispatchRequest, validates server visibility plus optional plugin permissions, rejects unsafe targets, and queues files.read or files.write jobs using logical keys and refs.

File dispatch responses expose only logical target keys, scoped input/artifact refs, and bounded job metadata. They do not expose host filesystem paths, raw credentials, direct sockets, run session tokens, raw AI provider keys, or inline large file contents.

Implemented AI Provider Management Actions

  • POST /api/v1/ai-providers/{id}/status: enable or disable one provider using AIProviderStatusRequest.
  • POST /api/v1/ai-providers/{id}/test: invoke the configured provider client with a bounded health request and return only a redacted AIProviderTestResponse.
  • GET /api/v1/ai-providers/{id}/models: return configured model names using AIProviderModelsResponse.
  • POST /api/v1/ai/invocations: accept AIInvocationRequest, authorize explicit purposes, select an active provider, invoke a mockable provider client, and return AIInvocationResponse with bounded recommendation text, usage metadata, optional reviewable config recommendation, and safe errors.
  • POST /api/v1/ai/config-suggestions: compatibility route for console config assistance. It uses the mediated invocation service with purpose=config.suggest and returns LlmConfigSuggestionResponse for the existing review/approval workflow.

AI invocation is platform-mediated. PLATFORM_AI_PROVIDER_MODE=live uses the Platform-owned HTTP client and environment secret resolver; local verification explicitly uses mock. Invocation responses do not expose provider base URLs, API key refs, raw keys, bearer tokens, host paths, run sockets, or storage credentials. Config suggestions persist an expiring diff and never dispatch run-side writes before separate approval. AI Provider management responses also return only baseUrlConfigured and apiKeyConfigured; an empty base URL or secret reference in an update preserves the Platform-owned value instead of round-tripping it through the browser.

AI provider management responses expose apiKeyConfigured only. Create/update requests may carry a scoped secret reference, and a blank update preserves an existing configured secret; the stored reference is not returned to the browser.

Implemented Game Plugin Registry Actions

  • POST /api/v1/game-plugins/register-manifest: accept GamePluginManifestRegistrationRequest, validate a game management plugin manifest, and persist installed registry metadata using GamePluginResponse.

Plugin registry responses include identity, description, version, server type/display metadata, manifest and create-form schema references, required run capabilities, declared scoped permissions, aggregate platform permissions, lifecycle action references, plugin pages, tags, AI purposes, validation violations for invalid records, and install status. They do not expose raw host paths, raw credentials, direct run sockets, or raw AI provider keys.

Implemented Plugin Marketplace Actions

  • GET /api/v1/plugin-marketplace/plugins: list bounded marketplace plugin summaries projected from installed registry metadata. Optional filters include status, serverType, capability, and keyword.
  • GET /api/v1/plugin-marketplace/plugins/{id}: return one marketplace plugin detail using manifest-backed registry metadata.
  • POST /api/v1/plugin-marketplace/plugins/{id}/state: accept MarketplacePluginStateRequest with install, enable, or disable and update registry install state only.

Marketplace responses include game management plugin identity, version, display metadata, server type, installed state, capabilities, pages, permissions, tags, AI purposes, and validation violations. They are a platform registry projection, not a commerce catalog, and they do not include billing, pricing, ratings, reviews, cloud host sales, raw credentials, host paths, direct run sockets, package bytes, or raw AI provider keys.

Marketplace state actions are metadata-only in this change. install and enable mark the registered plugin installed; disable marks it disabled. These actions do not download external packages, create run jobs, execute plugin code, write files, or contact external services.

Marketplace catalog state remains separate from production lifecycle installations. Server-bound install/enable/disable/upgrade/rollback/retire operations use the production lifecycle routes below.

Plugin Operations

  • GET /api/v1/plugin-lifecycles: list server-bound plugin lifecycle installations.
  • POST /api/v1/plugin-lifecycles/{pluginId}/actions: validate manifest declaration, compatibility, confirmation, and idempotency before creating one durable Run job.
  • GET /api/v1/ai/config-diffs: list reviewable AI config recommendations visible to the session.
  • POST /api/v1/ai/config-diffs/{id}/approve: revalidate actor/server/config revision/checksum/expiry and dispatch exactly one bounded config.write job.

These responses expose logical IDs, states, safe diagnostics, and job links only. They never project raw credentials, provider transport configuration, Run sessions/endpoints, host paths, PIDs, sockets, DSNs, or RCON material.

Implemented Plugin Bridge Actions

  • POST /api/v1/plugin-bridge/authorize: accepts PluginBridgeAuthorizeRequest and returns whether an installed plugin page may use one declared bridge action with the effective route permissions.
  • POST /api/v1/plugin-bridge/execute: accepts PluginBridgeExecuteRequest, repeats backend validation and authorization, and executes only mapped platform-mediated actions. Supported execution currently includes server context reads, lifecycle job dispatch for declared run capabilities, log cursor metadata queries, scoped file dispatch, artifact open references, and platform-mediated AI invocation.

Bridge execution responses are typed envelopes with requestId, plugin/page/server scope, action, status, result refs, and safe error codes. They do not expose bearer tokens, run sockets, host filesystem paths, raw credentials, storage backend credentials, provider base URLs, raw AI provider keys, or unbounded file/log contents.

Artifact bridge execution returns safe metadata and platform content routes only. It does not return artifact bytes through the bridge message and does not expose run endpoints, storage adapter paths, presigned backend URLs, host paths, or credentials.

Implemented Server Lifecycle Actions

  • POST /api/v1/server-instances/workflows/create: accepts ServerLifecycleCreateRequest. Creation starts from pluginId and name, and may include the create-wizard deployment definition such as deployment mode, plugin create inputs, server root, or custom start command. The browser does not submit a deployment target, Run endpoint, lifecycle profile, or Run identity binding. Platform applies plugin defaults, creates the definition without waiting for a Run, and attaches the active Run when its authenticated heartbeat arrives. Unknown legacy binding fields are rejected by the strict JSON decoder.
  • POST /api/v1/server-instances/{id}/start: accept ServerLifecycleCommandRequest, validate state/config version/run capability, and queue a process.start job using ServerLifecycleResponse.
  • POST /api/v1/server-instances/{id}/stop: accept ServerLifecycleCommandRequest, validate state/config version/run capability, and queue a process.stop job using ServerLifecycleResponse.
  • POST /api/v1/server-instances/{id}/restart: accept ServerLifecycleCommandRequest, validate the instance is running or stopped with a matching config version and run capability, and queue the plugin-declared stop action using ServerLifecycleResponse. When that stop job succeeds, Platform queues the plugin-declared start action for the same instance.
  • POST /api/v1/server-instances/{id}/update: accept ServerLifecycleCommandRequest, validate the instance is running, stopped, ready, or failed with a matching config version and process.install run capability, and queue the plugin-declared install/update action using ServerLifecycleResponse. The plugin install action owns the graceful close of a running server; Platform queues the plugin-declared start action after the update succeeds.

Lifecycle workflow responses include accepted status, action, bounded server instance metadata, and bounded job metadata. They do not expose run credentials, host paths, raw credentials, AI provider keys, direct sockets, plugin action file contents, or large result bodies.

Implemented Runtime Distribution Actions

  • GET /api/v1/server-instances/{id}/runtime-binding: returns the visible server's selected profile and redacted logical binding readiness. Values are represented only by configured/secret-backed flags.
  • PUT /api/v1/server-instances/{id}/runtime-binding: lets the server owner or a platform administrator select a declared profile and patch safe logical refs for non-deleted servers. Undeclared keys, unsafe paths/sockets/credentials, and plaintext secrets are rejected.
  • GET /api/v1/server-instances/{id}/runtime/actions: returns the current user-visible runtime action matrix for the server, including run endpoint status, action availability, and safe unavailable reasons.
  • POST /api/v1/server-instances/{id}/run/generate: accepts RunDistributionGenerateRequest, queues a platform-owned Docker build, creates or reuses the server's current encrypted run key, writes that key into the secret-bearing generated package config, publishes an artifact, and returns RunDistributionResponse with checksum, key generation, artifact ID, build job ID, and redacted secret ref only.
  • POST /api/v1/server-instances/{id}/run/download: opens the latest available run package through ArtifactDownloadReferenceResponse after server-scoped authorization.
  • POST /api/v1/server-instances/{id}/run/key/reset: resets the server's single active run key, increments generation, revokes previous run packages, and returns ComponentKeyResponse.
  • POST /api/v1/server-instances/{id}/run/update: accepts RunUpdateRequest with an approved artifact ID/checksum and queues a bounded run.self-update job through RunUpdateJobResponse.
  • GET /api/v1/server-instances/{id}/run/update: lists safe update phase, target, progress message, artifact checksum, release identity, rollback, and summary for the authorized server.
  • GET /api/v1/server-instances/{id}/dependencies: returns the target-matched plugin/profile dependency catalog, current safe probe status/evidence, typed plan summaries, and deterministic immutable plan digests.
  • POST /api/v1/server-instances/{id}/dependencies/check: accepts DependencyJobRequest and queues a dependencies.check run job for a declared logical probe key.
  • POST /api/v1/server-instances/{id}/dependencies/install: accepts DependencyJobRequest with an install plan key and the exact catalog planDigest; stale/missing digests are denied before job creation. Server-scoped terminal log streaming (GET /api/v1/server-instances/{id}/logs/events) is registered for the server detail terminal drawer and emits platform-accepted live log SSE events only. It does not replay retained log entries or interpret their body; the raw log list/backfill routes (logs/live and logs/backfill) remain unavailable as product APIs, and internal log ingest and cursor query remain available for run/platform maintenance flows.

Runtime distribution APIs require the current bearer session, server visibility, plugin-declared permissions, complete runtime bindings only for actions that truly depend on external logical bindings, and platform-builder readiness. Run-side lifecycle commands separately require run endpoint capability support and use plugin-declared lifecycle actions without making manual runtime-profile binding a user prerequisite. Responses and summaries expose artifact IDs, job IDs, checksums, key generations, fingerprints, status, and redacted secret://runtime-keys/.../current refs only. They do not expose raw run keys, FTP passwords, database DSNs, RCON passwords, host paths, direct sockets, run endpoint private addresses, build workspace paths, or large inline logs.

SCUM product APIs expose platform-owned scum_user, trajectory, vehicle, and lock table records plus typed operation/workflow requests, approval status, confirmation status, blocker reasons, and bounded summaries. The browser reads one bounded server surface (GET /api/v1/server-instances/{id}/scum/surface) instead of issuing one list call per table. Signed Run facts arrive through POST /api/v1/run/scum/facts; browser Run endpoint management under /api/v1/run/endpoints keeps the normal bearer/admin authorization while the remaining /api/v1/run/ paths belong to the signed machine channels. These APIs never expose direct game database SQL text, DB paths, DSNs, RCON command text, raw request payloads, run sockets, host paths, or credentials.

POST /api/v1/server-instances/workflows/create requires only the plugin type and server name. A runtime binding may still be maintained internally for advanced logical transports, but browser lifecycle controls must not force operators to choose a runtime profile before start/stop or run-package generation when the plugin deployment/lifecycle declaration is sufficient. Platform builds distributions itself and never needs a registered Run endpoint with distribution.build to do so.

Private Run Dependency And Update Routes

The following signed routes are Run-only and never part of browser/plugin DTOs: POST /api/v1/run/jobs/dependency-input, POST /api/v1/run/jobs/source-rcon-input, POST /api/v1/run/jobs/update-input, POST /api/v1/run/jobs/update-chunk, and POST /api/v1/run/jobs/update-health. They require the current endpoint/session signature; input/chunk calls additionally require active attempt/lease/cancel fencing. Source RCON input returns one queued command exactly once to the active Run lease. Update chunks are bounded to 1 MiB and resolve only an available same-server target-matched Run distribution. Health reports are accepted only after the terminal staged job, matching attempt/lease proof, current online endpoint release, and reconciliation-capable session are verified. These routes never return raw artifact paths, browser download tokens, host paths, credentials, secret refs, or session/lease hashes.

Implemented Run Control Actions

  • POST /api/v1/run/control/hello: accept RunControlHelloRequest, create or update run endpoint metadata, and return RunControlHelloResponse with a platform-issued session token.
  • POST /api/v1/run/control/heartbeat: accept RunControlHeartbeatRequest, require the active session token, update heartbeat metadata, and return RunControlHeartbeatResponse with the next heartbeat hint and optional capability refresh request.
  • POST /api/v1/run/control/events: accept signed RunControlStreamRequest and keep a text/event-stream response open for lightweight Run wakeups such as control.ready, control.heartbeat, and job.changed. The stream never carries job payloads, logs, artifact bytes, host paths, credentials, or direct sockets.

Run control actions carry only lightweight metadata: endpoint ID, display name, version, status, capability fingerprint/list, capacity, session token, and timing hints. They do not carry job bodies, logs, artifact chunks, host paths, raw credentials, or direct sockets. Control is the highest-priority run-facing channel; artifact/file transfer pressure must not delay heartbeat processing or mutate endpoint capacity through heavy payload fields.

Implemented Run Job Actions

  • POST /api/v1/run/jobs/claim: accept RunJobClaimRequest, validate the active Run session, optionally support bounded legacy waitSeconds, sweep expired endpoint work, and durably claim one eligible queued/retrying job with a monotonic per-job attempt, hashed lease credential, ack deadline, and execution lease. Current workers should use the persistent control event stream for wakeups and keep claim as the durable assignment fetch.
  • POST /api/v1/run/jobs/ack: accept RunJobAckRequest, fence endpoint/session generation/attempt/lease, reject late acknowledgements, and move the current attempt into running state.
  • POST /api/v1/run/jobs/progress: accept RunJobProgressRequest, reject stale sequences and expired/old attempts, persist bounded progress, and renew the current execution lease.
  • POST /api/v1/run/jobs/result: accept RunJobResultRequest and write an idempotent terminal result or durable retry-wait transition with capped exponential backoff.
  • POST /api/v1/run/jobs/cancel: accept fenced RunJobCancelPollRequest and return durable pending cancellation intent for the current attempt.
  • POST /api/v1/run/jobs/reconcile: accept persisted Run journal evidence (jobId, attempt, leaseToken), rebind only matching active attempts to the current authenticated session generation, persist reconciliation metadata, retry/cancel platform-active missing work, and return confirmed assignments plus discard IDs.
  • POST /api/v1/jobs/{id}/cancel: authorize the server owner/administrator or platform administrator and durably record cancellation intent; queued/retrying work becomes cancelled immediately while active work completes through fenced Run polling/result.

Run job actions carry bounded job metadata only: job ID, run endpoint ID, server instance ID, capability, idempotency key, lease token, attempt/retry limits, deadlines, progress, terminal state, message, error code, result reference, and timing hints. Raw lease tokens exist only on the signed Run job channel; platform persistence stores their hashes. User-facing Job detail responses expose safe execution-result evidence; Job list responses stay summary-only for hot console polling. Job responses never expose raw/hashed leases, Run sessions, secret refs, host paths, sockets, or credentials. Job ack/progress/result/cancel/reconcile calls remain lightweight and independently valid while log batches or artifact/file chunks are queued, slow, or retrying. Equivalent duplicate terminal results remain idempotent under channel pressure.

Implemented Run File Transfer Actions

  • POST /api/v1/run/files/input-chunk: accept RunFileInputChunkRequest, validate the active Run endpoint session plus the fenced file-write job lease/attempt, and return RunFileInputChunkResponse with a bounded byte range from a server-instance scoped upload artifact.

Run file input chunks are used only for browser-staged file uploads that produce artifact:// job inputs. The route never returns storage backend paths, browser bearer credentials, machine paths, direct sockets, or unrestricted artifact bodies. This channel is lower priority than control, job lifecycle calls, and log ingest.

Implemented Log Ingest Actions

  • POST /api/v1/run/logs/batches: accept LogBatchIngestRequest, validate run session and stream metadata, store contiguous entries verbatim, update LogStream.LatestSeq, and return LogBatchIngestResponse with the acknowledged range.
  • POST /api/v1/log-streams/query: accept LogStreamCursorRequest and return LogStreamCursorResponse with bounded ordered entries after a cursor. Server-scoped SSE log streaming remains available for the terminal drawer. POST /api/v1/log-streams/query remains the bounded cursor contract for internal maintenance/debug reads.

Log ingest actions carry durable log metadata and bounded entries only: run endpoint ID, session token, stream identity, source, sequence range, compression metadata, checksum, entries, and cursor limits. They do not carry artifact chunks, host paths, raw credentials, direct sockets, or unbounded inline data. Log ingest is durable and independently retried. Artifact/file transfer backlog must not prevent log acknowledgement, duplicate acknowledgement, cursor state updates, or spool cleanup.

Platform storage is configured by PLATFORM_STORAGE_BACKEND. The default file backend writes metadata snapshots to PLATFORM_METADATA_PATH and log bodies to segmented files in PLATFORM_LOG_DIR; memory remains available for tests and ephemeral local runs. Log bodies are retained and relayed verbatim; Platform does not parse, redact, filter, or derive plugin records from them. Relational stores such as MySQL/Postgres are reserved for metadata, stream cursors, indexes, retention state, and operational records. High-volume log bodies for hundreds or thousands of servers should use a log-optimized backend behind LogBodyStore, such as ClickHouse, Loki, OpenSearch/Elasticsearch, or object-storage segments.

Implemented Run Artifact Actions

  • POST /api/v1/run/artifacts/open: accept ArtifactTransferOpenRequest, validate active run session and scoped artifact owner, create or reuse uploading artifact metadata, and return ArtifactTransferOpenResponse with transfer resume state.
  • POST /api/v1/run/artifacts/chunks: accept application/octet-stream chunk bytes with transfer metadata in headers, validate chunk range and checksum, store idempotent chunk state, and return ArtifactChunkUploadResponse with acknowledged chunk indexes.
  • POST /api/v1/run/artifacts/status: accept ArtifactTransferStatusRequest and return ArtifactTransferStatusResponse with received chunks and next missing chunk index.
  • POST /api/v1/run/artifacts/complete: accept ArtifactTransferCompleteRequest, verify all chunks and final checksum, mark the artifact available, and return ArtifactTransferCompleteResponse.

Run artifact actions carry bounded upload metadata and raw chunk bodies only: run endpoint ID, session token, transfer ID, artifact ID, owner metadata, chunk indexes, byte ranges, checksums, and application/octet-stream chunk bytes. They do not carry control heartbeat metadata beyond session identity, job result bodies, logs, host paths, raw credentials, direct sockets, or plugin/browser storage credentials. Artifact/file transfer is lower priority than control, job lifecycle metadata, and durable log ingest. Slow or retrying chunks must not block heartbeat, job ack/result delivery, cancellation/reconcile calls, or log batch acknowledgement; lightweight routes reject heavy transfer payloads rather than storing them.

Implemented Browser Artifact Download Actions

  • GET /api/v1/artifacts/{id}: returns authorized artifact metadata for the current bearer session.
  • POST /api/v1/artifacts/{id}/download: returns ArtifactDownloadReferenceResponse with filename, content type, size, checksum, expiry, supported chunk size, and a platform-owned downloadUrl.
  • GET /api/v1/artifacts/{id}/content: streams the full artifact when no range is supplied, or returns a bounded byte range using offset/limit query parameters or a Range: bytes=start-end header. Responses include Content-Length, Accept-Ranges, optional Content-Range, X-Artifact-Checksum, X-Artifact-Content-Checksum, and X-Artifact-Storage headers.

Browser artifact downloads require an available artifact plus user access to the owning job/server context. Platform/plugin-owned artifacts are limited to platform administrators until a future storage policy adds narrower ownership. Current content reads use the private durable artifact body store; external object storage adapters are deferred behind the same service contract. Browser and plugin pages receive only platform routes and integrity metadata, never raw storage backend URLs, host paths, direct run sockets, run tokens, bearer tokens, or storage credentials.

Durable Observability And Scoped Remote Adapters

  • POST /api/v1/run/metrics/batches: accepts a signed bounded metric batch for the Run endpoint's server instances and returns an acknowledgement count.
  • GET /api/v1/metrics/server-instances/history: returns a bounded owner-scoped metric history by server instance and optional time/limit query.
  • GET|POST /api/v1/backups and GET /api/v1/backups/{id}: expose or create safe backup metadata, checksum, artifact reference, retention, and recovery state; body bytes and storage paths remain private.
  • GET|POST /api/v1/server-instances/{id}/remote-adapters: lists manifest-declared adapter capabilities or queues an owner-authorized fenced adapter job using logical target keys. Requests never carry arbitrary shell, socket, host, or credential data.

Metric, backup, log, and artifact records use the configured durable metadata/body stores. Control, job, log, artifact, metric, and remote adapter traffic remain independent channels; slow artifact or adapter retries do not share lightweight heartbeat or job result payloads.

Error Contract

API errors use dto.ErrorResponse:

  • 400: malformed JSON or validation failure.
  • 401: missing or invalid bearer session token.
  • 403: valid credentials for an account that is pending, disabled, or otherwise forbidden.
  • 404: missing resource or missing dependency reported by the service layer.
  • 409: duplicate resource ID.
  • 405: unsupported method on an implemented route.
  • 500: unexpected platform error.

Deferred Route Groups

These route groups remain documented future work beyond the currently implemented routes:

  • Authorization policy routes beyond role-scoped navigation and bearer session identity.
  • Run control transport beyond hello and heartbeat, including heartbeat reconciliation policies.
  • External metrics collectors, browser tail transport, external log body backends, and AI log analysis windows.
  • External artifact storage backends, presigned URLs, and production throttling policies. Run self-update range reads and local artifact upload are implemented, but production mirrors/signing are not.
  • Plugin page iframe packaging and remote hosting policies beyond SDK-mediated bridge contracts.
  • Live AI provider connectivity tests and remote model discovery.
  • Production Run distribution signing/KMS, fleet rollout rings, and real AI-provider integration.
  • Server delete/archive routes beyond the currently implemented lifecycle, metadata update, and archive actions.

Core Service Boundary

  • platform/service.Core owns create/list/get workflows and cross-resource invariants.
  • platform/repo.Store owns repository access and currently has a durable file-backed implementation for local startup plus an in-memory implementation for tests.
  • platform/validator owns local resource validation and dependency compatibility checks.
  • Handlers must never expose run credentials, host paths, or raw AI provider keys.