Skip to content

Node API reference

Every call of a Janus node’s gRPC API, the role and the domain it needs, and what it does - generated from the .proto files and the roles the node enforces. The messages’ fields are in the .proto files themselves (api/proto/janus/v1alpha1, at the release your nodes run).

  • Role: the least role that may make the call - os:operator may do what os:reader may, os:admin everything (certificates and roles).
  • Domain: what the call is about, for a permission narrowed to some domains (janus-as-domains); observe comes with any.
  • A call the role or the domain doesn’t allow answers PermissionDenied; a call to an extension the image doesn’t have, FailedPrecondition.
  • Every call is janus.v1alpha1.<Service>/<Call> - janus.v1alpha1.HAProxyService/ApplyConfig.

HAProxyService configures and drives the node’s HAProxy - what editing haproxy.cfg over SSH would be elsewhere: its configuration, its runtime state (servers, maps, ACLs, certificates) over its stats socket, its files, and the letsencrypt extension’s certificates.

CallRequest → responseRoleDomainWhat it does
GetConfigEmpty → GetConfigResponseos:readerhaproxyGetConfig returns the applied haproxy.cfg and its SHA-256.
ApplyConfigApplyConfigRequest → stream ApplyConfigResponseos:operatorhaproxyApplyConfig validates the given config via haproxy -c before reloading - a rejected config never reaches the running process. The stages stream: “validating”, then “reloading” and “done” (accepted), or “rejected” with HAProxy’s errors. The configuration is kept across reboots and updates.
ValidateConfigValidateConfigRequest → ValidateConfigResponseos:readerhaproxyValidateConfig runs the same validation as ApplyConfig without touching the running process (dry-run).
ReloadEmpty → ReloadResponseos:operatorhaproxyReload triggers a seamless (zero-downtime) reload of the currently applied config.
StatsEmpty → HAProxyStatsResponseos:readerobserveStats proxies HAProxy’s own stats socket (“show stat”).
ShowInfoEmpty → ShowInfoResponseos:readerobserveShowInfo proxies the stats socket’s “show info”.
BackendListEmpty → BackendListResponseos:readerobserveBackendList lists every backend with each server’s address and state (“up”, “down”, “maint”, “drain”…).
ServerSetStateServerSetStateRequest → Emptyos:operatorhaproxyServerSetState enables/disables/drains a single backend server at runtime (stats socket “set server … state …”).

Runtime maps and ACL lists (stats socket “show/add/del map”, “add/del acl”): only those haproxy.cfg loads from a file. A change is made in the running HAProxy’s memory - the file, and so the next reload, keep the old content (FilePut changes the file).

CallRequest → responseRoleDomainWhat it does
MapListEmpty → MapListResponseos:readerhaproxyMapList lists the maps the running HAProxy loaded.
MapGetMapGetRequest → MapGetResponseos:readerhaproxyMapGet returns a map’s entries.
MapUpdateMapUpdateRequest → Emptyos:operatorhaproxyMapUpdate adds, replaces or deletes one entry of a map.
ACLUpdateACLUpdateRequest → Emptyos:operatorhaproxyACLUpdate adds or deletes one value of an ACL list.
CertificateListEmpty → CertificateListResponseos:readerhaproxyCertificateList lists the certificates the running HAProxy holds, with their expiry.
CertificateUploadCertificateUploadRequest → Emptyos:operatorhaproxyCertificateUpload loads a certificate into the running HAProxy and keeps it on the node (STATE): janusd puts it back - crt-list binding included - into every new HAProxy process, after a reload, a restart or a reboot.
CertificateDeleteCertificateDeleteRequest → Emptyos:operatorhaproxyCertificateDelete removes an uploaded certificate from the running HAProxy and from the node, unbinding it from crt_list first.

HAProxy’s own files (Files, maps and certificates): what haproxy.cfg references besides the letsencrypt extension’s certificates - error pages, maps, ACL lists, Lua, other certificates - as /etc/haproxy/files/<name>, kept on the node. A file is only written or removed if haproxy.cfg still loads with the change; a file holding a private key is never read back.

