Skip to main content
Version: v3.9.0

Remote ControlPlane fields

Reference for remote kind: ControlPlane. Deploy flow is on Remote. Several hosts are on Multi-controller HA.

Deploy
apiVersion: datasance.com/v3 # required, string
kind: ControlPlane # required, string
metadata:
name: remote-cp # required, string
namespace: my-ecn # no, string
spec:
endpoint: https://controller.example.com:51121 # recommended, string. Must match controller.publicUrl when both are set
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # no, string. Base64 PEM. CLI trust
iofogUser:
email: [email protected] # required, string
password: "ChangeMe!12345" # no, string
controller:
publicUrl: https://controller.example.com:51121 # no, string
trustProxy: false # no, bool
consoleUrl: https://controller.example.com:8008 # no, string
consolePort: 8008 # no, int, default 8008
logLevel: info # no, string
pidBaseDir: /home/runner # no, string
package:
image: ghcr.io/datasance/controller:3.9.0 # no, string. Default controller:3.9.0 on the flavor registry
registry: registry.example.com # no, string. Set with username and password
username: pull-user # no, string
password: pull-token # no, string
email: [email protected] # no, string
auth: # required, object. Keep the fields for one mode
mode: embedded # required, embedded or external
insecureAllowHttp: false # no, bool
insecureAllowBootstrapLog: false # no, bool
bootstrap:
username: bootstrap-user # required if embedded, string
password: "ChangeMe!12345" # required if embedded, string
issuerUrl: https://idp.example.com # required if external, string. Remove when mode is embedded
client:
id: cli # required if external, string
secret: client-secret # required if external, string
rateLimit:
enabled: false # no, bool
maxRequestsPerWindow: 100 # no, int
windowMs: 60000 # no, int
sessionStore:
type: memory # no, string
ttlMs: 3600000 # no, int
secret: session-secret # no, string
tokenTtl:
accessTokenTtlSeconds: 3600 # no, int
refreshTokenTtlSeconds: 86400 # no, int
oidcTtl:
interactionTtlSeconds: 600 # no, int
grantTtlSeconds: 600 # no, int
sessionTtlSeconds: 3600 # no, int
idTokenTtlSeconds: 3600 # no, int
database: # required when there are several controllers
provider: postgres # string. postgres or mysql
host: db.example.com # string
port: 5432 # int
user: pot # string
password: db-password # string
databaseName: pot # string
ssl: false # no, bool
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # no, string. Base64 PEM
routerSiteCA: # no, object
tlsCert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string. Base64 PEM
tlsKey: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ # string. Base64 PEM
routerLocalCA:
tlsCert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string
tlsKey: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ # string
natsSiteCA:
tlsCert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string
tlsKey: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ # string
natsLocalCA:
tlsCert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string
tlsKey: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ # string
tls: # no, object. cert and key are set together
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string. Base64 PEM
cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string. Base64 PEM
key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ # string. Base64 PEM
systemMicroservices: # no, object. Keys amd64, arm64, arm, riscv64
router:
amd64: ghcr.io/datasance/router:3.9.0
arm64: ghcr.io/datasance/router:3.9.0
arm: ghcr.io/datasance/router:3.9.0
riscv64: ghcr.io/datasance/router:3.9.0
nats:
amd64: ghcr.io/datasance/nats:2.15.0
arm64: ghcr.io/datasance/nats:2.15.0
arm: ghcr.io/datasance/nats:2.15.0
riscv64: ghcr.io/datasance/nats:2.15.0
nats:
enabled: true # no, bool
events:
auditEnabled: false # no, bool
retentionDays: 30 # no, int
cleanupInterval: 3600 # no, int. Seconds
captureIpAddress: false # no, bool
vault: # no, object. Keep the provider block that matches provider
enabled: false # no, bool
provider: hashicorp # no, string
basePath: secret/data/ecn # no, string. Stored as written
hashicorp:
address: https://vault.example.com # string
token: vault-token # string
mount: secret # string
aws:
region: us-east-1 # string
accessKeyId: <access-key-id> # string
accessKey: <secret-access-key> # string
azure:
url: https://example.vault.azure.net # string
tenantId: <tenant-id> # string
clientId: <client-id> # string
clientSecret: <client-secret> # string
google:
projectId: <project-id> # string
credentials: "<service-account-json>" # string
airgap: false # no, bool
controllers: # required, list. At least one
- name: ctrl-1 # required, string. DNS-like lowercase alphanumeric
host: 10.0.0.10 # required, string
ssh:
user: ubuntu # required, string
keyFile: ~/.ssh/id_rsa # required, string
port: 22 # no, int, default 22
systemAgent: {} # required, object. {} is allowed
tls: # no, object. Overrides global spec.tls for this host
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string
cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t # string
key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ # string
airgap: false # no, bool
- name: ctrl-2 # required, string
host: 10.0.0.11 # required, string
ssh:
user: ubuntu # required, string
keyFile: ~/.ssh/id_rsa # required, string
port: 22 # no, int, default 22
systemAgent: # required, object
config:
arch: amd64 # required when config is set, string. Required for airgap
host: 10.0.0.11 # no, string
deploymentType: native # no, string. native or container
package: {} # no, object
scripts: {} # no, object
airgap: false # no, bool

