Overview
The Talos API uses several common types shared across all services. These types are defined in the common package and provide consistent data structures for metadata, errors, and data transfer.
Every response message includes metadata identifying the responding node and any errors.
message Metadata {
string hostname = 1;
string error = 2;
google.rpc.Status status = 3;
}
Hostname of the node that generated this response. Injected by the proxy when targeting multiple nodes.
Human-readable error message if the request failed. Empty string indicates success.
gRPC status code and details if the request failed. Contains:
code: gRPC status code (int32)
message: Error message
details: Additional error details
Example
{
"metadata": {
"hostname": "worker-1",
"error": "",
"status": null
}
}
With error:
{
"metadata": {
"hostname": "worker-2",
"error": "service kubelet is not running",
"status": {
"code": 5,
"message": "not found"
}
}
}
Error Types
Error
Represents a Talos-specific error with additional context.
message Error {
Code code = 1;
string message = 2;
repeated google.protobuf.Any details = 3;
}
Talos error code (see below)
Human-readable error message
Additional error details (type-specific)
Error Codes
enum Code {
FATAL = 0;
LOCKED = 1;
CANCELED = 2;
}
Fatal error, operation cannot be retried
Resource is locked, retry later
Data Transfer
Data
Used for streaming binary data (files, logs, etc.).
message Data {
Metadata metadata = 1;
bytes bytes = 2;
}
Usage example:
// Read file from node
stream, err := client.Read(ctx, &machine.ReadRequest{
Path: "/etc/os-release",
})
if err != nil {
return err
}
var content []byte
for {
data, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
content = append(content, data.Bytes...)
}
fmt.Println(string(content))
DataResponse
Wraps multiple Data messages in a unary response.
message DataResponse {
repeated Data messages = 1;
}
Empty
Empty response with metadata.
message Empty {
Metadata metadata = 1;
}
EmptyResponse
Wraps multiple Empty messages.
message EmptyResponse {
repeated Empty messages = 1;
}
Container Types
ContainerDriver
Specifies which container runtime to use.
enum ContainerDriver {
CONTAINERD = 0;
CRI = 1;
}
Use containerd directly (system containers)
Use CRI interface (Kubernetes containers)
Usage:
// List Kubernetes containers
client.Containers(ctx, &machine.ContainersRequest{
Namespace: "k8s.io",
Driver: common.ContainerDriver_CRI,
})
// List system containers
client.Containers(ctx, &machine.ContainersRequest{
Namespace: "system",
Driver: common.ContainerDriver_CONTAINERD,
})
ContainerdNamespace
Containerd namespace for container operations.
enum ContainerdNamespace {
NS_UNKNOWN = 0;
NS_SYSTEM = 1;
NS_CRI = 2;
}
Unknown/unspecified namespace
System namespace (Talos services)
CRI namespace (Kubernetes pods)
ContainerdInstance
Specifies a containerd instance.
message ContainerdInstance {
ContainerDriver driver = 1;
ContainerdNamespace namespace = 2;
}
Network Types
NetIP
Represents an IP address.
message NetIP {
bytes ip = 1;
}
IP address in network byte order (4 bytes for IPv4, 16 bytes for IPv6)
Example:
// Create NetIP from string
ip := net.ParseIP("10.0.0.1")
netIP := &common.NetIP{
Ip: ip,
}
// Convert back to string
ipAddr := net.IP(netIP.Ip).String() // "10.0.0.1"
NetIPPort
Represents an IP address and port.
message NetIPPort {
bytes ip = 1;
int32 port = 2;
}
IP address in network byte order
Example:
endpoint := &common.NetIPPort{
Ip: net.ParseIP("10.0.0.1"),
Port: 6443,
}
// Represents: 10.0.0.1:6443
NetIPPrefix
Represents an IP address with CIDR prefix.
message NetIPPrefix {
bytes ip = 1;
int32 prefix_length = 2;
}
Network address in network byte order
CIDR prefix length (0-32 for IPv4, 0-128 for IPv6)
Example:
// Represents 10.0.0.0/24
cidr := &common.NetIPPrefix{
Ip: net.ParseIP("10.0.0.0"),
PrefixLength: 24,
}
// Represents 2001:db8::/32
cidrV6 := &common.NetIPPrefix{
Ip: net.ParseIP("2001:db8::"),
PrefixLength: 32,
}
Cryptographic Types
PEMEncodedCertificateAndKey
PEM-encoded certificate and private key pair.
message PEMEncodedCertificateAndKey {
bytes crt = 1;
bytes key = 2;
}
PEM-encoded X.509 certificate
PEM-encoded private key (RSA, ECDSA, or Ed25519)
Example:
certPair := &common.PEMEncodedCertificateAndKey{
Crt: []byte(`-----BEGIN CERTIFICATE-----
MIIC...
-----END CERTIFICATE-----`),
Key: []byte(`-----BEGIN PRIVATE KEY-----
MIIE...
-----END PRIVATE KEY-----`),
}
PEMEncodedCertificate
PEM-encoded certificate only.
message PEMEncodedCertificate {
bytes crt = 1;
}
PEM-encoded X.509 certificate
PEMEncodedKey
PEM-encoded private key only.
message PEMEncodedKey {
bytes key = 1;
}
URL Type
URL
Represents a URL.
message URL {
string full_path = 1;
}
Complete URL including scheme, host, path, and query parameters
Example:
url := &common.URL{
FullPath: "https://github.com/siderolabs/talos/releases/download/v1.7.0/talos-amd64.tar.gz",
}
Deprecation Annotations
The common package defines custom options for deprecation tracking:
extend google.protobuf.MessageOptions {
string remove_deprecated_message = 93117;
}
extend google.protobuf.FieldOptions {
string remove_deprecated_field = 93117;
}
extend google.protobuf.EnumOptions {
string remove_deprecated_enum = 93117;
}
extend google.protobuf.EnumValueOptions {
string remove_deprecated_enum_value = 93117;
}
extend google.protobuf.MethodOptions {
string remove_deprecated_method = 93117;
}
extend google.protobuf.ServiceOptions {
string remove_deprecated_service = 93117;
}
These annotations indicate which Talos version will remove deprecated API elements.
Example:
rpc ImageList(ImageListRequest) returns (stream ImageListResponse) {
option (common.remove_deprecated_method) = "v1.18";
option deprecated = true;
}
This indicates ImageList will be removed in Talos v1.18.
Response Patterns
Single-Node Responses
When targeting a single node, responses contain one message:
resp, err := client.Version(ctx)
if err != nil {
return err
}
if len(resp.Messages) != 1 {
return fmt.Errorf("unexpected response")
}
msg := resp.Messages[0]
if msg.Metadata.Error != "" {
return fmt.Errorf("node error: %s", msg.Metadata.Error)
}
fmt.Printf("Version: %s\n", msg.Version.Tag)
Multi-Node Responses
When targeting multiple nodes, responses contain multiple messages:
resp, err := client.Version(ctx)
if err != nil {
return err
}
for _, msg := range resp.Messages {
if msg.Metadata.Error != "" {
fmt.Printf("%s: ERROR: %s\n",
msg.Metadata.Hostname,
msg.Metadata.Error,
)
continue
}
fmt.Printf("%s: Talos %s\n",
msg.Metadata.Hostname,
msg.Version.Tag,
)
}
Streaming Responses
Streaming responses send multiple messages over time:
stream, err := client.Logs(ctx, &machine.LogsRequest{
Namespace: "system",
Id: "kubelet",
Follow: true,
})
if err != nil {
return err
}
for {
data, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
if data.Metadata.Error != "" {
return fmt.Errorf("%s: %s",
data.Metadata.Hostname,
data.Metadata.Error,
)
}
fmt.Print(string(data.Bytes))
}
Best Practices
Never assume a response succeeded. Always check metadata:
// Bad
resp, _ := client.Version(ctx)
version := resp.Messages[0].Version.Tag
// Good
resp, err := client.Version(ctx)
if err != nil {
return err
}
for _, msg := range resp.Messages {
if msg.Metadata.Error != "" {
log.Printf("%s failed: %s",
msg.Metadata.Hostname,
msg.Metadata.Error,
)
continue
}
// Use msg.Version...
}
Handle Partial Failures
In multi-node scenarios, some nodes may succeed while others fail:
successful := 0
failed := 0
for _, msg := range resp.Messages {
if msg.Metadata.Error != "" {
failed++
continue
}
successful++
}
if failed > 0 {
log.Printf("Partial failure: %d succeeded, %d failed", successful, failed)
}
Use Type-Safe Helpers
Create helper functions for common conversions:
func NetIPFromString(s string) (*common.NetIP, error) {
ip := net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("invalid IP: %s", s)
}
return &common.NetIP{Ip: ip}, nil
}
func NetIPToString(n *common.NetIP) string {
return net.IP(n.Ip).String()
}
func NetIPPrefixFromString(s string) (*common.NetIPPrefix, error) {
ip, ipNet, err := net.ParseCIDR(s)
if err != nil {
return nil, err
}
prefixLen, _ := ipNet.Mask.Size()
return &common.NetIPPrefix{
Ip: ip,
PrefixLength: int32(prefixLen),
}, nil
}
See Also