Все проверки выполнены успешно
CI / test (push) Successful in 30s
Docker / Build and publish worker image (push) Successful in 10m49s
157 строки
5.5 KiB
Markdown
157 строки
5.5 KiB
Markdown
# 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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"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_id` and the exact `lease_token` from 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.go`
|
|
- `internal/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_id` for private workers;
|
|
- monotonic `config_version`;
|
|
- `issued_at` and `expires_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.
|