Remove pre-1.0 audit and protected request scaffolding

This commit is contained in:
npc0-hue
2026-08-20 23:42:02 +08:00
parent 40b35b05c7
commit a7e2e4c6c0
130 changed files with 526 additions and 3767 deletions
+1 -1
View File
@@ -312,7 +312,7 @@ describe("PlatformApiClient AI providers", () => {
return jsonResponse({ accepted: true, action: "stop", instance: server, job: { ...job, capability: "process.stop" } });
}
if (url.endsWith("/api/v1/server-instances/server-1/process/status") && init?.method === "POST") {
return jsonResponse({ accepted: true, action: "status", instance: server, job: { ...job, capability: "process.status", executionResult: { kind: "process", processState: "running", auditSummary: "private supervised process identity" } } });
return jsonResponse({ accepted: true, action: "status", instance: server, job: { ...job, capability: "process.status", executionResult: { kind: "process", processState: "running", summary: "private supervised process identity" } } });
}
if (url.endsWith("/api/v1/server-instances/server-1/administrators/candidates") && (!init?.method || init.method === "GET")) {
return jsonResponse({ items: [{ id: "user-2", displayName: "Helper", status: "active", roles: ["server-admin"] }], count: 1 });
+6 -5
View File
@@ -19,7 +19,6 @@ import type {
ArtifactFilterRequest,
ArtifactListResponse,
AuthSessionResponse,
AuditEventListResponse,
ClientManagerBuildRequest,
ClientManagerControlRequest,
ClientManagerDeployRequest,
@@ -95,6 +94,8 @@ import type {
ServerMemberListResponse,
ServerMemberRequest,
ServerMetricsListResponse,
SourceRCONCommandRequest,
SourceRCONCommandResponse,
MetricSampleListResponse,
BackupListResponse,
BackupResponse,
@@ -405,6 +406,10 @@ export class PlatformApiClient {
return parseSafeGameClientBridgeCommand(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands`, { method: "POST", body: request }));
}
async dispatchSourceRCONCommand(id: string, request: SourceRCONCommandRequest): Promise<SourceRCONCommandResponse> {
return this.request<SourceRCONCommandResponse>(`/server-instances/${encodeURIComponent(id)}/rcon/commands`, { method: "POST", body: request });
}
async getGameClientBridgeCommand(id: string, commandId: string): Promise<GameClientBridgeCommandResponse> {
return parseSafeGameClientBridgeCommand(await this.request<unknown>(`/server-instances/${encodeURIComponent(id)}/game-client-bridge/commands/${encodeURIComponent(commandId)}`));
}
@@ -627,10 +632,6 @@ export class PlatformApiClient {
return this.request<LogStreamCursorResponse>("/log-streams/query", { method: "POST", body: request });
}
async listAuditEvents(): Promise<AuditEventListResponse> {
return this.request<AuditEventListResponse>("/audit-events");
}
async suggestServerConfig(request: LlmConfigSuggestionRequest): Promise<LlmConfigSuggestionResponse> {
return this.request<LlmConfigSuggestionResponse>("/ai/config-suggestions", { method: "POST", body: request });
}
+3 -3
View File
@@ -34,7 +34,7 @@ Normal browser login uses the platform's HttpOnly SameSite cookie and `credentia
- `invokeAI` posts `AIInvocationRequest` to `/ai/invocations` for platform-mediated AI assistance. Responses carry redacted recommendations, usage metadata, optional reviewable config suggestions, and safe errors; they must not include provider base URLs, key refs, raw keys, or direct provider transport details.
- `listRunEndpoints` and `listJobs` provide refresh data for endpoint availability, capacity, and durable lifecycle status. Job projections include `retrying`, attempt/max-attempt counts, next retry timing, safe ack/lease deadlines, cancellation timestamps/reason, terminal time, and reconciliation outcome/count.
- `getDependencyCatalog` reads `GET /server-instances/{id}/dependencies` and returns only target-matched probe state/evidence, typed plan step summaries, approved download hosts, and immutable SHA-256 `planDigest` values. Install requests must submit the selected digest; the browser never receives bindings, commands, paths, credentials, tokens, or private download refs.
- `listRunUpdates` reads `GET /server-instances/{id}/run/update` and returns only target, artifact checksum, release identity, phase, bounded audit message, rollback flag, and timestamps. The UI treats `restart-requested`/`activating` as non-terminal until a later safe projection confirms health.
- `listRunUpdates` reads `GET /server-instances/{id}/run/update` and returns only target, artifact checksum, release identity, phase, bounded status message, rollback flag, and timestamps. The UI treats `restart-requested`/`activating` as non-terminal until a later safe projection confirms health.
- `listMetricHistory`, `listBackups`, and `getBackup` read bounded owner-scoped metric and backup projections. Backup responses contain artifact IDs/checksums and recovery/retention state only; they never include body bytes or storage paths.
- `listRemoteAdapters` and `requestRemoteAdapter` use declaration-backed logical target keys and return queued status/result references. The browser never receives adapter credentials, host addresses, sockets, Run tokens, leases, session hashes, or secret refs.
- Server management DTOs may include bounded `ownerUserId` and `adminUserIds` metadata, but must not include raw run credentials, host paths, direct socket details, user password hashes, or AI provider keys.
@@ -43,7 +43,7 @@ Normal browser login uses the platform's HttpOnly SameSite cookie and `credentia
## Redesign Contract Gaps (redesign-platform-web-interactions)
Existing platform APIs already cover server lifecycle, jobs, log stream metadata and cursor query, audit events, users, run endpoints, game plugins, plugin bridge authorization, and AI provider health/test. The redesigned UI additionally declares the following frontend contracts; where the platform backend does not yet serve them, the UI must degrade to a clearly labeled local/unavailable state instead of failing silently:
Existing platform APIs already cover server lifecycle, jobs, log stream metadata and cursor query, users, run endpoints, game plugins, plugin bridge authorization, and AI provider health/test. The redesigned UI additionally declares the following frontend contracts; where the platform backend does not yet serve them, the UI must degrade to a clearly labeled local/unavailable state instead of failing silently:
- `POST /api/v1/auth/register` (`RegisterRequest`/`AuthSessionResponse`): visitor registration. Implemented: the first registered user becomes an active platform administrator; later self-registered users become pending server-scoped users and do not receive platform administrator privileges.
- `POST /api/v1/auth/login` (`LoginRequest`/`AuthSessionResponse`), `POST /api/v1/auth/rotate`, and `POST /api/v1/auth/logout`: implemented bounded, durable bearer session lifecycle for authenticated workspace entry.
@@ -56,7 +56,7 @@ Existing platform APIs already cover server lifecycle, jobs, log stream metadata
- `POST /api/v1/file-operations/dispatch` (`FileOperationDispatchRequest`/`FileOperationDispatchResponse`): implemented scoped file operation dispatch using logical keys and refs only.
- `POST /api/v1/ai/config-suggestions` (`LlmConfigSuggestionRequest`/`LlmConfigSuggestionResponse`) and `POST /api/v1/ai/invocations` (`AIInvocationRequest`/`AIInvocationResponse`): platform-mediated AI recommendation or diff scoped to one server. Provider keys stay in `platform/`; responses carry only recommendation text, usage metadata, and reviewable suggestions, never keys or provider secrets.
- Per-server plugin controls are rendered from installed plugin manifests (`bridgeActions`, `lifecycleActions`, `pages`, `declaredPermissions`); a richer declared-control schema remains a future plugin contract. Hosted bridge execution uses `POST /api/v1/plugin-bridge/execute` for server context, scoped file, log, job, artifact reference, and AI action envelopes instead of direct plugin fetches to platform internals.
- Operation/job traceability reuses `GET /api/v1/jobs`, `GET /api/v1/jobs/{id}`, `POST /api/v1/jobs/{id}/cancel`, and `GET /api/v1/audit-events`; the frontend wraps these in one visible operation lifecycle per user intent.
- Operation/job traceability reuses `GET /api/v1/jobs`, `GET /api/v1/jobs/{id}`, and `POST /api/v1/jobs/{id}/cancel`; the frontend wraps these in one visible operation lifecycle per user intent.
Browser Job contracts explicitly exclude raw or hashed lease tokens, Run session tokens/generations, secret refs, host paths, sockets, and credentials. The safe schema rejects those keys, and existing API client 401/403 behavior remains authoritative for expired sessions and cross-owner access.
- Server-scoped terminal log streaming (`GET /api/v1/server-instances/{id}/logs/events`) is used by the server detail terminal drawer for platform-accepted SSE history/live output. Raw log list/backfill routes (`GET .../logs/live`, `POST .../logs/backfill`) and direct management-terminal/RCON input routes remain removed from product clients; internal log ingest and cursor query remain available to platform services and maintenance/debug flows.
+13 -16
View File
@@ -16,7 +16,7 @@ const status = {
profileKey: "scum-client",
available: false,
reason: "component heartbeat is unavailable",
commandTypes: ["scum.announcement.send"],
commandTypes: ["scum.diagnostic.ping"],
snapshotTypes: ["scum.players"],
queryTemplateKeys: ["scum.player.search"]
}]
@@ -27,12 +27,11 @@ const pendingCommand = {
serverInstanceId: "server-1",
pluginId: "game.scum",
profileKey: "scum-client",
commandType: "scum.announcement.send",
commandType: "scum.diagnostic.ping",
priority: 20,
state: "pending",
approvalState: "pending",
requesterId: "user-1",
auditReferences: ["audit-command-1"],
expiresAt: later,
createdAt: now,
updatedAt: now
@@ -42,10 +41,10 @@ const completedCommand = {
...pendingCommand,
state: "succeeded",
approvalState: "approved",
resultSummary: "announcement delivered",
resultSummary: "diagnostic completed",
result: {
status: "succeeded",
summary: "announcement delivered",
summary: "diagnostic completed",
payload: { delivered: true, recipientCount: 12 },
completedAt: later
},
@@ -57,7 +56,6 @@ const cancellation = {
commandId: pendingCommand.id,
state: "cancelled",
cancellation: { requestedBy: "user-1", reason: "maintenance window changed", cancelledAt: later },
auditReferences: ["audit-command-1", "audit-command-cancel-1"],
updatedAt: later
} as const;
@@ -73,19 +71,18 @@ const snapshot = {
observedAt: now,
payload: { players: [{ playerId: "player-1", displayName: "Moonlight" }] },
retention: { keepForSeconds: 3600, maxRecords: 24 },
auditReferences: ["audit-snapshot-1"],
createdAt: now,
expiresAt: later
} as const;
const manifestDeclaration: GameClientBridgeManifestResponse = {
commands: [{
type: "scum.announcement.send",
title: "Send announcement",
type: "scum.diagnostic.ping",
title: "Diagnostic ping",
permission: "server.game-client.command",
approvalLevel: "operator",
payloadSchemaRef: "schemas/bridge/commands/announcement.request.json",
resultSchemaRef: "schemas/bridge/commands/announcement.result.json",
payloadSchemaRef: "schemas/bridge/commands/diagnostic-ping.request.json",
resultSchemaRef: "schemas/bridge/commands/diagnostic-ping.result.json",
timeoutSeconds: 30,
maxPayloadBytes: 4096
}],
@@ -104,7 +101,7 @@ const manifestDeclaration: GameClientBridgeManifestResponse = {
}],
commandRetentionSeconds: 86400,
maxCommands: 1000,
pages: [{ pageKey: "operations", commandTypes: ["scum.announcement.send"], snapshotTypes: ["scum.players"], queryTemplateKeys: ["scum.player.search"] }],
pages: [{ pageKey: "operations", commandTypes: ["scum.diagnostic.ping"], snapshotTypes: ["scum.players"], queryTemplateKeys: ["scum.player.search"] }],
companion: {
profileKey: "scum-client-manager",
configTemplateKey: "client-config",
@@ -151,15 +148,15 @@ describe("PlatformApiClient Game Client Bridge operator API", () => {
const client = new PlatformApiClient("/api/v1", () => "operator-session");
const queueRequest: GameClientBridgeQueueRequest = {
profileKey: "scum-client",
commandType: "scum.announcement.send",
commandType: "scum.diagnostic.ping",
payload: { message: "Restart in ten minutes", channels: ["global"] },
idempotencyKey: "announcement-1",
idempotencyKey: "diagnostic-1",
priority: 20,
expiresAt: later
};
await expect(client.getGameClientBridgeStatus("server-1")).resolves.toMatchObject({ available: false, profiles: [{ profileKey: "scum-client" }] });
await expect(client.listGameClientBridgeCommands("server-1", { profileKey: "scum-client", state: "pending", commandType: "scum.announcement.send" })).resolves.toMatchObject({ count: 1 });
await expect(client.listGameClientBridgeCommands("server-1", { profileKey: "scum-client", state: "pending", commandType: "scum.diagnostic.ping" })).resolves.toMatchObject({ count: 1 });
await expect(client.queueGameClientBridgeCommand("server-1", queueRequest)).resolves.toMatchObject({ state: "pending", approvalState: "pending" });
await expect(client.getGameClientBridgeCommand("server-1", pendingCommand.id)).resolves.toMatchObject({ result: { status: "succeeded", payload: { delivered: true } } });
await expect(client.cancelGameClientBridgeCommand("server-1", pendingCommand.id, { reason: "maintenance window changed" })).resolves.toMatchObject({ state: "cancelled" });
@@ -167,7 +164,7 @@ describe("PlatformApiClient Game Client Bridge operator API", () => {
expect(calls.map((call) => `${call.method} ${call.url}`)).toEqual([
"GET /api/v1/server-instances/server-1/game-client-bridge",
"GET /api/v1/server-instances/server-1/game-client-bridge/commands?profileKey=scum-client&state=pending&commandType=scum.announcement.send",
"GET /api/v1/server-instances/server-1/game-client-bridge/commands?profileKey=scum-client&state=pending&commandType=scum.diagnostic.ping",
"POST /api/v1/server-instances/server-1/game-client-bridge/commands",
"GET /api/v1/server-instances/server-1/game-client-bridge/commands/command-1",
"POST /api/v1/server-instances/server-1/game-client-bridge/commands/command-1/cancel",
@@ -5,7 +5,7 @@ import { PlatformApiClient } from "./client";
describe("PlatformApiClient production operations", () => {
afterEach(() => vi.unstubAllGlobals());
it("uses Platform-only governance routes and bounded request bodies", async () => {
it("uses Platform-only operations routes and bounded request bodies", async () => {
const calls: Array<{ url: string; method: string; body?: unknown }> = [];
vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
calls.push({ url: String(input), method: init?.method ?? "GET", body: init?.body ? JSON.parse(String(init.body)) : undefined });
+1 -23
View File
@@ -146,7 +146,6 @@ export interface GameClientBridgeCommandResponse {
resultSummary?: string;
result?: GameClientBridgeCommandResultResponse;
cancellation?: GameClientBridgeCommandCancellationResponse;
auditReferences?: string[];
expiresAt: string;
createdAt: string;
updatedAt: string;
@@ -181,7 +180,6 @@ export interface GameClientBridgeCancelResponse {
commandId: string;
state: GameClientBridgeCommandState;
cancellation: GameClientBridgeCommandCancellationResponse;
auditReferences?: string[];
updatedAt: string;
}
@@ -202,7 +200,6 @@ export interface GameClientBridgeSnapshotResponse {
observedAt: string;
payload: GameClientBridgeJsonObject;
retention: GameClientBridgeRetentionResponse;
auditReferences?: string[];
createdAt: string;
expiresAt: string;
}
@@ -695,7 +692,7 @@ export interface JobExecutionResultResponse {
version?: number;
checksum?: string;
sizeBytes?: number;
auditSummary?: string;
summary?: string;
}
export interface JobListResponse {
@@ -1537,22 +1534,6 @@ export interface LogStreamEventOptions {
historyLimit?: number;
}
export interface AuditEventResponse {
id: string;
actorId: string;
action: string;
resourceKind: string;
resourceId: string;
result: string;
summary: string;
createdAt: string;
}
export interface AuditEventListResponse {
items: AuditEventResponse[];
count: number;
}
export interface JobCreateRequest {
id: string;
serverInstanceId?: string;
@@ -1628,7 +1609,6 @@ export interface CapacityAdmissionDecisionResponse {
pressureCodes?: string[];
checkedAt: string;
alertId?: string;
auditEventId?: string;
}
export interface EndpointCapacityProjectionResponse {
@@ -1672,7 +1652,6 @@ export interface AlertResponse {
retryable: boolean;
retryAfterSeconds?: number;
lastJobId?: string;
lastAuditEventId?: string;
lastSeenAt: string;
acknowledgedBy?: string;
acknowledgedAt?: string;
@@ -1700,7 +1679,6 @@ export interface PluginLifecycleInstallationResponse {
dependencyState?: string;
jobId?: string;
alertId?: string;
auditEventId?: string;
failureReason?: string;
createdAt: string;
updatedAt: string;