Files
DDI/proto/ddi/agent/v1/agent.proto
T
beug.k@beugsiud.deandClaude Opus 5.5 d5511b3286 feat: M4 contracts for push deployments, diff, rollback and live events
ADR 0022, OpenAPI additions (agent mode, deploy, rollback, diff, pending
changes, server-sent events), deployment_id in GetDesiredStateResponse
for pushes picked up by polling, and the textdiff package (Myers,
unified format).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:53:21 +02:00

289 lines
8.5 KiB
Protocol Buffer

// SPDX-License-Identifier: Apache-2.0
syntax = "proto3";
package ddi.agent.v1;
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
option go_package = "git.beugsiud.de/beug.k/DDI/proto/gen/ddi/agent/v1;agentv1";
// AgentService is implemented by the management server and called by agents.
// Agents always connect outbound (docs/adr/0007-server-agent-grpc-mtls.md).
// Every RPC except Enroll requires a client certificate issued by the
// server's internal CA (docs/adr/0008-interne-ca-enrollment.md).
service AgentService {
// Enroll exchanges a one-time token and a certificate signing request for a
// client certificate. It is the only RPC allowed without client certificate.
rpc Enroll(EnrollRequest) returns (EnrollResponse);
// RenewCertificate issues a new client certificate for a freshly generated
// key. Allowed with a certificate that expired at most 30 days ago.
rpc RenewCertificate(RenewCertificateRequest) returns (RenewCertificateResponse);
// Connect is the long-lived control stream. The agent sends Hello first,
// then heartbeats and command acknowledgements; the server answers with
// Welcome and pushes commands.
rpc Connect(stream ConnectRequest) returns (stream ConnectResponse);
// GetDesiredState returns the revision the agent should run. Used for
// auto_pull and as fallback when the Connect stream is unavailable.
rpc GetDesiredState(GetDesiredStateRequest) returns (GetDesiredStateResponse);
// FetchBundle downloads the signed configuration bundle of a revision
// (docs/adr/0009-signierte-config-bundles.md).
rpc FetchBundle(FetchBundleRequest) returns (FetchBundleResponse);
// ReportDeployment reports the outcome of applying a revision.
rpc ReportDeployment(ReportDeploymentRequest) returns (ReportDeploymentResponse);
// ReportLeases sends a batch of DHCP lease observations (DHCP agents only).
rpc ReportLeases(ReportLeasesRequest) returns (ReportLeasesResponse);
}
// Role of an agent.
enum Role {
ROLE_UNSPECIFIED = 0;
ROLE_DNS = 1;
ROLE_DHCP = 2;
}
// OperatingMode controls when an agent applies new revisions.
enum OperatingMode {
OPERATING_MODE_UNSPECIFIED = 0;
// The agent applies new revisions on its own within the pull interval.
OPERATING_MODE_AUTO_PULL = 1;
// The agent applies revisions only when told to (push from the UI).
OPERATING_MODE_MANUAL = 2;
}
// HealthState summarizes the state of the managed service.
enum HealthState {
HEALTH_STATE_UNSPECIFIED = 0;
HEALTH_STATE_OK = 1;
HEALTH_STATE_DEGRADED = 2;
HEALTH_STATE_FAILED = 3;
}
message EnrollRequest {
// One-time enrollment token in the form "<id>.<secret>".
string token = 1;
// PKCS#10 certificate signing request, DER encoded. The private key never
// leaves the agent.
bytes csr_der = 2;
Role role = 3;
string hostname = 4;
string agent_version = 5;
}
message EnrollResponse {
// Agent identifier assigned by the server (UUID).
string agent_id = 1;
// Issued client certificate, DER encoded.
bytes certificate_der = 2;
// CA chain, DER encoded, root last.
repeated bytes ca_certificates_der = 3;
// Ed25519 public key that verifies configuration bundles.
bytes bundle_signing_public_key = 4;
}
message RenewCertificateRequest {
bytes csr_der = 1;
}
message RenewCertificateResponse {
bytes certificate_der = 1;
repeated bytes ca_certificates_der = 2;
}
message ConnectRequest {
oneof message {
Hello hello = 1;
Heartbeat heartbeat = 2;
CommandAck command_ack = 3;
}
}
message ConnectResponse {
oneof message {
Welcome welcome = 1;
Command command = 2;
}
}
// Hello is the first message an agent sends on a new stream.
message Hello {
string agent_version = 1;
// Revision currently active on the agent (0 = none).
uint64 applied_revision = 2;
// Last revision that passed its health check.
uint64 last_good_revision = 3;
HealthStatus health = 4;
// Version of the managed service, e.g. "BIND 9.20.3" or "Kea 3.2.1".
string service_version = 5;
string hostname = 6;
}
message Heartbeat {
uint64 applied_revision = 1;
HealthStatus health = 2;
uint64 last_good_revision = 3;
}
message HealthStatus {
HealthState state = 1;
string message = 2;
google.protobuf.Timestamp checked_at = 3;
repeated HealthCheck checks = 4;
}
message HealthCheck {
string name = 1;
HealthState state = 2;
string message = 3;
}
// Welcome tells the agent its current settings.
message Welcome {
OperatingMode mode = 1;
google.protobuf.Duration pull_interval = 2;
google.protobuf.Duration heartbeat_interval = 3;
// Revision the agent should run (0 = none). In auto_pull mode the agent
// applies it right away if it is newer than its applied revision.
uint64 desired_revision = 4;
}
// Command is pushed by the server over the Connect stream.
message Command {
// Unique ID, echoed in CommandAck.
string command_id = 1;
oneof action {
ApplyRevisionCommand apply_revision = 10;
RenewCertificateCommand renew_certificate = 11;
}
}
// ApplyRevisionCommand asks the agent to fetch and apply a revision now.
message ApplyRevisionCommand {
uint64 revision = 1;
// Correlates server, agent logs and the deployment record.
string deployment_id = 2;
}
// RenewCertificateCommand asks the agent to renew its certificate now.
message RenewCertificateCommand {}
// CommandAck confirms receipt of a command. The outcome of an apply is
// reported separately via ReportDeployment.
message CommandAck {
string command_id = 1;
bool accepted = 2;
// Reason if not accepted (e.g. "maintenance window closed").
string message = 3;
}
message GetDesiredStateRequest {
uint64 applied_revision = 1;
}
message GetDesiredStateResponse {
// Revision the agent should run. 0 means no configuration assigned yet.
uint64 desired_revision = 1;
OperatingMode mode = 2;
google.protobuf.Duration pull_interval = 3;
// Set while an explicit push of desired_revision is open (not yet
// reported). The agent then applies desired_revision as a push with this
// deployment ID, also in manual mode (docs/adr/0022-ausrollen-pull-push-m4.md).
string deployment_id = 4;
}
message FetchBundleRequest {
uint64 revision = 1;
}
message FetchBundleResponse {
// Manifest bytes exactly as signed (JSON). Verify before parsing.
bytes manifest = 1;
// Ed25519 signature over manifest.
bytes signature = 2;
// Artifact contents; paths, modes and SHA-256 sums are in the manifest.
repeated Artifact artifacts = 3;
}
message Artifact {
string path = 1;
bytes content = 2;
}
// DeploymentTrigger says why an agent applied a revision.
enum DeploymentTrigger {
DEPLOYMENT_TRIGGER_UNSPECIFIED = 0;
DEPLOYMENT_TRIGGER_PULL = 1;
DEPLOYMENT_TRIGGER_PUSH = 2;
}
// DeploymentOutcome is the result of applying a revision.
enum DeploymentOutcome {
DEPLOYMENT_OUTCOME_UNSPECIFIED = 0;
// Validated, activated and healthy.
DEPLOYMENT_OUTCOME_SUCCEEDED = 1;
// Signature or syntax check failed; nothing was changed.
DEPLOYMENT_OUTCOME_REJECTED = 2;
// Activated but unhealthy; the previous revision was restored.
DEPLOYMENT_OUTCOME_ROLLED_BACK = 3;
// Activated, unhealthy and the rollback failed as well.
DEPLOYMENT_OUTCOME_FAILED = 4;
}
message ReportDeploymentRequest {
// Empty for pulls the server did not initiate.
string deployment_id = 1;
uint64 revision = 2;
DeploymentTrigger trigger = 3;
DeploymentOutcome outcome = 4;
// Human-readable details (English), e.g. checker output.
string message = 5;
// Revision active after this deployment.
uint64 active_revision = 6;
google.protobuf.Timestamp started_at = 7;
google.protobuf.Timestamp finished_at = 8;
}
message ReportDeploymentResponse {}
// LeaseState mirrors the Kea lease states relevant for address tracking.
enum LeaseState {
LEASE_STATE_UNSPECIFIED = 0;
LEASE_STATE_ACTIVE = 1;
LEASE_STATE_EXPIRED = 2;
LEASE_STATE_RELEASED = 3;
LEASE_STATE_DECLINED = 4;
}
message Lease {
// IPv4 or IPv6 address in text form.
string ip = 1;
// Hardware address, lowercase colon-separated.
string mac = 2;
string hostname = 3;
LeaseState state = 4;
google.protobuf.Timestamp expires_at = 5;
google.protobuf.Timestamp updated_at = 6;
// Kea subnet ID as rendered by the configuration generator.
uint32 subnet_id = 7;
string client_id = 8;
}
message ReportLeasesRequest {
// Monotonic per agent; lets the server drop duplicate batches.
uint64 sequence = 1;
// True if this batch is a full snapshot rather than a set of changes.
bool full_sync = 2;
repeated Lease leases = 3;
}
message ReportLeasesResponse {
uint64 acknowledged_sequence = 1;
}