CallRequest → responseRoleDomainWhat it does
FileListEmpty → FileListResponseos:readerhaproxyFileList lists the files, with their size and SHA-256.
FileGetFileGetRequest → FileGetResponseos:readerhaproxyFileGet returns a file’s content - refused for one holding a private key.
FilePutFilePutRequest → FilePutResponseos:operatorhaproxyFilePut writes a file, and reloads HAProxy with it if asked.
FileDeleteFileDeleteRequest → FileDeleteResponseos:operatorhaproxyFileDelete removes a file - refused while haproxy.cfg needs it.

ACME: the letsencrypt extension (Let's Encrypt). The node obtains and renews its certificates itself, from Let’s Encrypt or any ACME CA, writes each to /etc/haproxy/acme/<name>.pem and swaps a renewed one into the running HAProxy without a reload. Without the extension in the image, ACMEStatus says so and the other calls answer FailedPrecondition.

CallRequest → responseRoleDomainWhat it does
ACMEStatusEmpty → ACMEStatusResponseos:readerhaproxyACMEStatus reports each certificate’s state: obtained, its expiry, its next renewal, the last error.
ACMEGetConfigEmpty → ACMEGetConfigResponseos:readerhaproxyACMEGetConfig returns the configuration - never a secret (a DNS provider’s settings, the EAB key): their values come back empty.
ACMEApplyConfigACMEApplyConfigRequest → ACMEApplyConfigResponseos:operatorhaproxyACMEApplyConfig replaces the configuration; an empty secret keeps the saved one.
ACMERenewACMERenewRequest → ACMERenewResponseos:operatorhaproxyACMERenew obtains the named certificates (every one if none is named) now, whether or not they’re due, in the background: ACMEStatus says how it went.

NetworkService configures the node’s network - always available - and its optional network extensions: BGP (bird), VRRP (keepalived), the firewall (nftables), Consul. An extension is chosen when the image is built (Images and extensions): without it, its binary isn’t in the image at all, and its calls say so (MODULE_STATE_NOT_ENABLED, FailedPrecondition).

BGP: the bird extension (BGP (BIRD)). The protocols named haproxy_* are kept down while this node’s HAProxy doesn’t answer. Without the extension in the image, BGPStatus says so and the other calls answer FailedPrecondition - as for VRRP, the firewall and Consul below.

CallRequest → responseRoleDomainWhat it does
BGPStatusEmpty → BGPStatusResponseos:readerobserveBGPStatus reads every protocol’s state over BIRD’s control socket.
BGPGetConfigEmpty → BGPGetConfigResponseos:readernetworkBGPGetConfig returns the saved bird.conf.
BGPApplyConfigBGPApplyConfigRequest → BGPApplyConfigResponseos:adminnetworkBGPApplyConfig has BIRD check a bird.conf, saves it, and BIRD reconfigures.

VRRP: the keepalived extension (VRRP (keepalived)).

CallRequest → responseRoleDomainWhat it does
VRRPStatusEmpty → VRRPStatusResponseos:readerobserveVRRPStatus reads keepalived’s own state of each instance.
VRRPGetConfigEmpty → VRRPGetConfigResponseos:readernetworkVRRPGetConfig returns the saved keepalived.conf.
VRRPApplyConfigVRRPApplyConfigRequest → VRRPApplyConfigResponseos:adminnetworkVRRPApplyConfig has keepalived check a keepalived.conf, saves it, and keepalived reloads.

Firewall: the nftables extension (Firewall (nftables)). The ruleset is the node’s whole nftables ruleset, in nft’s own syntax.

CallRequest → responseRoleDomainWhat it does
FirewallListEmpty → FirewallListResponseos:readerobserveFirewallList reports the module and the live ruleset.
FirewallGetRulesetEmpty → FirewallGetRulesetResponseos:readernetworkFirewallGetRuleset returns the saved ruleset.
FirewallApplyRulesetFirewallApplyRulesetRequest → FirewallApplyRulesetResponseos:adminnetworkFirewallApplyRuleset validates and applies a ruleset on trial: unless FirewallConfirm comes within the timeout, the previous one is put back. Confirmed, it’s saved and applied at every boot.
FirewallConfirmEmpty → FirewallConfirmResponseos:adminnetworkFirewallConfirm keeps the ruleset on trial. It must come over a connection opened after the apply - established connections are kept whatever the ruleset.
FirewallSetsEmpty → FirewallSetsResponseos:readernetworkFirewallSets lists the live ruleset’s named sets and their elements.
FirewallSetUpdateFirewallSetUpdateRequest → FirewallSetUpdateResponseos:adminnetworkFirewallSetUpdate adds and deletes elements of a named set without reloading the ruleset - elements added without a timeout are kept across applies and reboots.

Consul: the consul extension (Consul). The agent runs with the operator’s own configuration and the files it names (TLS certificates, keys…) under /run/janus/consul/files.

CallRequest → responseRoleDomainWhat it does
ConsulStatusEmpty → ConsulStatusResponseos:readerobserveConsulStatus reports the agent’s service and what the agent says of itself.
ConsulGetConfigEmpty → ConsulGetConfigResponseos:adminnetworkConsulGetConfig returns the configuration and the files’ names, never their content.
ConsulApplyConfigConsulApplyConfigRequest → ConsulApplyConfigResponseos:adminnetworkConsulApplyConfig has consul validate check a configuration, saves it with its files, and restarts the agent.

The node’s own network configuration: hostname, interfaces (physical and 802.1Q VLANs, DHCP or static), DNS and NTP. Unlike the optional modules above, always available.

CallRequest → responseRoleDomainWhat it does
NetworkConfigGetEmpty → NetworkConfigGetResponseos:readernetworkNetworkConfigGet returns the saved network configuration.
NetworkConfigApplyNetworkConfigApplyRequest → stream NetworkConfigApplyResponseos:adminnetworkNetworkConfigApply applies a configuration on trial: the node switches to it at once, but keeps it only if NetworkConfigConfirm arrives within the confirm window - otherwise it reverts to the previous configuration by itself. A configuration that cuts the caller off therefore undoes itself. Only a confirmed configuration is written to persistent storage, so a reboot during the trial also comes back on the previous one. The stream ends once the configuration is applied and awaiting confirmation.
NetworkConfigConfirmEmpty → NetworkConfigConfirmResponseos:adminnetworkNetworkConfigConfirm confirms the configuration on trial. Accepted only over a connection that reaches the node on an address the new configuration keeps - proof that it’s still reachable - and refused over one whose local address the trial removed.
NetworkStatusEmpty → NetworkStatusResponseos:readerobserveNetworkStatus reports what’s actually in effect: links, addresses, DHCP leases, routes, resolvers, hostname and clock synchronization.

SystemService is the reduced, non-Kubernetes equivalent of Talos’s MachineService: the machine’s power, observability, managed-service control, and the handful of read-only file/network RPCs that replace an interactive shell. See The gRPC API for the full design rationale. Every method is implemented but ApplyConfiguration, MetaWrite and MetaDelete, which return codes.Unimplemented.

CallRequest → responseRoleDomainWhat it does
VersionEmpty → VersionResponseos:readerobserveVersion reports what the node runs: Janus’s version, its kernel, the A/B slot it booted from and its image schematic.
HostnameEmpty → HostnameResponseos:readerobserveHostname reports the node’s hostname.
RebootRebootRequest → RebootResponseos:operatorservicesReboot power-cycles the whole machine, after a soft stop of HAProxy: connections in flight get a chance to finish.
ShutdownEmpty → ShutdownResponseos:operatorservicesShutdown powers the machine off, after the same soft stop of HAProxy.
RestartEmpty → RestartResponseos:operatorservicesRestart restarts the janusd control-plane process in place, without rebooting the machine or interrupting HAProxy itself.
ResetResetRequest → ResetResponseos:adminsystemReset wipes the requested partitions (state/ephemeral) and reboots - the equivalent of returning the node to its just-installed state.
ApplyConfigurationApplyConfigurationRequest → stream ApplyConfigurationResponseos:adminsystemApplyConfiguration is not implemented - it answers Unimplemented: a node has no declarative machine configuration, each area has its own calls (HAProxyService, NetworkService…).
EventsEventsRequest → stream Eventos:readerobserveEvents streams the machine’s internal event log (config applied, service state changes, upgrade progress, etc.).
DmesgDmesgRequest → stream Dataos:adminsystemDmesg streams the kernel ring buffer.
LogsLogsRequest → stream Dataos:operatorservicesLogs streams a managed service’s log output.
StatsEmpty → StatsResponseos:readerobserveStats sums the CPU time and memory of each managed service’s processes - janusd, haproxy (a reload briefly leaves an old haproxy process finishing its connections next to the new one).
SystemStatEmpty → SystemStatResponseos:readerobserveSystemStat reports the machine’s counters since boot: boot time, context switches, processes created, CPU ticks (/proc/stat).
MemoryEmpty → MemoryResponseos:readerobserveMemory reports the machine’s total, available and cached memory.
CPUInfoEmpty → CPUInfoResponseos:readerobserveCPUInfo describes the machine’s CPUs: model, frequency, sockets and cores.
LoadAvgEmpty → LoadAvgResponseos:readerobserveLoadAvg reports the load averages over 1, 5 and 15 minutes.
DiskStatsEmpty → DiskStatsResponseos:readerobserveDiskStats reports each disk’s I/O counters.
DiskUsageDiskUsageRequest → stream DiskUsageInfoos:adminsystemDiskUsage reports each requested path’s total size (apparent size of every regular file under it, not crossing into /proc, /sys or /dev); with recursive, also one entry per directory beneath it.
NetworkDeviceStatsEmpty → NetworkDeviceStatsResponseos:readerobserveNetworkDeviceStats reports each network interface’s traffic and error counters.
NetstatEmpty → NetstatResponseos:readerobserveNetstat lists the machine’s TCP and UDP sockets: listening ones and connections.
MountsEmpty → MountsResponseos:readerobserveMounts lists the mounted filesystems with their size and free space.
ProcessesEmpty → ProcessesResponseos:readerobserveProcesses lists the machine’s processes.
ServiceListEmpty → ServiceListResponseos:readerobserveServiceList reports the managed services: janusd itself, haproxy, and the services of the image’s optional extensions.
ServiceStartServiceRequest → ServiceResponseos:operatorservicesServiceStart starts a managed service. NotFound for a service the image doesn’t have; FailedPrecondition for an extension’s service its settings disable.
ServiceStopServiceRequest → ServiceResponseos:operatorservicesServiceStop stops a managed service - haproxy after a soft stop; never janusd (FailedPrecondition: Restart restarts it).
ServiceRestartServiceRequest → ServiceResponseos:operatorservicesServiceRestart restarts a managed service.

List, Read, Copy and PacketCapture are the deliberate, narrow replacements for an interactive shell: read-only, scoped, never arbitrary command execution. None of them serves the node’s secrets.

CallRequest → responseRoleDomainWhat it does
ListListRequest → stream FileInfoos:adminsystemList walks a directory, never into /proc, /sys or /dev.
ReadReadRequest → stream Dataos:adminsystemRead streams a file’s content - never a device’s.
CopyCopyRequest → stream Dataos:adminsystemCopy streams a file or a directory as a tar archive - never from /proc or /sys.
PacketCapturePacketCaptureRequest → stream Dataos:adminsystemPacketCapture streams a live capture as a pcap file (classic libpcap format, microsecond timestamps), split across Data messages - concatenate them to get a file tcpdump/Wireshark read directly. Runs for duration_seconds, or until the client cancels.
MetaWriteMetaWriteRequest → Emptyos:adminsystemMetaWrite is not implemented - it answers Unimplemented: a node has no META partition for small key/value entries.
MetaDeleteMetaDeleteRequest → Emptyos:adminsystemMetaDelete is not implemented - it answers Unimplemented, like MetaWrite.
GenerateClientConfigurationGenerateClientConfigurationRequest → GenerateClientConfigurationResponseos:adminsystemGenerateClientConfiguration issues a client certificate signed by this node’s CA (see internal/pki), for the given roles, named after who it’s for and valid one year or less.

The node’s Prometheus exporter (Metrics): Janus’s own metrics - certificate expiry, boot slot, HAProxy as janusd runs it, extension services, time sync, SELinux - over plain HTTP, on by default on port 10056.

CallRequest → responseRoleDomainWhat it does
MetricsConfigGetEmpty → MetricsConfigResponseos:readerobserveMetricsConfigGet reports the exporter’s settings.
MetricsConfigSetMetricsConfig → MetricsConfigResponseos:adminsystemMetricsConfigSet changes the exporter’s settings, at once and for good; a port that can’t be bound is refused and the exporter stays as it was.

The prometheus-node-exporter extension’s settings (Metrics): whether node_exporter runs, the address and port it listens on, and its collectors, from a fixed list. FailedPrecondition when the image doesn’t have the extension.

CallRequest → responseRoleDomainWhat it does
NodeExporterConfigGetEmpty → NodeExporterConfigResponseos:readerobserveNodeExporterConfigGet reports node_exporter’s settings.
NodeExporterConfigSetNodeExporterConfig → NodeExporterConfigResponseos:adminsystemNodeExporterConfigSet changes node_exporter’s settings: it restarts with them, and they’re kept.

LifecycleService handles installation, upgrade and rollback: writing a new immutable image to the inactive A/B slot, switching the bootloader to it, and rolling back automatically if the new slot doesn’t become healthy within its grace period. See Architecture for the A/B partition layout this is built on.

CallRequest → responseRoleDomainWhat it does
InstallInstallRequest → stream InstallResponseos:adminsystemInstall writes an image to a blank disk for the first time (bare metal / fresh VM) - partitioning it from scratch and writing identical content to both A/B slots, since there’s no “other slot” yet to leave untouched. Streams progress; never reboots anything, since the disk it just wrote isn’t necessarily the one this node itself is running from.
UpgradeUpgradeRequest → stream UpgradeResponseos:adminsystemUpgrade writes a new image to the currently-inactive A/B slot, switches the bootloader default, and reboots. If wait_for_health is true, the next boot has to confirm itself healthy within health_timeout_seconds or the node reverts and reboots back to the slot that was active before this call - autonomously, driven by the node itself, not by this RPC: the stream (and the connection it rides on) ends once the first reboot happens, well before any confirmation or possible revert, so neither is ever visible as a stream message here.
RollbackEmpty → RollbackResponseos:adminsystemRollback switches the bootloader default back to the other A/B slot and reboots, without needing a new image.
UploadReleaseFilestream UploadReleaseFileRequest → UploadReleaseFileResponseos:adminsystemUploadReleaseFile streams one release-bundle file (one of “rootfs.squashfs”, “rootfs.verity”, “uki-a.efi”, “uki-b.efi” - the fixed set image/release/assemble.sh produces) to a local staging area on this node, for network topologies where the node can’t dial out to fetch a bundle itself (see UpgradeRequest.source’s own doc comment for the alternative, node-initiated http(s):// fetch mode). Call once per file the upcoming Upgrade call will need, then call Upgrade itself with source.reference set to the returned staging_dir. Never triggers an upgrade by itself - purely a file transfer.

AccessService: whom a node trusts besides its own certificate authority - its fleet (internal/pki/fleet.go). A node pins its fleet’s root once; afterwards it only takes a bundle that root signed, newer than its own, listing the issuing CAs whose client certificates it accepts. Its own CA - the first-boot admin certificate - always lets in.

CallRequest → responseRoleDomainWhat it does
TrustGetEmpty → TrustStateos:readerobserveThe fleet the node trusts: its root, its bundle. Empty when none.
TrustSetTrustSetRequest → TrustStateos:adminsystemPins root_cert (the first time; afterwards it must be the same, or left empty) and applies bundle - signed by that root, newer than the node’s. The same bundle again changes nothing.
TrustResetEmpty → TrustStateos:adminsystemForgets the fleet: its certificates no longer let anyone in. Only a certificate of the node’s own CA may do it - the way back when a fleet is lost, never for the fleet itself.
LocalCARotateLocalCARotateRequest → LocalCARotateResponseos:adminsystemReplaces the node’s own CA: a new CA, server certificate and admin certificate. Every certificate the old CA issued stops working - the first-boot admin one, a Controller’s service credential, those GenerateClientConfiguration issued; the fleet’s let in as before. With admin_public_key (PKIX, PEM: ECDSA P-256/P-384, Ed25519 or RSA 2048+), the new admin certificate is issued for it, its key never seen by the node; without, the node makes the key and prints both on its console, like at first boot. Clients must verify the node with the new CA (ca_cert) from now on.