Skip to main content
Version: v3.9.0

Field agent

The Field Agent is the Controller client. It polls the remote Controller over HTTPS (/api/v3/...), persists desired state to SQLite, drives Process Manager updates, posts aggregated status back to the controller, and handles provision/deprovision, OTA, exec/log WebSocket sessions, and service account token rotation.

Code: internal/fieldagent/

Purpose​

  • Maintain connection and trust with the Controller (ping, certificate verification)
  • Poll config/changes and apply microservice, registry, volume mount, model, and RuntimeClass deltas
  • Hydrate agent credentials and Edge Guard signature from SQLite
  • Notify Process Manager when desired microservice set changes
  • POST status/diagnostics on a configurable interval
  • Run release OTA when Controller signals changeVersion
  • Bridge Controller-initiated exec and log streaming to EdgeletAPI WebSocket handlers

Dependencies​

Depends onReason
storeController cache, credentials, Edge Guard JWT
configcontrollerUrl, frequencies, agent UUID, keys
authJWT manager, EdgeletAPI token reconciliation
processmanagerUpdate() after microservice/registry changes
Used byReason
supervisorStarted before Process Manager; passed as microservice manager
edgeletapi / runtimeapiProvision, deprovision, system status, exec/log paths
healthcheckEdgelet-engine healthcheck coordination

Lifecycle​

Start​

Entry: (*FieldAgent).Start() in agent.go.

  1. Create APIClient and Orchestrator (Controller HTTPS)
  2. Hydrate private_key from agent_credentials table; reset JWT manager if missing
  3. If unprovisioned with edgeGuardFrequency > 0, force frequency to 0
  4. If provisioned: load initial microservices, registries, volume mounts, models, and RuntimeClasses from Controller into SQLite; notify Process Manager
  5. Start six background workers (see below)

Stop​

Cancel context; wait for worker goroutines (wg.Wait()).

Config update​

Update() on reload: re-hydrate private key, reset JWT, recreate API client asynchronously, optionally postFogConfig() if last reload succeeded.

Background workers​

WorkerConfig frequencyRole
pingControllerWorkerpingFrequencyController connectivity; updates connection state
runChangesWorkerchangeFrequencyGET config/changes; processes add/update/delete
postStatusWorkerstatusFrequencyAggregated status POST to Controller (PUT status; per-MS errorMessage plus additive last-crash keys)
upgradeScanWorkerupgradeScanFrequencyRelease OTA when changeVersion changes
localAPITokenRotationWorkerinternalEdgeletAPI admin JWT rotation
serviceAccountTokenRotationWorkerinternalProjected SA token lifecycle

Workers skip Controller calls when not provisioned or not connected.

Controller API​

All paths are relative to {controllerUrl}/api/v3/... (Controller-compatible). The APIClient wraps HTTP with agent JWT and TLS settings from config.

Typical flows:

Operator-facing ControlPlane deploy is documented in Local control plane; reconcile still flows through Process Manager after SQLite rows exist.

Configuration​

KeyEffect
controllerUrlController base URL
iofogUuidEmpty when unprovisioned
changeFrequencyChanges poll interval (seconds)
statusFrequencyStatus POST interval
pingFrequencyConnectivity probe interval
upgradeScanFrequencyOTA scan interval
edgeGuardFrequencyForced 0 when unprovisioned
privateKeyIn-memory; persisted in agent_credentials

Provision/deprovision via EdgeletAPI mutates config and clears or sets credentials; see Edgelet API.

Data and persistence​

Field Agent is the primary writer for Controller-sourced rows:

TableContent
controller_microservicesDesired microservices from Controller
controller_registriesRegistry credentials
controller_volume_mountsSecrets/configmaps
controller_modelsFleet model snapshot
controller_runtime_classesFleet RuntimeClass snapshot (name + handler)
agent_credentialsAgent Ed25519 private key (singleton row)
agent_edgeguard_signatureLast attested Edge Guard JWT

Local deploy tables (local_workloads, etc.) are written by EdgeletAPI/runtimeapi, not the changes worker.

External APIs​

SurfaceRole
Controller RESTPoll, provision, status, diagnostics, OTA. Fog PUT status top-level keys are unchanged. Each microserviceStatus item keeps errorMessage through crash + 30s RUNNING grace, then sends errorMessage:"". Additive lastError, lastErrorAt, restartCount may be ignored by older controllers. No new getChanges flags or REST paths.
EdgeletAPI (via runtimeapi)POST/DELETE /v1/system/provision, exec/log WebSocket upgrade
Process ManagerUpdate() channel; implements microservice list for PM

Observability​

  • Log module name: "Field Agent"
  • StatusReporter index: 4 (utils.FieldAgent)
  • Controller connection state in system status (controllerStatus, verification flags)
  • Structured debug codes: FAPC, FACL, FAPS, etc. in internal/utils/constants.go

Failure modes​

SymptomTypical cause
No workloads startNot provisioned; Controller unreachable
Repeated deprovisionEdge Guard hash mismatch (see EdgeGuard)
Deprovision during OTA/restartPre-v1.0.2 first-hit 401; upgrade Edgelet. v1.0.2+ defers until 5×401 over ≥60s (see Troubleshooting)
Status not updatingpostStatusWorker blocked; certificate errors; controller /status 503 (not ready: pair with Controller ≥ v3.8.2)
Exec/log failuresActive session map; WebSocket handler not registered

Code map​

FileRole
agent.goStart/stop, provision hooks, callbacks
workers.goBackground polling loops
changes.goChange list processing
sync.goInitial and incremental sync helpers
api_client.goController HTTP transport
orchestrator.goPing, certificate renewal
exec_*.go, log_*.goWebSocket/exec session bridging
provision_body.goProvision request handling

Related: Process manager, Store, Edgelet API module.

Group 3See anything wrong with the document? Help us improve it!