Not deployed​

These fields come back from describe. Leave them out of the file you pass to deploy.

controllers:
- endpoint: https://10.0.0.10:51121 # filled after deploy. Not part of the first deploy file

At parse time, a document kind RemoteController maps to Controller for add-on deploys only.

metadata.controlPlaneType is rejected. Use kind: ControlPlane.

Global spec fields​

spec.endpoint​

TypeRequiredNotes
string (URL)RecommendedCLI API wait target. Must match controller.publicUrl when both are set.

spec.ca​

Base64 PEM. CLI trust for the Controller API. Not sent to Edgelet. See Securing a remote control plane.

spec.iofogUser​

FieldRequired
emailYes
passwordNo. Complexity rules apply when set. The CLI may prompt.

spec.controller​

Same controller block as local: publicUrl, trustProxy, consoleUrl, consolePort, logLevel, pidBaseDir, and package for the image and a private registry.

The stored control plane must include this block before a Controller add-on is allowed.

FieldNotes
publicUrlPublic URL embedded for Edgelet
trustProxy
consoleUrlEdgeOps Console URL
consolePortDefault 8008
logLevel
pidBaseDir
package.imageController image. Default for this train is controller:3.9.0 on the flavor registry. See Default image pins.
package.registryRegistry hostname for a private pull
package.usernameRequired together with registry and password
package.password
package.emailOptional

When any of registry, username, or password is set, all three are required.

spec.auth (required)​

Global auth for every host. The same block is copied into each translated Edgelet manifest.

FieldTypeRequiredNotes
modeembedded or externalYes
insecureAllowHttpboolNoTrials
insecureAllowBootstrapLogboolNoTrials
bootstrap.usernamestringYes if embedded
bootstrap.passwordstringYes if embeddedRequired in YAML at deploy. At least 12 characters, one uppercase letter, one special character.
issuerUrlstringYes if external
client.idstringYes if external
client.secretstringYes if external
rateLimit.*objectNo
sessionStore.*objectNo
tokenTtl.*objectNo
oidcTtl.*objectNo

Translated to Edgelet spec.auth on each edgelet deploy -f. Environment names are in Edgelet control plane.

ModeCLI on the initial ControlPlane deploy
embeddedAfter all hosts: bootstrap once and create iofogUser if it is missing
externalSkips embedded user bootstrap

The Controller add-on does not run auth or embedded user setup again. It reuses stored spec.auth.

Keycloak-shaped auth.url and realm fields are retired.

