Skip to content

feat: node → controller transport (ADR 0010) - #22

Merged
kfeofantov merged 1 commit into
mainfrom
feat/node-controller-transport
Aug 10, 2026
Merged

kfeofantov merged 1 commit into
mainfrom
feat/node-controller-transport

Conversation

@kfeofantov

Copy link
Copy Markdown
Contributor

What

Slice 3a: the node→controller inventory channel wired end to end at the transport layer. The per-workload join and the go_inventory payload land in the next slice (3b).

Node role

  • New -controller-endpoint / -token-path flags. With an endpoint set, each scan pass POSTs a nodescan.Report (node, kept binaries, counters) to the controller, authenticated with a projected controller-audience ServiceAccount token read fresh per send (rotation-safe). No endpoint → log-only, unchanged (existing node e2e and unit test stay green).
  • Best-effort delivery: failures are logged, next pass retries; the controller rebuilds inventory from re-scans, so a lost report costs nothing (loss-harmless).

Controller

  • internal/nodeintake: HTTP receiver that verifies the bearer token locally via nodeauth (cluster JWKS, no TokenReview), decodes the report (size-capped, unknown-fields + trailing-data rejected), hands it to a callback. Response is ack/error only — one-way (ADR 0001) one level down. This slice's callback logs aggregates.
  • config.NodeIntake gates the receiver (off by default → controller-only installs open no port); the JWKS client reuses the API server's in-cluster REST transport.

Manifest

Node DaemonSet gains a projected serviceAccountToken volume (audience-bound, tmpfs, rotated), a NODE_NAME downward-API env, and the -controller-endpoint arg. automountServiceAccountToken stays false — the default API token is still never mounted, and the controller-audience token can't reach the API server. Validated by decoding with real k8s types.

security.md

§2 / §4 / §5 / §7 / §9 updated: the controller receiver, local token validation (no new RBAC verb), the projected token that is not an API credential, and the honest cleartext-HTTP tradeoff (bounded, one-way, no-money — ADR 0008 blast-radius reasoning).

Tests

Receiver: accept, missing/bad token, malformed, unknown fields, trailing data, oversize, wrong method. Shipper: bearer from token file, per-send rotation, non-2xx and missing-token errors.

Note on e2e

Touches cluster-facing code (node manifest). I'll run make node-e2e locally against kind before merge to confirm the manifest change doesn't break DaemonSet startup — pending Docker being up.

🤖 Generated with Claude Code

…er receives (ADR 0010)

Wires the node→controller inventory channel end to end at the transport layer;
the per-workload join and payload land in the next slice.

Node role:
- New -controller-endpoint and -token-path flags. When an endpoint is set, each
  scan pass POSTs a nodescan.Report (node name, kept binaries, counters) to the
  controller, authenticated with a projected controller-audience ServiceAccount
  token read fresh per send (rotation). No endpoint → log-only, unchanged.
- Delivery is best-effort: a failure is logged and the next pass retries; the
  controller rebuilds inventory from re-scans, so a lost report costs nothing.

Controller:
- internal/nodeintake: an HTTP receiver that verifies the bearer token locally
  via nodeauth (cluster JWKS, no TokenReview), decodes the report (size-capped,
  unknown fields and trailing data rejected), and hands it to a callback. The
  response is ack/error only — never configuration or commands (one-way, ADR
  0001, one level down). For this slice the callback logs aggregates.
- config.NodeIntake gates the receiver (off by default, so a controller-only
  install opens no port); the JWKS client reuses the API server's in-cluster
  REST transport.

Manifest: the node DaemonSet gains a projected serviceAccountToken volume
(audience-bound to the controller, tmpfs, rotated), a NODE_NAME downward-API
env, and the -controller-endpoint arg. automountServiceAccountToken stays false:
the default API token is still never mounted, and the controller-audience token
cannot reach the API server.

security.md: §2/§4/§5/§7/§9 updated for the new in-cluster flow — the
controller's receiver, local token validation (no new RBAC verb), the projected
controller-audience token that is not an API credential, and the honest
cleartext-HTTP tradeoff.

Unit tests: receiver auth/decoding (accept, missing/bad token, malformed,
unknown fields, trailing data, oversize, wrong method); node shipper (bearer
from token file, per-send rotation, non-2xx and missing-token errors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kfeofantov
kfeofantov merged commit 15a04b2 into main Aug 10, 2026
3 checks passed
@kfeofantov
kfeofantov deleted the feat/node-controller-transport branch August 10, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant