5.5 KiB
Control-Plane Protocol
Authority
internal/wire/types.go is the executable schema. New fields must be optional
during rollout, and new branches require decode tests against both old and new
frames. Version strings are informational; explicit capabilities select
protocol features.
The worker connects to:
GET /worker?token=<RSMON_TOKEN>
/api/worker and the HTTP jobs/results APIs remain compatibility paths. New
workers use WebSocket task envelopes.
Frame Model
Every frame is a wire.WorkerMessage with kind and one active content
branch.
| Direction | kind |
Active branch | State |
|---|---|---|---|
| Server to worker | init or config |
init |
Implemented |
| Server to worker | task |
task_envelope |
Implemented |
| Worker to server | result |
result |
Implemented |
| Worker to server | result |
notification_result |
Implemented |
| Worker to server | result |
server_metric |
Implemented |
| Worker to server | heartbeat |
heartbeat |
Implemented |
| Either | error |
error |
Implemented |
| Worker to server | result |
inventory report | Planned |
| Server to worker | cluster config/witness | dedicated typed branch | Planned |
| Server to worker | diagnostic task | new task-envelope variant | Planned |
The legacy top-level task and notification_task branches are accepted for
rollout compatibility. If a frame includes both a valid current
task_envelope and a legacy branch, the worker executes only
task_envelope. The current runner ignores malformed or unsupported task
branches and does not yet compare the envelope job ID with the inner job ID;
strict rejection and reporting are P1 work below.
Initialization And Refresh
wire.WorkerInit supplies runtime values owned by the control plane:
- worker ID, region, advertised URL, capabilities, and concurrency;
- allowed notification methods and account IDs;
- optional linked server ID for host metrics;
- LLM endpoints;
- scoped notification credentials and system contacts;
- peer workers for selfcheck/cluster-adjacent behavior.
The worker clamps supplied concurrency to its local maximum. Credentials are replaced atomically in memory on refresh. Removed credentials must become unavailable immediately after the refresh is applied.
Private-worker hardening will add an immutable worker account ID, config version, expiry, and signature. Until then the executable trusts the authenticated control plane to send a correctly scoped config; server-side selection remains the primary isolation boundary.
Normal Task Envelope
wire.TaskEnvelope is a tagged union:
{
"kind": "task",
"task_envelope": {
"type": "check",
"job_id": "uuid",
"check": {
"job_id": "uuid",
"lease_token": "per-lease-secret",
"check_id": 123,
"monitor_id": 456,
"kind": "http",
"host": "example.com",
"url": "https://example.com",
"interval": 60,
"settings": {}
}
}
}
type=notification activates notification instead. Job IDs in the envelope
and active branch must match once P1 validation lands. Today a task without a
recognized populated branch is ignored without execution.
Result Invariants
- Echo
job_idand the exactlease_tokenfrom the task. - Send one terminal result per execution attempt.
- Never retry a result by executing the task again. Result transport retries resend the same terminal report.
- Treat duplicate terminal acknowledgements as success.
- Do not infer task acceptance from a WebSocket write alone; durable ownership remains on the control plane until it validates the result.
- Bound error strings and provider responses before transmission.
Host Metrics
wire.ServerMetricReport is sent only after WorkerInit.ServerID is present.
The worker collects locally and sends bounded snapshots. The control plane
validates worker/server/account ownership and persists both the latest cache
and VictoriaMetrics points. The worker does not have TSDB credentials.
Protocol Work Packages
P1: Conformance Tests
Files:
internal/wire/types_test.gointernal/distworker/runner_protocol_test.go
Tests:
- decode every current frame branch;
- prefer the current envelope over duplicate legacy branches;
- reject mismatched envelope and inner job IDs;
- preserve unknown optional fields during compatible rollout;
- reject missing lease tokens before execution result submission;
- atomically replace config and credentials.
P2: Safe Token Rotation
Current Runner.RotateToken closes the runner stop channel. Replace this with a
connection-scoped cancellation path that stores the new token, closes only the
active WebSocket, and reconnects without terminating worker services.
Acceptance: rotating from the web console produces a reconnect using the new token while the HTTP listener, collectors, and optional cluster stay running.
P3: Signed Private-Worker Config
Add to the init/config branch:
account_idfor private workers;- monotonic
config_version; issued_atandexpires_at;- signature key ID and Ed25519 signature over canonical payload bytes.
Reject regressions, invalid signatures, expired config, and account changes. Keep the last valid config only until its expiry; do not silently accept an invalid replacement.
P4: New Typed Branches
Inventory, diagnostic, and critical-cluster messages each receive a dedicated
wire type. Do not tunnel them through event or arbitrary json.RawMessage.
Each branch must define payload limits, account/target validation ownership,
idempotency, and compatibility behavior before implementation.