- New proto/mcp/v1/master.proto: McpMasterService with Deploy, Undeploy, Status, ListNodes RPCs and all message types per architecture v2 spec. - ServiceSpec gains tier (field 5), node (field 6), snapshot (field 7). - RouteSpec gains public (field 5) for edge routing. - New SnapshotConfig message (method + excludes). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
439 lines
10 KiB
Protocol Buffer
439 lines
10 KiB
Protocol Buffer
syntax = "proto3";
|
|
package mcp.v1;
|
|
|
|
option go_package = "git.wntrmute.dev/mc/mcp/gen/mcp/v1;mcpv1";
|
|
|
|
import "google/protobuf/timestamp.proto";
|
|
|
|
service McpAgentService {
|
|
// Service lifecycle
|
|
rpc Deploy(DeployRequest) returns (DeployResponse);
|
|
rpc UndeployService(UndeployServiceRequest) returns (UndeployServiceResponse);
|
|
rpc StopService(StopServiceRequest) returns (StopServiceResponse);
|
|
rpc StartService(StartServiceRequest) returns (StartServiceResponse);
|
|
rpc RestartService(RestartServiceRequest) returns (RestartServiceResponse);
|
|
|
|
// Desired state
|
|
rpc SyncDesiredState(SyncDesiredStateRequest) returns (SyncDesiredStateResponse);
|
|
|
|
// Status and registry
|
|
rpc ListServices(ListServicesRequest) returns (ListServicesResponse);
|
|
rpc GetServiceStatus(GetServiceStatusRequest) returns (GetServiceStatusResponse);
|
|
rpc LiveCheck(LiveCheckRequest) returns (LiveCheckResponse);
|
|
|
|
// Adopt
|
|
rpc AdoptContainers(AdoptContainersRequest) returns (AdoptContainersResponse);
|
|
|
|
// Purge
|
|
rpc PurgeComponent(PurgeRequest) returns (PurgeResponse);
|
|
|
|
// File transfer
|
|
rpc PushFile(PushFileRequest) returns (PushFileResponse);
|
|
rpc PullFile(PullFileRequest) returns (PullFileResponse);
|
|
|
|
// Node
|
|
rpc NodeStatus(NodeStatusRequest) returns (NodeStatusResponse);
|
|
|
|
// DNS (query MCNS)
|
|
rpc ListDNSRecords(ListDNSRecordsRequest) returns (ListDNSRecordsResponse);
|
|
|
|
// Proxy routes (query mc-proxy)
|
|
rpc ListProxyRoutes(ListProxyRoutesRequest) returns (ListProxyRoutesResponse);
|
|
rpc AddProxyRoute(AddProxyRouteRequest) returns (AddProxyRouteResponse);
|
|
rpc RemoveProxyRoute(RemoveProxyRouteRequest) returns (RemoveProxyRouteResponse);
|
|
|
|
// Edge routing (called by master on edge nodes)
|
|
rpc SetupEdgeRoute(SetupEdgeRouteRequest) returns (SetupEdgeRouteResponse);
|
|
rpc RemoveEdgeRoute(RemoveEdgeRouteRequest) returns (RemoveEdgeRouteResponse);
|
|
rpc ListEdgeRoutes(ListEdgeRoutesRequest) returns (ListEdgeRoutesResponse);
|
|
|
|
// Health (called by master on missed heartbeats)
|
|
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
|
|
|
|
// Logs
|
|
rpc Logs(LogsRequest) returns (stream LogsResponse);
|
|
}
|
|
|
|
// --- Service lifecycle ---
|
|
|
|
message RouteSpec {
|
|
string name = 1; // route name (used for $PORT_<NAME>)
|
|
int32 port = 2; // mc-proxy listener port (e.g. 443, 8443, 9443); NOT the container internal port
|
|
string mode = 3; // "l4" or "l7"
|
|
string hostname = 4; // optional public hostname override
|
|
bool public = 5; // triggers edge routing when true
|
|
}
|
|
|
|
message ComponentSpec {
|
|
string name = 1;
|
|
string image = 2;
|
|
string network = 3;
|
|
string user = 4;
|
|
string restart = 5;
|
|
repeated string ports = 6;
|
|
repeated string volumes = 7;
|
|
repeated string cmd = 8;
|
|
repeated RouteSpec routes = 9;
|
|
repeated string env = 10;
|
|
}
|
|
|
|
message SnapshotConfig {
|
|
string method = 1; // "grpc", "cli", "exec: <cmd>", "full", or "" (default)
|
|
repeated string excludes = 2; // paths relative to /srv/<service>/ to skip
|
|
}
|
|
|
|
message ServiceSpec {
|
|
string name = 1;
|
|
bool active = 2;
|
|
repeated ComponentSpec components = 3;
|
|
string comment = 4;
|
|
string tier = 5; // "core" or "worker" (default: "worker")
|
|
string node = 6; // explicit node pin (overrides tier)
|
|
SnapshotConfig snapshot = 7; // snapshot method and excludes
|
|
}
|
|
|
|
message DeployRequest {
|
|
ServiceSpec service = 1;
|
|
// Deploy a single component by name. Empty means deploy all.
|
|
string component = 2;
|
|
}
|
|
|
|
message DeployResponse {
|
|
repeated ComponentResult results = 1;
|
|
}
|
|
|
|
message ComponentResult {
|
|
string name = 1;
|
|
bool success = 2;
|
|
string error = 3;
|
|
}
|
|
|
|
message StopServiceRequest {
|
|
string name = 1;
|
|
string component = 2;
|
|
}
|
|
|
|
message StopServiceResponse {
|
|
repeated ComponentResult results = 1;
|
|
}
|
|
|
|
message StartServiceRequest {
|
|
string name = 1;
|
|
string component = 2;
|
|
}
|
|
|
|
message StartServiceResponse {
|
|
repeated ComponentResult results = 1;
|
|
}
|
|
|
|
message RestartServiceRequest {
|
|
string name = 1;
|
|
string component = 2;
|
|
}
|
|
|
|
message RestartServiceResponse {
|
|
repeated ComponentResult results = 1;
|
|
}
|
|
|
|
message UndeployServiceRequest {
|
|
string name = 1;
|
|
}
|
|
|
|
message UndeployServiceResponse {
|
|
repeated ComponentResult results = 1;
|
|
}
|
|
|
|
// --- Desired state ---
|
|
|
|
message SyncDesiredStateRequest {
|
|
// All services for this node.
|
|
repeated ServiceSpec services = 1;
|
|
}
|
|
|
|
message SyncDesiredStateResponse {
|
|
repeated ServiceSyncResult results = 1;
|
|
}
|
|
|
|
message ServiceSyncResult {
|
|
string name = 1;
|
|
// Whether the desired state was updated.
|
|
bool changed = 2;
|
|
string summary = 3;
|
|
}
|
|
|
|
// --- Status and registry ---
|
|
|
|
message ListServicesRequest {}
|
|
|
|
message ServiceInfo {
|
|
string name = 1;
|
|
bool active = 2;
|
|
repeated ComponentInfo components = 3;
|
|
string comment = 4;
|
|
}
|
|
|
|
message ComponentInfo {
|
|
string name = 1;
|
|
string image = 2;
|
|
// "running", "stopped", "ignore"
|
|
string desired_state = 3;
|
|
// "running", "stopped", "exited", "removed", "unknown"
|
|
string observed_state = 4;
|
|
// Extracted from the image tag.
|
|
string version = 5;
|
|
google.protobuf.Timestamp started = 6;
|
|
}
|
|
|
|
message ListServicesResponse {
|
|
repeated ServiceInfo services = 1;
|
|
}
|
|
|
|
message GetServiceStatusRequest {
|
|
// Empty means all services.
|
|
string name = 1;
|
|
}
|
|
|
|
message DriftInfo {
|
|
string service = 1;
|
|
string component = 2;
|
|
string desired_state = 3;
|
|
string observed_state = 4;
|
|
}
|
|
|
|
message EventInfo {
|
|
string service = 1;
|
|
string component = 2;
|
|
string prev_state = 3;
|
|
string new_state = 4;
|
|
google.protobuf.Timestamp timestamp = 5;
|
|
}
|
|
|
|
message GetServiceStatusResponse {
|
|
repeated ServiceInfo services = 1;
|
|
repeated DriftInfo drift = 2;
|
|
repeated EventInfo recent_events = 3;
|
|
}
|
|
|
|
message LiveCheckRequest {}
|
|
|
|
message LiveCheckResponse {
|
|
// Services with freshly observed state from the container runtime.
|
|
repeated ServiceInfo services = 1;
|
|
}
|
|
|
|
// --- Adopt ---
|
|
|
|
message AdoptContainersRequest {
|
|
// Service name. The agent matches containers named <service>-*.
|
|
string service = 1;
|
|
}
|
|
|
|
message AdoptResult {
|
|
// Runtime container name (e.g., "metacrypt-api").
|
|
string container = 1;
|
|
// Derived component name (e.g., "api").
|
|
string component = 2;
|
|
bool success = 3;
|
|
string error = 4;
|
|
}
|
|
|
|
message AdoptContainersResponse {
|
|
repeated AdoptResult results = 1;
|
|
}
|
|
|
|
// --- File transfer ---
|
|
// All paths are relative to /srv/<service>/ on the node.
|
|
|
|
message PushFileRequest {
|
|
string service = 1;
|
|
// Relative path within the service directory.
|
|
string path = 2;
|
|
bytes content = 3;
|
|
// File permissions (e.g., 0600).
|
|
uint32 mode = 4;
|
|
}
|
|
|
|
message PushFileResponse {
|
|
bool success = 1;
|
|
string error = 2;
|
|
}
|
|
|
|
message PullFileRequest {
|
|
string service = 1;
|
|
// Relative path within the service directory.
|
|
string path = 2;
|
|
}
|
|
|
|
message PullFileResponse {
|
|
bytes content = 1;
|
|
uint32 mode = 2;
|
|
string error = 3;
|
|
}
|
|
|
|
// --- Node ---
|
|
|
|
message NodeStatusRequest {}
|
|
|
|
message NodeStatusResponse {
|
|
string node_name = 1;
|
|
// "podman" or "docker"
|
|
string runtime = 2;
|
|
string runtime_version = 3;
|
|
uint32 service_count = 4;
|
|
uint32 component_count = 5;
|
|
uint64 disk_total_bytes = 6;
|
|
uint64 disk_free_bytes = 7;
|
|
uint64 memory_total_bytes = 8;
|
|
uint64 memory_free_bytes = 9;
|
|
double cpu_usage_percent = 10;
|
|
google.protobuf.Timestamp uptime_since = 11;
|
|
string agent_version = 12;
|
|
}
|
|
|
|
// --- Purge ---
|
|
|
|
message PurgeRequest {
|
|
// Service name (empty = all services).
|
|
string service = 1;
|
|
// Component name (empty = all eligible in service).
|
|
string component = 2;
|
|
// Preview only, do not modify registry.
|
|
bool dry_run = 3;
|
|
// Currently-defined service/component pairs (e.g., "mcns/mcns").
|
|
// The agent uses this to determine what is "not in any service definition".
|
|
repeated string defined_components = 4;
|
|
}
|
|
|
|
message PurgeResponse {
|
|
repeated PurgeResult results = 1;
|
|
}
|
|
|
|
message PurgeResult {
|
|
string service = 1;
|
|
string component = 2;
|
|
// true if removed (or would be, in dry-run).
|
|
bool purged = 3;
|
|
// Why eligible, or why refused.
|
|
string reason = 4;
|
|
}
|
|
|
|
// --- Logs ---
|
|
|
|
message LogsRequest {
|
|
string service = 1;
|
|
string component = 2; // optional; defaults to first/only component
|
|
int32 tail = 3; // number of lines from the end (0 = all)
|
|
bool follow = 4; // stream new output
|
|
bool timestamps = 5; // prepend timestamps
|
|
string since = 6; // show logs since (e.g., "2h", "2026-03-28T00:00:00Z")
|
|
}
|
|
|
|
message LogsResponse {
|
|
bytes data = 1;
|
|
}
|
|
|
|
// --- DNS ---
|
|
|
|
message ListDNSRecordsRequest {}
|
|
|
|
message DNSZone {
|
|
string name = 1;
|
|
repeated DNSRecord records = 2;
|
|
}
|
|
|
|
message DNSRecord {
|
|
int64 id = 1;
|
|
string name = 2;
|
|
string type = 3;
|
|
string value = 4;
|
|
int32 ttl = 5;
|
|
}
|
|
|
|
message ListDNSRecordsResponse {
|
|
repeated DNSZone zones = 1;
|
|
}
|
|
|
|
// --- Proxy routes ---
|
|
|
|
message ListProxyRoutesRequest {}
|
|
|
|
message ProxyRouteInfo {
|
|
string hostname = 1;
|
|
string backend = 2;
|
|
string mode = 3;
|
|
bool backend_tls = 4;
|
|
}
|
|
|
|
message ProxyListenerInfo {
|
|
string addr = 1;
|
|
int32 route_count = 2;
|
|
int64 active_connections = 3;
|
|
repeated ProxyRouteInfo routes = 4;
|
|
}
|
|
|
|
message ListProxyRoutesResponse {
|
|
string version = 1;
|
|
int64 total_connections = 2;
|
|
google.protobuf.Timestamp started_at = 3;
|
|
repeated ProxyListenerInfo listeners = 4;
|
|
}
|
|
|
|
message AddProxyRouteRequest {
|
|
string listener_addr = 1; // e.g. ":443"
|
|
string hostname = 2;
|
|
string backend = 3;
|
|
string mode = 4; // "l4" or "l7"
|
|
bool backend_tls = 5;
|
|
string tls_cert = 6; // path to TLS cert (required for l7)
|
|
string tls_key = 7; // path to TLS key (required for l7)
|
|
}
|
|
|
|
message AddProxyRouteResponse {}
|
|
|
|
message RemoveProxyRouteRequest {
|
|
string listener_addr = 1; // e.g. ":443"
|
|
string hostname = 2;
|
|
}
|
|
|
|
message RemoveProxyRouteResponse {}
|
|
|
|
// --- Edge routes (v2) ---
|
|
|
|
message SetupEdgeRouteRequest {
|
|
string hostname = 1; // public hostname (e.g. "mcq.metacircular.net")
|
|
string backend_hostname = 2; // internal .svc.mcp hostname
|
|
int32 backend_port = 3; // port on worker's mc-proxy
|
|
bool backend_tls = 4; // MUST be true; agent rejects false
|
|
}
|
|
|
|
message SetupEdgeRouteResponse {}
|
|
|
|
message RemoveEdgeRouteRequest {
|
|
string hostname = 1;
|
|
}
|
|
|
|
message RemoveEdgeRouteResponse {}
|
|
|
|
message ListEdgeRoutesRequest {}
|
|
|
|
message ListEdgeRoutesResponse {
|
|
repeated EdgeRoute routes = 1;
|
|
}
|
|
|
|
message EdgeRoute {
|
|
string hostname = 1;
|
|
string backend_hostname = 2;
|
|
int32 backend_port = 3;
|
|
string cert_serial = 4;
|
|
string cert_expires = 5; // RFC3339
|
|
}
|
|
|
|
// --- Health check (v2) ---
|
|
|
|
message HealthCheckRequest {}
|
|
|
|
message HealthCheckResponse {
|
|
string status = 1; // "healthy" or "degraded"
|
|
int32 containers = 2;
|
|
}
|