BlockFields
rateLimitenabled, maxRequestsPerWindow, windowMs
sessionStoretype, ttlMs, secret
tokenTtlaccessTokenTtlSeconds, refreshTokenTtlSeconds
oidcTtlinteractionTtlSeconds, grantTtlSeconds, sessionTtlSeconds, idTokenTtlSeconds

spec.database​

SituationRule
One controllerOptional. SQLite on Edgelet when omitted.
Several controllersRequired. Full external database (postgres or mysql).

Fields: provider, host, port, user, password, databaseName, optional ssl, optional ca (base64 PEM).

Global CA blocks​

Each block is a site certificate:

FieldFormat
tlsCertbase64 PEM
tlsKeybase64 PEM
YAML keyController secret / CA name
routerSiteCArouter-site-ca
routerLocalCAdefault-router-local-ca
natsSiteCAnats-site-ca
natsLocalCAdefault-nats-local-ca

Uploaded once after deploy through the Controller API when any block is set. See Securing a remote control plane.

spec.tls (global)​

FieldFormat
ca, cert, keybase64 PEM. cert and key are set together.

Default Controller listener TLS for every host, unless a controller row overrides it.

spec.systemMicroservices​

Architecture to image map for router and nats. Keys: amd64, arm64, arm, riscv64. potctl fills missing entries from the build-time router and NATS images (router:3.9.0, nats:2.15.0).

spec.nats​

FieldNotes
enabledDeploy the NATS system microservice when true

spec.events​

auditEnabled, retentionDays, cleanupInterval, captureIpAddress.

spec.vault​

Same vault block as Kubernetes and local. Provider blocks are validated when vault is enabled.

FieldDescription
enabledEnable vault integration
providerhashicorp, openbao, vault, aws, aws-secrets-manager, azure, azure-key-vault, google, or google-secret-manager
basePathBase path inside the vault. On Edgelet this value is stored as written.
hashicorp.address, hashicorp.token, hashicorp.mountHashiCorp, OpenBao, or Vault
aws.region, aws.accessKeyId, aws.accessKeyAWS Secrets Manager
azure.url, azure.tenantId, azure.clientId, azure.clientSecretAzure Key Vault
google.projectId, google.credentialsGoogle Secret Manager

Kubernetes expands $namespace in basePath. Remote Edgelet does not. See KubernetesControlPlane fields.

spec.airgap​

When true, every controller that is air-gapped (global or per host) needs systemAgent.config.arch. See Airgap.

spec.controllers[] (required, at least one)​

FieldRequiredDescription
nameYesDNS-like lowercase alphanumeric. Unique in this control plane.
hostYesSSH target
ssh.userYes
ssh.keyFileYesPath to the private key
ssh.portNoDefault 22
systemAgentYesThe block must exist. {} is allowed.
tlsNoOverrides global spec.tls for this host only
airgapNoPer-host airgap. Inherits the global flag when this is false.
endpointNoFilled after deploy for that host's API URL

controllers[].systemAgent​

FieldNotes
config.archRequired when config is set. Required for airgap.
config.hostAddress registered in the Controller. Defaults from the controller API host.
config.deploymentTypenative or container
package, scriptsEdgelet install options

If systemAgent.config is present, arch is required and must be a valid architecture.

Translation to Edgelet​

  • Edgelet metadata.name comes from the deploy options (CLI namespace and control plane metadata).
  • Global spec.tls, or the per-controller override, becomes Edgelet spec.tls.base64.
  • Global CA blocks do not appear in Edgelet YAML. They are Controller API objects only.

The user file uses the flavor apiVersion. The file Edgelet applies is apiVersion: edgelet.iofog.org/v1, kind: ControlPlane. See Edgelet control plane.

Validation summary​

Rule
At least one controller
Unique controller names
SSH user, key file, and host on every controller
systemAgent present on every controller
endpoint and publicUrl are valid URLs and match when both are set
More than one controller requires database
Private controller.package requires registry, username, and password
ca, CA blocks, and tls are valid base64
Same file: ControlPlane and Controller must not share a name or host

Examples: Remote and Controller add-on.

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