Record project continuation changes

This commit is contained in:
2026-05-12 21:02:29 +03:00
parent 3059d1d7a3
commit 8f69d53193
339 changed files with 101111 additions and 1769 deletions
+5589 -285
View File
File diff suppressed because it is too large Load Diff
+814 -3
View File
@@ -1,5 +1,6 @@
import type {
AuditEvent,
AuditSummary,
AuthResult,
BootstrapOwnerResult,
Cluster,
@@ -12,19 +13,49 @@ import type {
FabricEntryPointNode,
FabricEgressPool,
FabricEgressPoolNode,
FabricServiceChannelAdaptivePolicy,
FabricServiceChannelBreadcrumbWindowPolicy,
FabricServiceChannelPoolPolicy,
FabricServiceChannelRecoveryPolicy,
FabricServiceChannelRouteFeedbackObservation,
FabricServiceChannelRouteRebuildAlertSilence,
FabricServiceChannelRouteRebuildAttempt,
FabricServiceChannelRouteRebuildHealthSummary,
FabricServiceChannelRouteRebuildIncident,
FabricServiceChannelRebuildInvestigationBreadcrumbs,
ExpireFabricServiceChannelRouteFeedbackResult,
FabricServiceChannelReadiness,
FabricServiceChannelRebuildSnapshotMaintenanceHealth,
FabricServiceChannelRebuildSnapshotWarmup,
FabricServiceChannelLeaseMaintenance,
FabricServiceChannelAccessTelemetry,
FabricServiceChannelSchemaStatus,
FabricTestingFlag,
InstallationStatus,
JoinRequest,
MeshLink,
MeshRouteIntent,
NodeJoinToken,
NodeHeartbeat,
NodeSyntheticMeshConfig,
NodeTelemetryObservation,
NodeUpdatePlan,
NodeUpdatePolicy,
NodeUpdateStatus,
NodeWorkloadDesiredState,
OrganizationAdminSummary,
Organization,
OrganizationMembership,
QoSPolicy,
ReleaseVersion,
Resource,
RoleAssignment,
UserAccount,
VPNClientDiagnosticCommand,
VPNClientDiagnosticStatus,
VPNConnection,
VPNConnectionLease,
VPNPacketStats,
WorkloadStatus,
} from "../types";
@@ -69,6 +100,83 @@ export type CreateVPNConnectionPayload = {
placementPolicy: Record<string, unknown>;
};
export type UpsertNodeUpdatePolicyPayload = {
product: string;
channel?: string;
targetVersion?: string | null;
strategy?: string;
enabled?: boolean;
rollbackAllowed?: boolean;
healthWindowSeconds?: number;
};
export type UpdateFabricServiceChannelRecoveryPolicyPayload = {
hysteresisPenalty?: number;
promotionMinSamples?: number;
demotionFailureThreshold?: number;
demotionDropThreshold?: number;
demotionSlowThreshold?: number;
demotionRebuildEnabled?: boolean;
demotionFencedEnabled?: boolean;
};
export type UpdateFabricServiceChannelAdaptivePolicyPayload = {
maxParallelWindow?: number;
bulkPressureChannelThreshold?: number;
queuePressureHighWatermark?: number;
queuePressureMaxInFlight?: number;
classWindows?: Record<string, number>;
};
export type UpdateFabricServiceChannelPoolPolicyPayload = {
entryPoolNodeIds?: string[];
exitPoolNodeIds?: string[];
preferredEntryNodeId?: string;
preferredExitNodeId?: string;
selectionStrategy?: string;
routeRebuild?: string;
entryFailover?: string;
exitFailover?: string;
backendFallbackAllowed?: boolean;
stickySession?: boolean;
};
export type UpdateFabricServiceChannelBreadcrumbWindowPolicyPayload = {
currentWindowSeconds?: number;
historyWindowSeconds?: number;
};
export type CreateOrganizationPayload = {
slug: string;
name: string;
metadata?: Record<string, unknown>;
};
export type CreateUserPayload = {
email: string;
password: string;
platformRole?: string;
};
export type CreateResourcePayload = {
organizationId: string;
name: string;
address: string;
protocol: string;
secretRef?: string | null;
certificateVerificationMode?: string;
renderQualityProfile?: string;
clipboardMode?: string;
fileTransferMode?: string;
metadata?: Record<string, unknown>;
};
export type UpsertResourceSecretPayload = {
username?: string;
password?: string;
domain?: string;
};
export type CreateFabricEntryPointPayload = {
name: string;
endpointType: string;
@@ -110,6 +218,12 @@ export class AdminApiClient {
});
}
async refresh(input: { refreshToken: string }): Promise<AuthResult> {
return this.post<AuthResult>("/auth/refresh", {
refresh_token: input.refreshToken,
});
}
async getInstallationStatus(): Promise<InstallationStatus> {
const payload = await this.request<{ installation: InstallationStatus }>("/installation/status", {
method: "GET",
@@ -244,6 +358,13 @@ export class AdminApiClient {
});
}
async deleteClusterNode(clusterId: string, nodeId: string, reason: string): Promise<void> {
await this.delete(`/clusters/${clusterId}/nodes/${nodeId}`, {
actor_user_id: this.actorUserId,
reason,
});
}
async listJoinRequests(clusterId: string): Promise<JoinRequest[]> {
const payload = await this.get<{ join_requests: JoinRequest[] }>(`/clusters/${clusterId}/join-requests`);
return payload.join_requests ?? [];
@@ -260,6 +381,18 @@ export class AdminApiClient {
return payload.join_token;
}
async listJoinTokens(clusterId: string): Promise<NodeJoinToken[]> {
const payload = await this.get<{ join_tokens: NodeJoinToken[] }>(`/clusters/${clusterId}/join-tokens`);
return payload.join_tokens ?? [];
}
async revokeJoinToken(clusterId: string, tokenId: string): Promise<NodeJoinToken> {
const payload = await this.post<{ join_token: NodeJoinToken }>(`/clusters/${clusterId}/join-tokens/${tokenId}/revoke`, {
actor_user_id: this.actorUserId,
});
return payload.join_token;
}
async approveJoinRequest(clusterId: string, requestId: string): Promise<void> {
await this.post(`/clusters/${clusterId}/join-requests/${requestId}/approve`, {
actor_user_id: this.actorUserId,
@@ -323,6 +456,49 @@ export class AdminApiClient {
return payload.telemetry ?? [];
}
async listReleaseVersions(clusterId: string, product = "rap-node-agent", channel = "dev"): Promise<ReleaseVersion[]> {
const params = new URLSearchParams({ product, channel });
const payload = await this.get<{ release_versions: ReleaseVersion[] }>(`/clusters/${clusterId}/updates/releases?${params.toString()}`);
return payload.release_versions ?? [];
}
async getNodeUpdatePlan(
clusterId: string,
nodeId: string,
input: { product?: string; currentVersion?: string | null; os?: string; arch?: string; installType?: string; channel?: string },
): Promise<NodeUpdatePlan> {
const params = new URLSearchParams({
product: input.product || "rap-node-agent",
current_version: input.currentVersion || "",
os: input.os || "linux",
arch: input.arch || "amd64",
install_type: input.installType || "docker",
channel: input.channel || "dev",
});
const payload = await this.get<{ node_update_plan: NodeUpdatePlan }>(`/clusters/${clusterId}/nodes/${nodeId}/updates/plan?${params.toString()}`);
return payload.node_update_plan;
}
async upsertNodeUpdatePolicy(clusterId: string, nodeId: string, input: UpsertNodeUpdatePolicyPayload): Promise<NodeUpdatePolicy> {
const payload = await this.put<{ node_update_policy: NodeUpdatePolicy }>(`/clusters/${clusterId}/nodes/${nodeId}/updates/policy`, {
actor_user_id: this.actorUserId,
product: input.product,
channel: input.channel || "dev",
target_version: input.targetVersion ?? null,
strategy: input.strategy || "rolling",
enabled: input.enabled ?? true,
rollback_allowed: input.rollbackAllowed ?? true,
health_window_seconds: input.healthWindowSeconds || 180,
});
return payload.node_update_policy;
}
async listNodeUpdateStatuses(clusterId: string, nodeId: string, limit = 80): Promise<NodeUpdateStatus[]> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId, limit: String(limit) });
const payload = await this.get<{ node_update_statuses: NodeUpdateStatus[] }>(`/clusters/${clusterId}/nodes/${nodeId}/updates/statuses?${params.toString()}`);
return payload.node_update_statuses ?? [];
}
async listFabricTestingFlags(): Promise<FabricTestingFlag[]> {
const payload = await this.get<{ testing_flags: FabricTestingFlag[] }>("/fabric/testing-flags");
return payload.testing_flags ?? [];
@@ -374,6 +550,28 @@ export class AdminApiClient {
return payload.mesh_links ?? [];
}
async listRouteIntents(clusterId: string): Promise<MeshRouteIntent[]> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ route_intents: MeshRouteIntent[] }>(`/clusters/${clusterId}/mesh/route-intents?${params.toString()}`);
return payload.route_intents ?? [];
}
async expireRouteIntent(clusterId: string, routeIntentId: string, reason: string): Promise<MeshRouteIntent> {
const payload = await this.post<{ route_intent: MeshRouteIntent }>(`/clusters/${clusterId}/mesh/route-intents/${routeIntentId}/expire`, {
actor_user_id: this.actorUserId,
reason,
});
return payload.route_intent;
}
async disableRouteIntent(clusterId: string, routeIntentId: string, reason: string): Promise<MeshRouteIntent> {
const payload = await this.post<{ route_intent: MeshRouteIntent }>(`/clusters/${clusterId}/mesh/route-intents/${routeIntentId}/disable`, {
actor_user_id: this.actorUserId,
reason,
});
return payload.route_intent;
}
async getNodeSyntheticMeshConfig(clusterId: string, nodeId: string): Promise<NodeSyntheticMeshConfig> {
const payload = await this.get<{ synthetic_mesh_config: NodeSyntheticMeshConfig }>(
`/clusters/${clusterId}/nodes/${nodeId}/mesh/synthetic-config`,
@@ -381,6 +579,443 @@ export class AdminApiClient {
return payload.synthetic_mesh_config;
}
async listFabricServiceChannelRouteFeedback(
clusterId: string,
input: { reporterNodeId?: string; routeId?: string; serviceClass?: string; feedbackStatus?: string; includeExpired?: boolean } = {},
): Promise<FabricServiceChannelRouteFeedbackObservation[]> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.reporterNodeId) {
params.set("reporter_node_id", input.reporterNodeId);
}
if (input.routeId) {
params.set("route_id", input.routeId);
}
if (input.serviceClass) {
params.set("service_class", input.serviceClass);
}
if (input.feedbackStatus) {
params.set("feedback_status", input.feedbackStatus);
}
if (input.includeExpired) {
params.set("include_expired", "true");
}
const payload = await this.get<{ route_feedback: FabricServiceChannelRouteFeedbackObservation[] }>(
`/clusters/${clusterId}/fabric/service-channels/route-feedback?${params.toString()}`,
);
return payload.route_feedback ?? [];
}
async expireFabricServiceChannelRouteFeedback(
clusterId: string,
input: { routeId: string; reporterNodeId?: string; serviceClass?: string; reason?: string },
): Promise<ExpireFabricServiceChannelRouteFeedbackResult> {
const payload = await this.post<{ route_feedback_expire: ExpireFabricServiceChannelRouteFeedbackResult }>(
`/clusters/${clusterId}/fabric/service-channels/route-feedback/expire`,
{
actor_user_id: this.actorUserId,
route_id: input.routeId,
reporter_node_id: input.reporterNodeId || "",
service_class: input.serviceClass || "",
reason: input.reason || "expired from admin fabric diagnostics",
},
);
return payload.route_feedback_expire;
}
async listFabricServiceChannelRouteRebuildAttempts(
clusterId: string,
input: {
reporterNodeId?: string;
routeId?: string;
replacementRouteId?: string;
serviceClass?: string;
rebuildStatus?: string;
rebuildRequestId?: string;
generation?: string;
feedbackSource?: string;
feedbackChannelId?: string;
feedbackViolationStatus?: string;
enrichment?: "summary" | "deep";
limit?: number;
offset?: number;
} = {},
): Promise<FabricServiceChannelRouteRebuildAttempt[]> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.reporterNodeId) {
params.set("reporter_node_id", input.reporterNodeId);
}
if (input.routeId) {
params.set("route_id", input.routeId);
}
if (input.replacementRouteId) {
params.set("replacement_route_id", input.replacementRouteId);
}
if (input.serviceClass) {
params.set("service_class", input.serviceClass);
}
if (input.rebuildStatus) {
params.set("rebuild_status", input.rebuildStatus);
}
if (input.rebuildRequestId) {
params.set("rebuild_request_id", input.rebuildRequestId);
}
if (input.generation) {
params.set("generation", input.generation);
}
if (input.feedbackSource) {
params.set("feedback_source", input.feedbackSource);
}
if (input.feedbackChannelId) {
params.set("feedback_channel_id", input.feedbackChannelId);
}
if (input.feedbackViolationStatus) {
params.set("feedback_violation_status", input.feedbackViolationStatus);
}
if (input.enrichment) {
params.set("enrichment", input.enrichment);
}
if (input.limit) {
params.set("limit", String(input.limit));
}
if (input.offset) {
params.set("offset", String(input.offset));
}
const payload = await this.get<{ rebuild_attempts: FabricServiceChannelRouteRebuildAttempt[] }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-attempts?${params.toString()}`,
);
return payload.rebuild_attempts ?? [];
}
async getFabricServiceChannelRouteRebuildHealthSummary(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelRouteRebuildHealthSummary> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
const payload = await this.get<{ rebuild_health: FabricServiceChannelRouteRebuildHealthSummary }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-health?${params.toString()}`,
);
return payload.rebuild_health;
}
async getFabricServiceChannelReadiness(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelReadiness> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
const payload = await this.get<{ fabric_service_channel_readiness: FabricServiceChannelReadiness }>(
`/clusters/${clusterId}/fabric/service-channels/readiness?${params.toString()}`,
);
return payload.fabric_service_channel_readiness;
}
async getFabricServiceChannelSchemaStatus(clusterId: string): Promise<FabricServiceChannelSchemaStatus> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ fabric_service_channel_schema_status: FabricServiceChannelSchemaStatus }>(
`/clusters/${clusterId}/fabric/service-channels/schema-status?${params.toString()}`,
);
return payload.fabric_service_channel_schema_status;
}
async getFabricServiceChannelRebuildSnapshotMaintenanceHealth(
clusterId: string,
input: { limit?: number; minAgeSeconds?: number; heartbeatThreshold?: number } = {},
): Promise<FabricServiceChannelRebuildSnapshotMaintenanceHealth> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
if (input.minAgeSeconds) {
params.set("min_age_seconds", String(input.minAgeSeconds));
}
if (input.heartbeatThreshold) {
params.set("heartbeat_threshold", String(input.heartbeatThreshold));
}
const payload = await this.get<{ rebuild_snapshot_health: FabricServiceChannelRebuildSnapshotMaintenanceHealth }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-snapshots/health?${params.toString()}`,
);
return payload.rebuild_snapshot_health;
}
async warmupFabricServiceChannelRebuildSnapshots(
clusterId: string,
input: { limit?: number; staleAfterSeconds?: number } = {},
): Promise<FabricServiceChannelRebuildSnapshotWarmup> {
const payload = await this.post<{ rebuild_snapshot_warmup: FabricServiceChannelRebuildSnapshotWarmup }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-snapshots/warmup`,
{
actor_user_id: this.actorUserId,
limit: input.limit || 10,
stale_after_seconds: input.staleAfterSeconds || 60,
},
);
return payload.rebuild_snapshot_warmup;
}
async getFabricServiceChannelLeaseMaintenance(
clusterId: string,
input: { limit?: number; includeExpired?: boolean } = {},
): Promise<FabricServiceChannelLeaseMaintenance> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
if (input.includeExpired) {
params.set("include_expired", "true");
}
const payload = await this.get<{ fabric_service_channel_lease_maintenance: FabricServiceChannelLeaseMaintenance }>(
`/clusters/${clusterId}/fabric/service-channels/leases?${params.toString()}`,
);
return payload.fabric_service_channel_lease_maintenance;
}
async cleanupFabricServiceChannelLeases(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelLeaseMaintenance> {
const payload = await this.post<{ fabric_service_channel_lease_maintenance: FabricServiceChannelLeaseMaintenance }>(
`/clusters/${clusterId}/fabric/service-channels/leases/cleanup`,
{
actor_user_id: this.actorUserId,
limit: input.limit || 100,
},
);
return payload.fabric_service_channel_lease_maintenance;
}
async getFabricServiceChannelAccessTelemetry(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelAccessTelemetry> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
const payload = await this.get<{ fabric_service_channel_access_telemetry: FabricServiceChannelAccessTelemetry }>(
`/clusters/${clusterId}/fabric/service-channels/access-telemetry?${params.toString()}`,
);
return payload.fabric_service_channel_access_telemetry;
}
async listFabricServiceChannelRouteRebuildIncidents(clusterId: string, input: { limit?: number } = {}): Promise<FabricServiceChannelRouteRebuildIncident[]> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
const payload = await this.get<{ rebuild_incidents: FabricServiceChannelRouteRebuildIncident[] }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-incidents?${params.toString()}`,
);
return payload.rebuild_incidents ?? [];
}
async getFabricServiceChannelRebuildInvestigationBreadcrumbs(
clusterId: string,
input: { limit?: number; currentWindowSeconds?: number; historyWindowSeconds?: number } = {},
): Promise<FabricServiceChannelRebuildInvestigationBreadcrumbs> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
if (input.limit) {
params.set("limit", String(input.limit));
}
if (input.currentWindowSeconds) {
params.set("current_window_seconds", String(input.currentWindowSeconds));
}
if (input.historyWindowSeconds) {
params.set("history_window_seconds", String(input.historyWindowSeconds));
}
const payload = await this.get<{ rebuild_investigation_breadcrumbs: FabricServiceChannelRebuildInvestigationBreadcrumbs }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-investigations/breadcrumbs?${params.toString()}`,
);
return payload.rebuild_investigation_breadcrumbs;
}
async recordFabricServiceChannelRouteRebuildInvestigation(
clusterId: string,
input: {
reporterNodeId?: string;
routeId?: string;
serviceClass?: string;
generation?: string;
guardStatus?: string;
incidentId?: string;
feedbackSource?: string;
feedbackChannelId?: string;
feedbackViolationStatus?: string;
drilldownSource?: string;
reason?: string;
},
): Promise<void> {
await this.post(`/clusters/${clusterId}/fabric/service-channels/rebuild-incidents/investigations`, {
actor_user_id: this.actorUserId,
reporter_node_id: input.reporterNodeId,
route_id: input.routeId,
service_class: input.serviceClass || "",
generation: input.generation || "",
guard_status: input.guardStatus || "",
incident_id: input.incidentId || "",
feedback_source: input.feedbackSource || "",
feedback_channel_id: input.feedbackChannelId || "",
feedback_violation_status: input.feedbackViolationStatus || "",
drilldown_source: input.drilldownSource || "",
reason: input.reason || "operator opened deep rebuild ledger",
});
}
async silenceFabricServiceChannelRouteRebuildAlert(
clusterId: string,
input: {
incidentSource?: string;
channelId?: string;
reporterNodeId: string;
routeId: string;
guardStatus: string;
generation?: string;
reason?: string;
ttlSeconds?: number;
},
): Promise<FabricServiceChannelRouteRebuildAlertSilence> {
const payload = await this.post<{ rebuild_alert_silence: FabricServiceChannelRouteRebuildAlertSilence }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-health/silences`,
{
actor_user_id: this.actorUserId,
incident_source: input.incidentSource || "",
channel_id: input.channelId || "",
reporter_node_id: input.reporterNodeId,
route_id: input.routeId,
guard_status: input.guardStatus,
generation: input.generation || "",
reason: input.reason || "operator acknowledged rebuild alert",
ttl_seconds: input.ttlSeconds || 21600,
},
);
return payload.rebuild_alert_silence;
}
async listFabricServiceChannelRouteRebuildAlertSilences(
clusterId: string,
): Promise<FabricServiceChannelRouteRebuildAlertSilence[]> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ rebuild_alert_silences: FabricServiceChannelRouteRebuildAlertSilence[] }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-health/silences?${params.toString()}`,
);
return payload.rebuild_alert_silences ?? [];
}
async unsilenceFabricServiceChannelRouteRebuildAlert(
clusterId: string,
silenceId: string,
reason?: string,
): Promise<FabricServiceChannelRouteRebuildAlertSilence> {
const payload = await this.delete<{ rebuild_alert_silence: FabricServiceChannelRouteRebuildAlertSilence }>(
`/clusters/${clusterId}/fabric/service-channels/rebuild-health/silences/${encodeURIComponent(silenceId)}`,
{
actor_user_id: this.actorUserId,
reason: reason || "operator removed rebuild alert silence",
},
);
return payload.rebuild_alert_silence;
}
async getFabricServiceChannelRecoveryPolicy(clusterId: string): Promise<FabricServiceChannelRecoveryPolicy> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ fabric_service_channel_recovery_policy: FabricServiceChannelRecoveryPolicy }>(
`/clusters/${clusterId}/fabric/service-channels/recovery-policy?${params.toString()}`,
);
return payload.fabric_service_channel_recovery_policy;
}
async updateFabricServiceChannelRecoveryPolicy(
clusterId: string,
input: UpdateFabricServiceChannelRecoveryPolicyPayload,
): Promise<FabricServiceChannelRecoveryPolicy> {
const payload = await this.put<{ fabric_service_channel_recovery_policy: FabricServiceChannelRecoveryPolicy }>(
`/clusters/${clusterId}/fabric/service-channels/recovery-policy`,
{
actor_user_id: this.actorUserId,
hysteresis_penalty: input.hysteresisPenalty,
promotion_min_samples: input.promotionMinSamples,
demotion_failure_threshold: input.demotionFailureThreshold,
demotion_drop_threshold: input.demotionDropThreshold,
demotion_slow_threshold: input.demotionSlowThreshold,
demotion_rebuild_enabled: input.demotionRebuildEnabled,
demotion_fenced_enabled: input.demotionFencedEnabled,
},
);
return payload.fabric_service_channel_recovery_policy;
}
async getFabricServiceChannelAdaptivePolicy(clusterId: string): Promise<FabricServiceChannelAdaptivePolicy> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ fabric_service_channel_adaptive_policy: FabricServiceChannelAdaptivePolicy }>(
`/clusters/${clusterId}/fabric/service-channels/adaptive-policy?${params.toString()}`,
);
return payload.fabric_service_channel_adaptive_policy;
}
async updateFabricServiceChannelAdaptivePolicy(
clusterId: string,
input: UpdateFabricServiceChannelAdaptivePolicyPayload,
): Promise<FabricServiceChannelAdaptivePolicy> {
const payload = await this.put<{ fabric_service_channel_adaptive_policy: FabricServiceChannelAdaptivePolicy }>(
`/clusters/${clusterId}/fabric/service-channels/adaptive-policy`,
{
actor_user_id: this.actorUserId,
max_parallel_window: input.maxParallelWindow,
bulk_pressure_channel_threshold: input.bulkPressureChannelThreshold,
queue_pressure_high_watermark: input.queuePressureHighWatermark,
queue_pressure_max_in_flight: input.queuePressureMaxInFlight,
class_windows: input.classWindows,
},
);
return payload.fabric_service_channel_adaptive_policy;
}
async getFabricServiceChannelPoolPolicy(clusterId: string): Promise<FabricServiceChannelPoolPolicy> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ fabric_service_channel_pool_policy: FabricServiceChannelPoolPolicy }>(
`/clusters/${clusterId}/fabric/service-channels/pool-policy?${params.toString()}`,
);
return payload.fabric_service_channel_pool_policy;
}
async updateFabricServiceChannelPoolPolicy(
clusterId: string,
input: UpdateFabricServiceChannelPoolPolicyPayload,
): Promise<FabricServiceChannelPoolPolicy> {
const payload = await this.put<{ fabric_service_channel_pool_policy: FabricServiceChannelPoolPolicy }>(
`/clusters/${clusterId}/fabric/service-channels/pool-policy`,
{
actor_user_id: this.actorUserId,
entry_pool_node_ids: input.entryPoolNodeIds,
exit_pool_node_ids: input.exitPoolNodeIds,
preferred_entry_node_id: input.preferredEntryNodeId,
preferred_exit_node_id: input.preferredExitNodeId,
selection_strategy: input.selectionStrategy,
route_rebuild: input.routeRebuild,
entry_failover: input.entryFailover,
exit_failover: input.exitFailover,
backend_fallback_allowed: input.backendFallbackAllowed,
sticky_session: input.stickySession,
},
);
return payload.fabric_service_channel_pool_policy;
}
async getFabricServiceChannelBreadcrumbWindowPolicy(clusterId: string): Promise<FabricServiceChannelBreadcrumbWindowPolicy> {
const params = new URLSearchParams({ actor_user_id: this.actorUserId });
const payload = await this.get<{ fabric_service_channel_breadcrumb_window_policy: FabricServiceChannelBreadcrumbWindowPolicy }>(
`/clusters/${clusterId}/fabric/service-channels/breadcrumb-window-policy?${params.toString()}`,
);
return payload.fabric_service_channel_breadcrumb_window_policy;
}
async updateFabricServiceChannelBreadcrumbWindowPolicy(
clusterId: string,
input: UpdateFabricServiceChannelBreadcrumbWindowPolicyPayload,
): Promise<FabricServiceChannelBreadcrumbWindowPolicy> {
const payload = await this.put<{ fabric_service_channel_breadcrumb_window_policy: FabricServiceChannelBreadcrumbWindowPolicy }>(
`/clusters/${clusterId}/fabric/service-channels/breadcrumb-window-policy`,
{
actor_user_id: this.actorUserId,
current_window_seconds: input.currentWindowSeconds,
history_window_seconds: input.historyWindowSeconds,
},
);
return payload.fabric_service_channel_breadcrumb_window_policy;
}
async listQoSPolicies(clusterId: string): Promise<QoSPolicy[]> {
const payload = await this.get<{ qos_policies: QoSPolicy[] }>(`/clusters/${clusterId}/mesh/qos-policies`);
return payload.qos_policies ?? [];
@@ -519,6 +1154,46 @@ export class AdminApiClient {
}
}
async getVPNPacketStats(clusterId: string, vpnConnectionId: string): Promise<VPNPacketStats> {
const payload = await this.get<{ vpn_packet_stats: VPNPacketStats }>(
`/clusters/${clusterId}/vpn-connections/${vpnConnectionId}/tunnel/stats`,
);
return payload.vpn_packet_stats ?? {};
}
async getVPNClientDiagnosticStatus(clusterId: string, deviceId: string): Promise<VPNClientDiagnosticStatus | null> {
if (!deviceId.trim()) {
return null;
}
try {
const payload = await this.get<{ vpn_client_diagnostic_status: VPNClientDiagnosticStatus }>(
`/clusters/${clusterId}/vpn/client-diagnostics/${encodeURIComponent(deviceId.trim())}/status`,
);
return payload.vpn_client_diagnostic_status ?? null;
} catch {
return null;
}
}
async listVPNClientDiagnosticStatuses(clusterId: string): Promise<VPNClientDiagnosticStatus[]> {
const payload = await this.get<{ vpn_client_diagnostic_statuses: VPNClientDiagnosticStatus[] }>(
`/clusters/${clusterId}/vpn/client-diagnostics`,
);
return payload.vpn_client_diagnostic_statuses ?? [];
}
async enqueueVPNClientDiagnosticCommand(
clusterId: string,
deviceId: string,
command: Record<string, unknown>,
): Promise<VPNClientDiagnosticCommand> {
const payload = await this.post<{ vpn_client_diagnostic_command: VPNClientDiagnosticCommand }>(
`/clusters/${clusterId}/vpn/client-diagnostics/${encodeURIComponent(deviceId.trim())}/commands`,
command,
);
return payload.vpn_client_diagnostic_command;
}
async expireStaleVPNLeases(clusterId: string): Promise<VPNConnectionLease[]> {
const payload = await this.post<{ expired_leases: VPNConnectionLease[] }>(
`/clusters/${clusterId}/vpn-connection-leases/expire-stale`,
@@ -529,9 +1204,47 @@ export class AdminApiClient {
return payload.expired_leases ?? [];
}
async listAudit(clusterId: string): Promise<AuditEvent[]> {
const payload = await this.get<{ audit_events: AuditEvent[] }>(`/clusters/${clusterId}/audit?limit=100`);
return payload.audit_events ?? [];
async listAudit(
clusterId: string,
input: {
eventTypes?: string[];
targetTypes?: string[];
correlation?: string;
limit?: number;
} = {},
): Promise<AuditEvent[]> {
return (await this.listAuditDetailed(clusterId, input)).events;
}
async listAuditDetailed(
clusterId: string,
input: {
eventTypes?: string[];
targetTypes?: string[];
correlation?: string;
limit?: number;
} = {},
): Promise<{ events: AuditEvent[]; summary?: AuditSummary }> {
const params = new URLSearchParams({ limit: String(input.limit || 100) });
for (const eventType of input.eventTypes || []) {
if (eventType) {
params.append("event_type", eventType);
}
}
for (const targetType of input.targetTypes || []) {
if (targetType) {
params.append("target_type", targetType);
}
}
if (input.correlation) {
params.set("correlation", input.correlation);
}
const payload = await this.get<{ audit_events: AuditEvent[]; audit_summary?: AuditSummary }>(`/clusters/${clusterId}/audit?${params.toString()}`);
return { events: payload.audit_events ?? [], summary: payload.audit_summary };
}
clusterEventsURL(clusterId: string): string {
return `${this.baseUrl}/clusters/${encodeURIComponent(clusterId)}/events?actor_user_id=${encodeURIComponent(this.actorUserId)}`;
}
async getOrganizationAdminSummary(organizationId: string): Promise<OrganizationAdminSummary> {
@@ -541,6 +1254,96 @@ export class AdminApiClient {
return payload.admin_summary;
}
async listOrganizations(): Promise<Organization[]> {
const payload = await this.request<{ organizations: Organization[] }>(
`/organizations?user_id=${encodeURIComponent(this.actorUserId)}`,
{ method: "GET" },
);
return payload.organizations ?? [];
}
async createOrganization(input: CreateOrganizationPayload): Promise<Organization> {
const payload = await this.post<{ organization: Organization }>("/organizations/", {
actor_user_id: this.actorUserId,
slug: input.slug,
name: input.name,
metadata: input.metadata || {},
});
return payload.organization;
}
async listUsers(): Promise<UserAccount[]> {
const payload = await this.get<{ users: UserAccount[] }>("/users/");
return payload.users ?? [];
}
async createUser(input: CreateUserPayload): Promise<UserAccount> {
const payload = await this.post<{ user: UserAccount }>("/users/", {
actor_user_id: this.actorUserId,
email: input.email,
password: input.password,
platform_role: input.platformRole || "user",
});
return payload.user;
}
async listOrganizationMemberships(organizationId: string): Promise<OrganizationMembership[]> {
const payload = await this.request<{ memberships: OrganizationMembership[] }>(
`/organizations/${organizationId}/memberships?user_id=${encodeURIComponent(this.actorUserId)}`,
{ method: "GET" },
);
return payload.memberships ?? [];
}
async addOrganizationMembership(organizationId: string, input: { userId: string; roleId: string }): Promise<OrganizationMembership> {
const payload = await this.post<{ membership: OrganizationMembership }>(`/organizations/${organizationId}/memberships`, {
actor_user_id: this.actorUserId,
user_id: input.userId,
role_id: input.roleId,
});
return payload.membership;
}
async listResources(organizationId?: string): Promise<Resource[]> {
const params = new URLSearchParams({ user_id: this.actorUserId });
if (organizationId) {
params.set("organization_id", organizationId);
}
const payload = await this.request<{ resources: Resource[] }>(`/resources?${params.toString()}`, { method: "GET" });
return payload.resources ?? [];
}
async createResource(input: CreateResourcePayload): Promise<Resource> {
const payload = await this.post<{ resource: Resource }>("/resources/", {
actor_user_id: this.actorUserId,
organization_id: input.organizationId,
name: input.name,
address: input.address,
protocol: input.protocol || "rdp",
secret_ref: input.secretRef || null,
certificate_verification_mode: input.certificateVerificationMode || "strict",
render_quality_profile: input.renderQualityProfile || "balanced",
clipboard_mode: input.clipboardMode || "disabled",
file_transfer_mode: input.fileTransferMode || "disabled",
metadata: input.metadata || {},
});
return payload.resource;
}
async upsertResourceSecret(resourceId: string, input: UpsertResourceSecretPayload): Promise<void> {
await this.put(`/resources/${resourceId}/secret`, {
actor_user_id: this.actorUserId,
payload: {
username: input.username || "",
password: input.password || "",
domain: input.domain || "",
},
metadata: {
source: "web-admin",
},
});
}
private async get<T>(path: string): Promise<T> {
const separator = path.includes("?") ? "&" : "?";
return this.request<T>(`${path}${separator}actor_user_id=${encodeURIComponent(this.actorUserId)}`, {
@@ -564,6 +1367,14 @@ export class AdminApiClient {
});
}
private async delete<T>(path: string, body: unknown): Promise<T> {
return this.request<T>(path, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
}
private async request<T>(path: string, init: RequestInit): Promise<T> {
const response = await fetch(`${this.baseUrl}${path}`, init);
if (!response.ok) {
+478 -11
View File
@@ -149,7 +149,14 @@ button.danger {
min-height: 100vh;
}
.sideRail {
.portalShell {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
min-height: 100vh;
}
.sideRail,
.portalRail {
position: sticky;
top: 0;
height: 100vh;
@@ -160,6 +167,11 @@ button.danger {
radial-gradient(circle at 20% 20%, rgba(184, 111, 35, 0.36), transparent 16rem);
}
.portalRail button {
width: 100%;
margin-top: 16px;
}
.brandMark {
display: inline-grid;
width: 58px;
@@ -183,7 +195,8 @@ button.danger {
text-transform: uppercase;
}
.sideRail h1 {
.sideRail h1,
.portalRail h1 {
margin: 0 0 12px;
font-size: clamp(2.3rem, 4vw, 4.2rem);
line-height: 0.9;
@@ -218,9 +231,15 @@ button.danger {
padding: 28px 28px 54px;
}
.topBar {
.portalWorkspace {
width: min(1280px, calc(100vw - 310px));
padding: 28px 28px 54px;
}
.topBar,
.portalTop {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(260px, 360px) auto minmax(220px, auto);
grid-template-columns: minmax(0, 1fr) minmax(260px, 360px) auto minmax(150px, auto) minmax(220px, auto);
align-items: center;
gap: 20px;
padding: 28px;
@@ -231,6 +250,10 @@ button.danger {
backdrop-filter: blur(16px);
}
.portalTop {
grid-template-columns: minmax(0, 1fr) minmax(240px, 340px) auto;
}
.clusterPicker {
display: grid;
gap: 8px;
@@ -248,6 +271,25 @@ button.danger {
overflow-wrap: anywhere;
}
.refreshStatus {
display: grid;
gap: 4px;
min-width: 150px;
color: var(--muted);
font-size: 0.78rem;
font-weight: 800;
line-height: 1.35;
}
.refreshStatus strong {
color: var(--text);
font-size: 0.82rem;
}
.refreshStatus span {
overflow-wrap: anywhere;
}
.profilePanel {
display: grid;
grid-template-columns: 1fr;
@@ -441,6 +483,69 @@ summary {
gap: 16px;
}
.cardHead.compact {
gap: 10px;
}
.subPanel {
display: grid;
gap: 14px;
padding: 16px;
border: 1px solid var(--line);
border-radius: 8px;
background: rgba(255, 255, 255, 0.45);
}
.portalInstallList {
display: grid;
gap: 10px;
margin-top: 14px;
}
.installTile {
display: grid;
gap: 6px;
padding: 16px;
border: 1px solid var(--line);
border-radius: 18px;
color: var(--ink);
background: rgba(255, 255, 255, 0.52);
text-decoration: none;
}
.installTile:hover {
transform: translateY(-1px);
}
.installTile strong {
font-size: 1.05rem;
}
.installTile span,
.installTile small {
color: var(--muted);
overflow-wrap: anywhere;
}
.primaryInstall {
border-color: rgba(47, 111, 79, 0.35);
background: rgba(47, 111, 79, 0.1);
}
.portalRoadmap {
display: grid;
gap: 8px;
}
.portalRoadmap span {
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: 14px;
background: rgba(255, 255, 255, 0.48);
color: var(--muted);
font-weight: 800;
}
.vpnCard {
grid-template-columns: minmax(0, 1.2fr) minmax(220px, 0.8fr) auto;
padding: 16px 0;
@@ -476,7 +581,7 @@ summary {
.nodeListRow {
display: grid;
grid-template-columns: minmax(240px, 1.2fr) auto minmax(110px, 0.55fr) minmax(130px, 0.7fr) auto auto;
grid-template-columns: minmax(220px, 1.1fr) auto minmax(220px, 0.9fr) minmax(150px, 0.7fr) auto minmax(180px, 0.8fr) minmax(130px, 0.7fr) auto auto;
align-items: center;
gap: 10px;
padding: 10px;
@@ -496,6 +601,43 @@ summary {
white-space: nowrap;
}
.runtimeBadges {
display: flex;
flex-wrap: wrap;
gap: 5px;
min-width: 0;
}
.runtimeBadges .pill {
padding: 0.24rem 0.52rem;
font-size: 0.68rem;
}
.nodeEndpointCell {
display: grid;
gap: 5px;
min-width: 0;
}
.nodeEndpointCell .pill {
width: fit-content;
max-width: 100%;
padding: 0.28rem 0.58rem;
font-size: 0.72rem;
}
.nodeEndpointCell strong,
.nodeEndpointCell small {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.nodeEndpointCell small {
color: var(--muted);
font-size: 0.74rem;
}
.functionList {
display: grid;
gap: 8px;
@@ -513,6 +655,41 @@ summary {
background: rgba(255, 255, 255, 0.42);
}
.functionState {
min-width: 96px;
display: grid;
gap: 2px;
padding: 8px 10px;
border-radius: 14px;
background: rgba(16, 43, 35, 0.06);
color: var(--ink);
}
.functionState small {
color: var(--muted);
font-size: 0.68rem;
font-weight: 800;
}
.functionState strong {
font-size: 0.76rem;
}
.functionState.good {
background: rgba(36, 118, 84, 0.14);
color: var(--green);
}
.functionState.info {
background: rgba(41, 80, 111, 0.12);
color: var(--blue);
}
.functionState.warn {
background: rgba(175, 110, 46, 0.15);
color: #8a4f19;
}
.nodePanel {
display: grid;
gap: 12px;
@@ -522,6 +699,111 @@ summary {
background: rgba(255, 255, 255, 0.42);
}
.nodeDetails {
display: grid;
gap: 14px;
}
.nodeDetailGrid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
}
.nodeMetricGrid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.summaryChips {
display: flex;
flex-wrap: wrap;
gap: 8px;
min-width: 0;
}
.inlineActions {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
}
.stackedText {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.nodeTabs {
display: flex;
flex-wrap: wrap;
gap: 8px;
position: sticky;
top: 0;
z-index: 2;
padding: 8px 0;
background: rgba(249, 247, 239, 0.94);
}
.nodeTabs button {
min-width: 92px;
padding: 0.55rem 0.8rem;
}
.nodeTabs button.active {
color: #fffaf0;
border-color: transparent;
background: #36556c;
}
.rawDetailsGrid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
.rawBlock {
min-width: 0;
border: 1px solid var(--line);
border-radius: 12px;
background: rgba(255, 255, 255, 0.42);
}
.rawBlock summary {
cursor: pointer;
padding: 10px 12px;
font-weight: 700;
}
.rawBlock pre {
max-height: 320px;
margin: 0;
overflow: auto;
padding: 0 12px 12px;
font-size: 0.78rem;
line-height: 1.45;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.segmented {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.segmented button {
min-width: 84px;
padding: 0.52rem 0.85rem;
}
.segmented button.active {
color: #fffaf0;
border-color: transparent;
background: #36556c;
}
.stateList {
display: grid;
gap: 10px;
@@ -621,11 +903,21 @@ summary {
background: rgba(47, 111, 79, 0.12);
}
.pill.info {
color: var(--steel);
background: rgba(54, 85, 108, 0.1);
}
.pill.bad {
color: var(--red);
background: rgba(177, 68, 52, 0.12);
}
.pill.warn {
color: var(--amber);
background: rgba(184, 111, 35, 0.14);
}
.membershipList {
display: flex;
flex-wrap: wrap;
@@ -691,7 +983,8 @@ summary {
.topologySvg {
width: 100%;
min-height: 440px;
min-height: 520px;
max-height: 78vh;
border: 1px solid var(--line);
border-radius: 22px;
color: rgba(24, 32, 24, 0.42);
@@ -750,12 +1043,25 @@ summary {
stroke: var(--amber);
}
.topologyLink.oneWay {
color: var(--amber);
stroke: var(--amber);
stroke-dasharray: 4 7;
}
.topologyLink.bad {
color: var(--red);
stroke: var(--red);
stroke-dasharray: 10 8;
}
.topologyLink.stale {
color: var(--red);
stroke: var(--red);
stroke-dasharray: 2 8;
opacity: 0.42;
}
.topologyPlacementLink {
stroke: var(--steel);
stroke-width: 3;
@@ -774,6 +1080,15 @@ summary {
stroke: var(--amber);
}
.topologyConfiguredLink {
color: var(--steel);
stroke: var(--steel);
stroke-width: 2.5;
stroke-linecap: round;
stroke-dasharray: 4 7;
opacity: 0.72;
}
.topologyLinkLabel,
.topologyNodeName,
.topologyNodeMeta,
@@ -834,6 +1149,7 @@ summary {
}
.topologyNodeCircle.critical,
.topologyNodeCircle.offline,
.topologyNodeCircle.failed {
stroke: var(--red);
}
@@ -884,6 +1200,22 @@ summary {
border-color: var(--amber);
}
.legendLine.oneWay {
border-top-style: dashed;
border-color: var(--amber);
}
.legendLine.stale,
.legendLine.problem {
border-top-style: dashed;
border-color: var(--red);
}
.legendLine.configured {
border-top-style: dashed;
border-color: var(--steel);
}
.serviceTags {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
@@ -944,13 +1276,25 @@ summary {
.status.active,
.status.approved,
.status.healthy,
.status.connected,
.status.current,
.status.running,
.status.authoritative {
background: var(--green);
}
.status.outdated,
.status.no_policy {
background: var(--amber);
}
.status.rejected,
.status.failed,
.status.critical,
.status.missing,
.status.offline,
.status.stale,
.status.unreachable,
.status.disabled,
.status.revoked {
background: var(--red);
@@ -996,6 +1340,23 @@ th {
background: var(--green);
}
.noticePanel.goodPanel {
background: var(--green);
}
.noticePanel.warnPanel {
background: var(--amber);
}
.noticePanel.badPanel {
background: var(--red);
}
.noticePanel .muted,
.noticePanel .stateLine span {
color: rgba(255, 255, 255, 0.78);
}
.formGrid {
display: grid;
grid-template-columns: repeat(2, minmax(180px, 1fr));
@@ -1060,10 +1421,30 @@ th {
margin: 18px 0;
}
.inlineForm input {
.inlineForm input,
.inlineForm select {
flex: 1 1 320px;
}
.diagnosticCommandPanel {
display: grid;
gap: 12px;
margin: 12px 0 18px;
padding: 14px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255, 255, 255, 0.42);
}
.diagnosticCommandPanel label {
display: grid;
gap: 6px;
}
.diagnosticCommandPanel input {
min-width: min(520px, 100%);
}
.secretOnce {
display: grid;
gap: 8px;
@@ -1087,16 +1468,19 @@ code {
}
@media (max-width: 1100px) {
.consoleShell {
.consoleShell,
.portalShell {
grid-template-columns: 1fr;
}
.sideRail {
.sideRail,
.portalRail {
position: relative;
height: auto;
}
.workspace {
.workspace,
.portalWorkspace {
width: 100%;
}
@@ -1105,7 +1489,10 @@ code {
.grid.two,
.controlBar,
.topBar,
.portalTop,
.loginShell,
.nodeDetailGrid,
.rawDetailsGrid,
.vpnCard {
grid-template-columns: 1fr;
}
@@ -1121,11 +1508,91 @@ code {
}
@media (max-width: 720px) {
body {
background: #f4f1e7;
}
button,
input,
select,
textarea {
font-size: 16px;
}
button {
min-height: 46px;
}
.workspace,
.sideRail {
.portalWorkspace,
.sideRail,
.portalRail {
padding: 18px;
}
.loginShell {
align-items: start;
padding: 16px;
}
.loginCard {
padding: 18px;
border-radius: 18px;
}
.portalRail {
height: auto;
min-height: 0;
}
.portalRail .brandMark,
.portalRail .sideKicker,
.portalRail .sideText {
display: none;
}
.portalRail h1 {
margin: 0;
font-size: 2rem;
letter-spacing: 0;
}
.portalTop,
.card,
.metric,
.empty,
.errorPanel,
.noticePanel {
border-radius: 18px;
}
.portalTop {
padding: 18px;
}
.portalTop h2 {
font-size: 2rem;
letter-spacing: 0;
}
.portalInstallList,
.stack,
.grid {
gap: 12px;
}
.installTile {
border-radius: 14px;
}
.metric {
min-height: 96px;
}
.metric strong {
font-size: 2.2rem;
}
.formGrid,
.signalStrip,
.clusterCardMain,
+886
View File
@@ -148,6 +148,8 @@ export type CreatedJoinToken = {
token: string;
};
export type NodeJoinToken = Omit<CreatedJoinToken, "token">;
export type RoleAssignment = {
id: string;
cluster_id: string;
@@ -168,9 +170,58 @@ export type AuditEvent = {
target_type: string;
target_id?: string | null;
payload: Record<string, unknown>;
correlation_hints?: AuditCorrelationHints;
created_at: string;
};
export type AuditCorrelationHints = {
scope?: string;
current_diagnostic_status?: string;
breadcrumb_status?: string;
breadcrumb_age_seconds?: number;
breadcrumb_current_window_seconds?: number;
breadcrumb_history_window_seconds?: number;
feedback_breakdown?: FabricServiceChannelRouteRebuildFeedbackHealthBreakdown;
rebuild_incident?: FabricServiceChannelRouteRebuildIncident;
recommended_action?: string;
};
export type AuditSummary = {
total_count: number;
counts_by_event_type?: Record<string, number>;
counts_by_target_type?: Record<string, number>;
counts_by_current_diagnostic_status?: Record<string, number>;
counts_by_feedback_source?: Record<string, number>;
counts_by_feedback_violation_status?: Record<string, number>;
counts_by_breadcrumb_status?: Record<string, number>;
correlated_count?: number;
not_visible_count?: number;
latest_at?: string;
};
export type FabricServiceChannelRebuildInvestigationBreadcrumbs = {
cluster_id: string;
events: AuditEvent[];
summary: AuditSummary;
current_window_seconds?: number;
history_window_seconds?: number;
current_count?: number;
stale_count?: number;
expired_count?: number;
};
export type FabricServiceChannelBreadcrumbWindowPolicy = {
schema_version: string;
fingerprint?: string;
current_window_seconds: number;
history_window_seconds: number;
source: string;
updated_by_user_id?: string | null;
updated_at?: string;
control_plane_only: boolean;
production_forwarding: boolean;
};
export type WorkloadStatus = {
id: string;
cluster_id: string;
@@ -239,6 +290,90 @@ export type NodeTelemetryObservation = {
observed_at: string;
};
export type ReleaseArtifact = {
id: string;
release_id: string;
cluster_id: string;
product: string;
version: string;
os: string;
arch: string;
install_type: string;
kind: string;
url: string;
sha256: string;
size_bytes: number;
signature?: string | null;
metadata?: Record<string, unknown>;
created_at: string;
};
export type ReleaseVersion = {
id: string;
cluster_id: string;
product: string;
version: string;
channel: string;
status: string;
compatibility?: Record<string, unknown>;
changelog?: string | null;
created_by_user_id?: string | null;
created_at: string;
artifacts?: ReleaseArtifact[];
authority_payload?: Record<string, unknown>;
authority_signature?: ClusterSignature;
};
export type NodeUpdatePlan = {
schema_version: string;
cluster_id: string;
node_id: string;
product: string;
current_version?: string;
action: string;
reason: string;
target_version?: string;
channel?: string;
strategy?: string;
rollback_allowed: boolean;
health_window_seconds?: number;
artifact?: ReleaseArtifact;
authority_payload?: Record<string, unknown>;
authority_signature?: ClusterSignature;
production_forwarding: boolean;
};
export type NodeUpdatePolicy = {
id: string;
cluster_id: string;
node_id: string;
product: string;
channel: string;
target_version?: string | null;
strategy: string;
enabled: boolean;
rollback_allowed: boolean;
health_window_seconds: number;
updated_by_user_id?: string | null;
updated_at: string;
};
export type NodeUpdateStatus = {
id: string;
cluster_id: string;
node_id: string;
product: string;
current_version?: string;
target_version?: string;
phase: string;
status: string;
attempt_id?: string;
error_message?: string | null;
rollback_version?: string | null;
payload?: Record<string, unknown>;
observed_at: string;
};
export type MeshLink = {
id: string;
cluster_id: string;
@@ -251,6 +386,23 @@ export type MeshLink = {
observed_at: string;
};
export type MeshRouteIntent = {
id: string;
cluster_id: string;
source_selector: Record<string, unknown>;
destination_selector: Record<string, unknown>;
service_class: string;
priority: number;
status: string;
lifecycle_status?: string;
is_expired?: boolean;
policy_expires_at?: string | null;
policy: Record<string, unknown>;
created_by_user_id?: string | null;
created_at: string;
updated_at: string;
};
export type PeerEndpointCandidate = {
endpoint_id: string;
node_id: string;
@@ -330,6 +482,19 @@ export type RendezvousRelayPolicyReport = {
export type RoutePathDecision = {
decision_id: string;
route_id: string;
replacement_route_id?: string;
rebuild_request_id?: string;
rebuild_status?: string;
rebuild_reason?: string;
rebuild_attempt?: number;
feedback_observation_id?: string;
feedback_source?: string;
feedback_observed_at?: string;
feedback_expires_at?: string;
feedback_channel_id?: string;
feedback_resource_id?: string;
feedback_violation_status?: string;
feedback_violation_reason?: string;
cluster_id: string;
local_node_id: string;
source_node_id: string;
@@ -357,8 +522,15 @@ export type RoutePathDecisionReport = {
schema_version: string;
decision_mode: string;
generation: string;
recovery_policy?: FabricServiceChannelRecoveryPolicy;
decision_count: number;
replacement_decision_count: number;
degraded_decision_count?: number;
rebuild_request_count?: number;
rebuild_applied_count?: number;
recovery_hysteresis_count?: number;
recovery_promoted_count?: number;
recovery_demoted_count?: number;
control_plane_only: boolean;
production_forwarding: boolean;
decisions?: RoutePathDecision[];
@@ -379,6 +551,620 @@ export type SyntheticMeshRoute = {
peer_directory_version?: string;
};
export type FabricServiceChannelRouteFeedbackObservation = {
id?: string;
cluster_id: string;
reporter_node_id: string;
route_id: string;
service_class: string;
feedback_status: string;
score_adjustment: number;
reasons?: string[];
last_error?: string;
consecutive_failures?: number;
stall_count?: number;
last_send_duration_ms?: number;
payload?: Record<string, unknown>;
observed_at: string;
expires_at: string;
retry_cooldown_until?: string;
recovery_state?: string;
recovery_hysteresis_active?: boolean;
recovery_hysteresis_penalty?: number;
recovery_promoted?: boolean;
recovery_demoted?: boolean;
recovery_reason?: string;
observed_policy_fingerprint?: string;
effective_policy_fingerprint?: string;
observed_route_generation?: string;
effective_route_generation?: string;
provenance_missing?: boolean;
stale_policy?: boolean;
stale_generation?: boolean;
stale_reason?: string;
};
export type FabricServiceChannelRouteRebuildAttempt = {
id: string;
cluster_id: string;
reporter_node_id: string;
service_class: string;
route_id: string;
replacement_route_id?: string;
rebuild_request_id: string;
rebuild_status: string;
rebuild_reason?: string;
rebuild_attempt?: number;
decision_source: string;
outcome: string;
generation?: string;
policy_fingerprint?: string;
observed_policy_fingerprint?: string;
observed_route_generation?: string;
effective_route_generation?: string;
feedback_status?: string;
feedback_observation_id?: string;
feedback_source?: string;
feedback_observed_at?: string;
feedback_expires_at?: string;
feedback_channel_id?: string;
feedback_resource_id?: string;
feedback_violation_status?: string;
feedback_violation_reason?: string;
feedback_score_adjustment?: number;
feedback_effective_score_adjustment?: number;
feedback_reasons?: string[];
last_error?: string;
consecutive_failures?: number;
stall_count?: number;
last_send_duration_ms?: number;
quality_window_sample_count?: number;
quality_window_failure_count?: number;
quality_window_drop_count?: number;
quality_window_slow_count?: number;
old_hops?: string[];
replacement_hops?: string[];
node_transition_status?: string;
node_transition_generation?: string;
node_transition_observed_at?: string;
node_transition_matched?: boolean;
node_route_generation_status?: string;
node_route_generation_applied_at?: string;
node_route_generation_withdrawn_at?: string;
node_route_generation_matched?: boolean;
post_rebuild_selected_route_id?: string;
post_rebuild_send_packets?: number;
post_rebuild_send_failures?: number;
post_rebuild_send_flow_packets?: number;
post_rebuild_send_flow_dropped?: number;
guard_status?: string;
guard_severity?: string;
guard_reason?: string;
guard_age_seconds?: number;
guard_transition_deadline_seconds?: number;
guard_traffic_deadline_seconds?: number;
alert_silenced?: boolean;
alert_silence_id?: string;
alert_silence_reason?: string;
alert_silenced_until?: string;
alert_resurfaced?: boolean;
alert_resurfaced_from_silence_id?: string;
alert_resurfaced_previous_generation?: string;
alert_resurfaced_previous_until?: string;
timeline?: FabricServiceChannelRouteRebuildTimelineEvent[];
payload?: Record<string, unknown>;
created_at: string;
updated_at: string;
};
export type FabricServiceChannelRouteRebuildTimelineEvent = {
stage: string;
status: string;
at?: string;
route_id?: string;
generation?: string;
payload?: Record<string, unknown>;
};
export type FabricServiceChannelRouteRebuildHealthSummary = {
cluster_id: string;
observed_at: string;
window_limit: number;
total_attempts: number;
good_count: number;
warn_count: number;
bad_count: number;
unknown_count: number;
active_bad_count: number;
active_warn_count: number;
silenced_count: number;
resurfaced_count: number;
applied_count: number;
pending_count: number;
access_route_decision_count?: number;
access_replacement_count?: number;
access_applied_count?: number;
access_recovery_count?: number;
access_no_safe_count?: number;
counts_by_guard_status?: Record<string, number>;
counts_by_guard_severity?: Record<string, number>;
feedback_breakdowns?: FabricServiceChannelRouteRebuildFeedbackHealthBreakdown[];
affected_reporter_node_ids?: string[];
affected_route_ids?: string[];
most_recent_bad_attempts?: FabricServiceChannelRouteRebuildAttempt[];
resurfaced_attempts?: FabricServiceChannelRouteRebuildAttempt[];
recommended_operator_action?: string;
};
export type FabricServiceChannelRouteRebuildFeedbackHealthBreakdown = {
feedback_source?: string;
feedback_channel_id?: string;
feedback_violation_status?: string;
total_count: number;
good_count?: number;
warn_count?: number;
bad_count?: number;
unknown_count?: number;
active_warn_count?: number;
active_bad_count?: number;
silenced_count?: number;
latest_observed_at?: string;
affected_reporter_node_ids?: string[];
affected_route_ids?: string[];
};
export type FabricServiceChannelReadiness = {
cluster_id: string;
observed_at: string;
status: string;
reason: string;
active_alert_count: number;
active_bad_count: number;
active_warn_count: number;
resurfaced_count: number;
silenced_count: number;
missing_transition_count: number;
missing_route_generation_count: number;
missing_post_rebuild_traffic_count: number;
unexpected_route_count: number;
post_rebuild_degraded_count: number;
blocking_reasons?: string[];
degraded_reasons?: string[];
recommended_operator_action?: string;
};
export type FabricServiceChannelSchemaStatus = {
cluster_id: string;
observed_at: string;
status: string;
reason: string;
required_migration: string;
required_check_count: number;
passed_check_count: number;
missing_check_count: number;
required_checks: FabricServiceChannelSchemaCheck[];
missing_checks?: FabricServiceChannelSchemaCheck[];
recommended_operator_action?: string;
};
export type FabricServiceChannelSchemaCheck = {
check_id: string;
relation_name: string;
column_name?: string;
status: string;
required_by: string;
};
export type FabricServiceChannelRebuildSnapshotWarmup = {
cluster_id: string;
observed_at: string;
window_limit: number;
stale_after_seconds: number;
scanned_count: number;
warmed_count: number;
already_fresh_count: number;
missing_snapshot_count: number;
stale_snapshot_count: number;
deferred_stale_count: number;
error_count: number;
status: string;
reason: string;
recommended_operator_action?: string;
};
export type FabricServiceChannelRebuildSnapshotMaintenanceHealth = {
cluster_id: string;
observed_at: string;
status: string;
reason: string;
window_limit: number;
min_age_seconds: number;
heartbeat_threshold: number;
recent_attempt_count: number;
valid_snapshot_count: number;
missing_snapshot_count: number;
overdue_missing_snapshot_count: number;
auto_warmup_event_count: number;
auto_warmup_warmed_count: number;
auto_warmup_already_fresh_count: number;
auto_warmup_error_count: number;
latest_auto_warmup_at?: string;
nodes?: FabricServiceChannelRebuildSnapshotNodeHealth[];
overdue_missing_snapshot_attempts?: FabricServiceChannelRouteRebuildAttempt[];
recommended_operator_action?: string;
};
export type FabricServiceChannelRebuildSnapshotNodeHealth = {
node_id: string;
recent_attempt_count: number;
valid_snapshot_count: number;
missing_snapshot_count: number;
overdue_missing_snapshot_count: number;
heartbeat_after_attempt_count: number;
last_heartbeat_at?: string;
auto_warmup_event_count: number;
auto_warmup_warmed_count: number;
auto_warmup_error_count: number;
latest_auto_warmup_at?: string;
};
export type FabricServiceChannelLeaseSummary = {
cluster_id: string;
channel_id: string;
resource_id?: string;
service_class: string;
status: string;
selected_entry_node_id?: string;
selected_exit_node_id?: string;
allowed_channels?: string[];
primary_route_id?: string;
primary_route_status?: string;
data_plane?: FabricServiceChannelDataPlaneContract;
force_backend_fallback: boolean;
expired: boolean;
issued_at: string;
expires_at: string;
created_at: string;
updated_at: string;
};
export type FabricServiceChannelLeaseMaintenance = {
schema_version: string;
cluster_id: string;
status: string;
reason: string;
observed_at: string;
active_count: number;
expired_count: number;
scanned_count: number;
deleted_expired_count?: number;
window_limit: number;
recommended_operator_action?: string;
leases?: FabricServiceChannelLeaseSummary[];
};
export type FabricServiceChannelDataPlaneContract = {
schema_version: string;
mode: string;
control_plane_transport: string;
working_data_transport: string;
steady_state_transport: string;
backend_relay_policy: string;
production_forwarding_required: boolean;
service_neutral: boolean;
protocol_agnostic: boolean;
logical_flow_mode: string;
required_flow_isolation_classes?: string[];
route_selection_strategy: string;
entry_failover_mode: string;
exit_failover_mode: string;
route_rebuild_mode: string;
failure_detection_source: string;
degraded_fallback_visibility: string;
stable_contract_for_service_class?: string;
};
export type FabricServiceChannelAccessTelemetryNode = {
node_id: string;
node_name?: string;
observed_at: string;
total_accepted: number;
signed_accepted: number;
introspection_accepted: number;
legacy_unsigned_accepted: number;
backend_fallback_count: number;
backend_fallback_blocked_count?: number;
fabric_route_send_failure_count?: number;
data_plane_contract_count?: number;
last_data_plane_mode?: string;
last_working_data_transport?: string;
last_steady_state_transport?: string;
last_backend_relay_policy?: string;
last_logical_flow_mode?: string;
last_data_plane_violation_status?: string;
last_data_plane_violation_reason?: string;
traffic_class_counts?: Record<string, number>;
flow_channel_count?: number;
flow_dropped?: number;
flow_high_watermark?: number;
flow_max_in_flight?: number;
flow_health_status?: string;
flow_health_reason?: string;
recommended_parallel_windows?: Record<string, number>;
adaptive_backpressure_active?: boolean;
adaptive_backpressure_reason?: string;
adaptive_policy_fingerprint?: string;
last_accepted_at?: string;
};
export type FabricServiceChannelAccessTelemetryChannel = {
channel_id: string;
resource_id?: string;
service_class: string;
status: string;
selected_entry_node_id?: string;
selected_exit_node_id?: string;
primary_route_id?: string;
primary_route_status?: string;
force_backend_fallback: boolean;
entry_node_total_accepted: number;
entry_node_introspection_accepted: number;
entry_node_backend_fallback_count: number;
entry_node_backend_fallback_blocked_count?: number;
entry_node_fabric_route_send_failure_count?: number;
entry_node_data_plane_contract_count?: number;
entry_node_last_data_plane_mode?: string;
entry_node_last_working_data_transport?: string;
entry_node_last_steady_state_transport?: string;
entry_node_last_backend_relay_policy?: string;
entry_node_last_logical_flow_mode?: string;
entry_node_last_data_plane_violation_status?: string;
entry_node_last_data_plane_violation_reason?: string;
entry_node_traffic_class_counts?: Record<string, number>;
entry_node_flow_channel_count?: number;
entry_node_flow_dropped?: number;
entry_node_flow_high_watermark?: number;
entry_node_flow_max_in_flight?: number;
entry_node_flow_health_status?: string;
entry_node_flow_health_reason?: string;
entry_node_recommended_parallel_windows?: Record<string, number>;
entry_node_adaptive_backpressure_active?: boolean;
entry_node_adaptive_backpressure_reason?: string;
entry_node_adaptive_policy_fingerprint?: string;
route_feedback_status?: string;
route_feedback_observed_at?: string;
route_feedback_score_adjustment?: number;
route_feedback_effective_score_adjustment?: number;
route_feedback_reasons?: string[];
route_quality_window_sample_count?: number;
route_quality_window_failure_count?: number;
route_quality_window_drop_count?: number;
route_quality_window_slow_count?: number;
last_send_duration_ms?: number;
remediation_action?: string;
remediation_reason?: string;
remediation_route_id?: string;
remediation_route_status?: string;
remediation_guard_status?: string;
remediation_guard_reason?: string;
remediation_execution_status?: string;
remediation_execution_reason?: string;
remediation_execution_generation?: string;
remediation_execution_observed_at?: string;
route_decision_source?: string;
route_decision_route_id?: string;
route_decision_replacement_route_id?: string;
route_decision_rebuild_status?: string;
route_decision_rebuild_reason?: string;
route_decision_generation?: string;
route_decision_score_reasons?: string[];
pool_policy_fingerprint?: string;
data_plane?: FabricServiceChannelDataPlaneContract;
remediation_command?: {
schema_version: string;
command_id: string;
action: string;
cluster_id: string;
channel_id: string;
resource_id?: string;
service_class: string;
entry_node_id?: string;
exit_node_id?: string;
primary_route_id?: string;
replacement_route_id?: string;
replacement_route_status?: string;
pool_policy_fingerprint?: string;
guard_status?: string;
guard_reason?: string;
execution_status?: string;
execution_reason?: string;
execution_generation?: string;
execution_observed_at?: string;
reason?: string;
operator_action?: string;
issued_at: string;
expires_at: string;
};
recommended_operator_action?: string;
expires_at: string;
};
export type FabricServiceChannelAccessTelemetry = {
schema_version: string;
cluster_id: string;
status: string;
reason: string;
observed_at: string;
node_count: number;
reporting_node_count: number;
total_accepted: number;
signed_accepted: number;
introspection_accepted: number;
legacy_unsigned_accepted: number;
backend_fallback_count: number;
backend_fallback_blocked_count?: number;
fabric_route_send_failure_count?: number;
data_plane_contract_count?: number;
last_data_plane_mode?: string;
last_working_data_transport?: string;
last_steady_state_transport?: string;
last_backend_relay_policy?: string;
last_logical_flow_mode?: string;
last_data_plane_violation_status?: string;
last_data_plane_violation_reason?: string;
active_channel_count: number;
degraded_fallback_channel_count: number;
correlated_route_count: number;
degraded_route_count: number;
route_decision_channel_count?: number;
replacement_decision_count?: number;
applied_rebuild_decision_count?: number;
recovery_decision_count?: number;
no_safe_recovery_decision_count?: number;
traffic_class_counts?: Record<string, number>;
flow_channel_count?: number;
flow_dropped?: number;
flow_high_watermark?: number;
flow_max_in_flight?: number;
flow_health_status?: string;
flow_health_reason?: string;
recommended_parallel_windows?: Record<string, number>;
adaptive_backpressure_active?: boolean;
adaptive_backpressure_reason?: string;
adaptive_policy_fingerprint?: string;
latest_accepted_at?: string;
nodes?: FabricServiceChannelAccessTelemetryNode[];
active_channels?: FabricServiceChannelAccessTelemetryChannel[];
recommended_operator_action?: string;
};
export type FabricServiceChannelRouteRebuildIncident = {
fingerprint: string;
cluster_id: string;
reporter_node_id: string;
route_id: string;
service_class: string;
generation?: string;
incident_source?: string;
channel_id?: string;
guard_status: string;
guard_severity: string;
guard_reason?: string;
attempt_count: number;
first_seen_at: string;
last_seen_at: string;
latest_replacement_route_id?: string;
latest_rebuild_status?: string;
latest_outcome?: string;
alert_silenced?: boolean;
alert_resurfaced?: boolean;
alert_resurfaced_from_silence_id?: string;
alert_resurfaced_cause?: string;
alert_resurfaced_previous_route_id?: string;
alert_resurfaced_previous_channel_id?: string;
alert_resurfaced_previous_generation?: string;
alert_resurfaced_previous_until?: string;
recommended_operator_action?: string;
};
export type FabricServiceChannelRouteRebuildAlertSilence = {
id: string;
cluster_id: string;
incident_source?: string;
channel_id?: string;
reporter_node_id: string;
route_id: string;
display_route_id?: string;
guard_status: string;
generation?: string;
reason?: string;
created_by_user_id?: string | null;
created_at: string;
expires_at: string;
payload?: Record<string, unknown>;
};
export type ExpireFabricServiceChannelRouteFeedbackResult = {
cluster_id: string;
reporter_node_id?: string;
route_id: string;
service_class?: string;
expired_count: number;
expired_at: string;
cooldown_until: string;
};
export type FabricServiceChannelRouteFeedbackReport = {
schema_version: string;
generated_at: string;
feedback_max_age_seconds: number;
recovery_policy?: FabricServiceChannelRecoveryPolicy;
observation_count: number;
fenced_route_count: number;
degraded_route_count: number;
healthy_route_count: number;
recovered_route_count?: number;
recovery_hysteresis_count?: number;
recovery_promoted_count?: number;
recovery_demoted_count?: number;
missing_provenance_count?: number;
stale_policy_count?: number;
stale_generation_count?: number;
observations?: FabricServiceChannelRouteFeedbackObservation[];
};
export type FabricServiceChannelRecoveryPolicy = {
schema_version: string;
fingerprint?: string;
hysteresis_penalty: number;
promotion_min_samples: number;
demotion_failure_threshold: number;
demotion_drop_threshold: number;
demotion_slow_threshold: number;
demotion_rebuild_enabled: boolean;
demotion_fenced_enabled: boolean;
source: string;
updated_by_user_id?: string | null;
updated_at?: string;
control_plane_only: boolean;
production_forwarding: boolean;
};
export type FabricServiceChannelAdaptivePolicy = {
schema_version: string;
fingerprint?: string;
max_parallel_window: number;
bulk_pressure_channel_threshold: number;
queue_pressure_high_watermark: number;
queue_pressure_max_in_flight: number;
class_windows: Record<string, number>;
source: string;
updated_by_user_id?: string | null;
updated_at?: string;
control_plane_only: boolean;
production_forwarding: boolean;
};
export type FabricServiceChannelPoolPolicy = {
schema_version: string;
fingerprint?: string;
entry_pool_node_ids?: string[];
exit_pool_node_ids?: string[];
preferred_entry_node_id?: string;
preferred_exit_node_id?: string;
selection_strategy: string;
route_rebuild: string;
entry_failover: string;
exit_failover: string;
backend_fallback_allowed: boolean;
sticky_session: boolean;
source: string;
updated_by_user_id?: string | null;
updated_at?: string;
control_plane_only: boolean;
production_forwarding: boolean;
};
export type NodeSyntheticMeshConfig = {
enabled: boolean;
schema_version: string;
@@ -398,6 +1184,28 @@ export type NodeSyntheticMeshConfig = {
rendezvous_leases?: PeerRendezvousLease[];
rendezvous_relay_policy?: RendezvousRelayPolicyReport;
route_path_decisions?: RoutePathDecisionReport;
service_channel_route_feedback?: FabricServiceChannelRouteFeedbackReport;
service_channel_adaptive_policy?: FabricServiceChannelAdaptivePolicy;
service_channel_remediation_commands?: FabricServiceChannelAccessTelemetryChannel["remediation_command"][];
mesh_listener?: {
schema_version: string;
source: string;
desired_state: string;
listen_addr: string;
listen_port_mode: string;
auto_port_start?: number;
auto_port_end?: number;
advertise_endpoint?: string;
advertise_transport?: string;
connectivity_mode?: string;
nat_type?: string;
region?: string;
config_version?: string;
updated_by_user_id?: string;
updated_at?: string;
control_plane_only: boolean;
production_forwarding: boolean;
};
routes: SyntheticMeshRoute[];
production_forwarding: boolean;
};
@@ -489,6 +1297,53 @@ export type OrganizationAdminSummary = {
topology_exposure: string;
};
export type Organization = {
id: string;
slug: string;
name: string;
status: string;
metadata?: Record<string, unknown>;
created_at: string;
updated_at: string;
};
export type OrganizationMembership = {
id: string;
organization_id: string;
user_id: string;
role_id: string;
status: string;
invited_by_user_id?: string | null;
created_at: string;
updated_at: string;
};
export type UserAccount = {
id: string;
email: string;
mfa_enabled: boolean;
platform_role: string;
created_at: string;
updated_at: string;
};
export type Resource = {
id: string;
organization_id: string;
name: string;
address: string;
protocol: string;
secret_ref?: string | null;
has_secret?: boolean;
certificate_verification_mode: string;
render_quality_profile?: string;
clipboard_mode: string;
file_transfer_mode: string;
metadata?: Record<string, unknown>;
created_at: string;
updated_at: string;
};
export type VPNConnection = {
id: string;
cluster_id: string;
@@ -527,3 +1382,34 @@ export type VPNConnectionLease = {
fenced_at?: string | null;
metadata: Record<string, unknown>;
};
export type VPNPacketDirectionStats = {
pushed: number;
popped: number;
dropped: number;
queue_depth: number;
last_push_size: number;
last_pop_size: number;
last_push_at?: string;
last_pop_at?: string;
};
export type VPNPacketStats = {
client_to_gateway?: VPNPacketDirectionStats;
gateway_to_client?: VPNPacketDirectionStats;
};
export type VPNClientDiagnosticStatus = {
cluster_id: string;
device_id: string;
payload: Record<string, unknown>;
observed_at: string;
};
export type VPNClientDiagnosticCommand = {
id: string;
cluster_id: string;
device_id: string;
payload: Record<string, unknown>;
created_at: string;
};