# Technical Documentation: siderolabs/talos

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/siderolabs/talos)

### Overview (`README.md`)

<p align="center">
  <h1 align="center">Talos Linux</h1>
  <p align="center">A modern OS for Kubernetes.</p>
  <p align="center">
    <a href="https://github.com/talos-systems/talos/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/talos-systems/talos.svg?logo=github&logoColor=white"></a>
    <a href="https://github.com/talos-systems/talos/releases/latest"><img alt="Pre-release" src="https://img.shields.io/github/release-pre/talos-systems/talos.svg?label=pre-release&logo=GitHub&logoColor=white"></a>
    <a href="https://www.bestpractices.dev/projects/7340"><img src="https://www.bestpractices.dev/projects/7340/badge" alt="OpenSSF badge"></a>
  </p>
</p>

---

**Talos** is a modern OS for running Kubernetes: secure, immutable, and minimal.
Talos is fully open source, production-ready, and supported by the people at [Sidero Labs](https://www.SideroLabs.com/).
All system management is done via an API - there is no shell or interactive console.
Benefits include:

- **Security**: Talos reduces your attack surface: It's minimal, hardened, and immutable.
  All API access is secured with mutual TLS (mTLS) authentication.
- **Predictability**: Talos eliminates configuration drift, reduces unknown factors by employing immutable infrastructure ideology, and delivers atomic updates.
- **Evolvability**: Talos simplifies your architecture, increases your agility, and always delivers current stable Kubernetes and Linux versions.

## Documentation

For instructions on deploying and managing Talos, see the [Documentation](https://docs.siderolabs.com/talos).

## Community

- Support: Questions, bugs, feature requests [GitHub Discussions](https://github.com/siderolabs/talos/discussions)
- Slack: Join our [slack channel](https://taloscommunity.slack.com/). Request access via [inviter.co](https://inviter.co/sidero-labs-community).
- Twitter: [@SideroLabs](https://twitter.com/SideroLabs)

If you're interested in this project and would like to help in engineering efforts or have general usage questions, we are happy to have you!
We hold a monthly meeting that all audiences are welcome to attend.

We would appreciate your feedback so that we can make Talos even better!
To do so, you can take our [survey](https://docs.google.com/forms/d/1TUna5YTYGCKot68Y9YN_CLobY6z9JzLVCq1G7DoyNjA/edit).

### Office Hours

- When: Second Monday of every month at 16:30 UTC.
- Where: [Google Meet](https://meet.google.com/ivb-kjfm-jfc).

> Note: You can convert the meeting hours to your [local time](https://everytimezone.com/s/599e61d6).

## Contributing

Contributions are welcomed and appreciated!
See [Contributing](CONTRIBUTING.md) for our guidelines.

## License

<a href="https://github.com/siderolabs/talos/blob/master/LICENSE">
  <img alt="GitHub" src="https://img.shields.io/github/license/siderolabs/talos">
</a>

Some software we distribute is under the General Public License family of licenses or other licenses that require we provide you with the source code.
If you would like a copy of the source code for this software, please contact us via email: info at SideroLabs.com.

---

### Overview (`api/signer/README.md`)

# Signer API

The signer API defines the gRPC service used by the Talos imager to delegate
SecureBoot and PCR signing to an out-of-process signer.

An example YubiKey-based implementation is available at:
https://github.com/jaakkonen/talos-secureboot

---

### Overview (`hack/ctrltrace-viewer/README.md`)

# ctrltrace viewer

A single-page viewer for the controller runtime traces produced by
`internal/pkg/ctrltrace`. It replays a capture as an animated graph: controllers and
resource types as nodes, clustered by namespace, flashing as they reconcile, with
resource nodes growing and shrinking as instances come and go.

## Using it

Open `index.html` in a browser — straight off the filesystem is fine — then drop a
`.jsonl` capture onto the page, or use **Load a trace**. Nothing is uploaded; the file
is parsed in the browser.

To capture one, see [the tracer's README](../../internal/pkg/ctrltrace/README.md):

```sh
make talos WITH_DEBUG=1
# boot with talos.trace=1 talos.trace.specs=1
talosctl -n <node> read /run/talos-trace.jsonl > talos-boot.jsonl
```

The page also tries to fetch `talos-boot.jsonl` from its own directory on load, so
dropping a capture there and serving the directory gives you a viewer that opens
straight onto that boot:

```sh
cp talos-boot.jsonl hack/ctrltrace-viewer/
python3 -m http.server -d hack/ctrltrace-viewer 8000
```

## Themes

The **Theme** control in the footer picks between *Auto* (follows the browser), *Light*,
*Dark* and *Projector*; `t` cycles them, and the choice is remembered per browser.

*Projector* is the one to use on stage: white ground, near-black ink, deep blue and rust
marks instead of the screen palette's lighter steps, thicker strokes and larger type. Lecture
hall projectors crush low contrast, so the greys that read as pleasantly quiet on a laptop
disappear entirely — this theme pushes everything toward black and scales every stroke,
node and label by 1.45x.

## What it shows

* **Squares** are controllers, **circles** are resource types; size follows the number
  of live instances. Position is a force layout clustered by namespace, computed once
  and frozen so the map stays put between replays.
* A node is an empty outline until the trace first touches it, so the boot fills in the
  architecture rather than starting fully lit.
* Selecting a node draws its edges as arrows in the direction data flows — orange out
  of the controller that writes a resource, blue from a resource into the controllers
  that read it. The side panel lists the same thing in text, along with the live
  instances at the current playhead; clicking an instance shows its spec, when the
  capture was taken with `talos.trace.specs=1`.
* The graph is the union of the static dependency graph embedded in the trace (the same
  one `talosctl inspect dependencies` renders) and the traffic actually observed, which
  in practice adds a few hundred edges the controllers never declared.
* The scrub track is state operations per second on a square-root scale — a boot is
  heavy-tailed enough that a linear axis shows one spike and nothing else.

## Notes

* The page loads d3 from cdnjs and its fonts from Google Fonts, so first paint wants a
  network connection. Everything else, including parsing a multi-megabyte trace, is local.
* Large captures are fine: a 6 MB, 29k-event trace parses, lays out and renders in well
  under a second.

---

### Overview (`tools/cloud-image-uploader/README.md`)

# cloud-image-uploader

## Image tags

Published images are tagged with their build type, which is what cleanup tooling keys off
of to decide how long an image should stick around:

- AWS: the tag `BuildType`, on both the AMI and the EBS snapshot backing it.
- GCP: the label `build-type` on the image. GCP label keys must be lowercase, hence the
  different spelling.

The values are the same on both:

| Value | Meaning |
| --- | --- |
| `e2e` | throwaway image built for an e2e run |
| `nightly` | branch build, versioned by `git describe` (e.g. `v1.14.0-alpha.1-101-g11a7fbe4c`) |
| `release` | tagged release, including `alpha`/`beta`/`rc` pre-releases |

The value is derived from `--tag` and `--name-prefix`.

## vmimport role

Role should be pre-created before running this command.

    aws iam create-role --role-name vmimport --assume-role-policy-document file://trust-policy.json
    aws iam put-role-policy --role-name vmimport --policy-name vmimport --policy-document file://role-policy.json

## Google Cloud Pre-requisites

- `GOOGLE_PROJECT_ID` - Google Cloud Project ID
- `GOOGLE_CREDENTIALS_JSON` - Google Cloud Service Account JSON

---

### Overview (`tools/kubeimportlinter/README.md`)

# kubeimportlinter

`kubeimportlinter` checks that versioned Kubernetes imports are aliased to a descriptive name.

Imports whose final path segment is a bare API version (e.g. `v1`, `v1beta1`, `v2`) from a Kubernetes host (`k8s.io`, `sigs.k8s.io`, ...) must be renamed:

```go
import "k8s.io/api/core/v1"            // invalid: package name is bare v1
import v1 "k8s.io/api/core/v1"         // invalid: alias equals the version
import corev1 "k8s.io/api/core/v1"     // valid
```

It runs as a `golangci-lint` module plugin custom linter named `kubeimportlinter`.

## Usage

The repo-root `.custom-gcl.yml` lists this module for the custom binary build:

```bash
make golangci-lint-custom
_out/custom-gcl run --config .golangci.yml
```

Enable it in `.golangci.yml`:

```yaml
linters:
  enable:
    - kubeimportlinter
  settings:
    custom:
      kubeimportlinter:
        type: module
        settings:
          exclude:
            - "**/*_test.go"
            - "vendor/**"
          rules:
            versioned_imports:
              # override the default host list if needed
              hosts:
                - k8s.io
                - sigs.k8s.io
              allow:
                - "path/to/exempt.go"
```

## Config

All rules are enabled by default. An empty settings block applies the rule to
every analyzed Go file under the repository root.

- the repository root is the directory containing the `golangci-lint` config file.
- `versioned_imports.hosts` lists the import path prefixes the rule applies to
  (defaults to `k8s.io`, `sigs.k8s.io`).
- `allow` exempts files from the rule; `exclude` is a broader file skip;
  `include` narrows the rule; `enabled: false` disables it.
- globs are repo-relative, slash-separated, and support `**`.

## Inline exceptions

```go
// kubeimportlint:ignore versioned_imports intentional bare alias
v1 "k8s.io/api/core/v1"
```

---

### Overview (`tools/loglinter/README.md`)

# log-linter

`log-linter` checks Go code for logging policy violations.

It runs as a `golangci-lint` module plugin custom linter named `loglinter`.

Current checks:

- disallowed `log/slog` imports unless allowlisted
- disallowed stdlib `log.*` calls unless allowlisted
- printf-style formatting directives in `*zap.Logger` message strings
- `fmt.Sprintf(...)` used as a zap message
- ad hoc zap root logger constructors not wrapped with `.With(logging.Component(...))` in the same expression

## Usage

### Build a custom golangci-lint binary

A repo-root `.custom-gcl.yml` is included for the custom binary build.

From the repo root:

```bash
make golangci-lint-custom
```

That builds the binary through the Docker-based flow and outputs it to `_out/custom-gcl`.

Run it directly:

```bash
_out/custom-gcl run --config .golangci.yml
```

### Enable the plugin in `.golangci.yml`

Add a custom linter entry like this:

```yaml
version: "2"

linters:
  enable:
    - loglinter
  settings:
    custom:
      loglinter:
        type: module
        description: checks logging conventions
        settings:
          exclude:
            - "**/*_test.go"
            - "_out/**"
            - "vendor/**"
          rules:
            slog_imports:
              allow:
                - "internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_supported.go"
```

The repository root is the directory containing the `golangci-lint` config file.

## Config

All rules are enabled by default. An empty settings block applies every rule to every analyzed Go file under the repository root.

Use config mainly to carve out exceptions.

- `allow` is the recommended way to exempt files from a specific rule.
- `exclude` is a broader file skip for a rule or for the whole run.
- `include` is still supported when you intentionally want to narrow a rule, but the default is repo-wide enforcement.
- `enabled: false` explicitly disables a rule.
- globs are repo-relative, slash-separated, and support `**`.

## Inline exceptions

A single issue can be ignored with a same-line or immediately preceding comment:

```go
// loglint:ignore stdlib_log_calls compatibility adapter
log.Printf("allowed here")
```

Multiple rules can be listed as a comma-separated token:

```go
// loglint:ignore stdlib_log_calls,zap_root_component reason
```

Use `all` to suppress every rule for the next line or same line.

## Notes

- Once `loglinter` is enabled in `.golangci.yml`, use the custom binary (`./custom-gcl`) or `make golangci-lint-custom`; a stock `golangci-lint` binary will not have the plugin compiled in.
- The root-component check is intentionally conservative: it only accepts `.With(logging.Component(...))` when that wrapper appears in the same expression as the configured constructor call.
- Constructor names and component helper calls are configurable.

---

### Overview (`internal/pkg/ctrltrace/README.md`)

# ctrltrace — controller runtime tracing

`ctrltrace` records what the COSI controller runtime actually does while Talos boots:
which controller woke up, what it read, what it wrote, and when every resource appeared,
changed and went away.

It is a development facility, and it is compiled in **only** under `WITH_DEBUG=1` (build tag
`sidero.debug`). A production image gets `stub.go` instead: no tracer, no state wrapping, no
way to turn it on. Even in a debug build nothing is wrapped or allocated until the trace is
explicitly enabled, so it costs nothing on a normal boot.

## Capturing a trace

Build a debug image:

```sh
make talos WITH_DEBUG=1
```

Then boot the node with the trace kernel arguments:

```text
talos.trace=1
```

Optional:

| parameter | default | meaning |
| --- | --- | --- |
| `talos.trace.path` | `/run/talos-trace.jsonl` | output file |
| `talos.trace.specs` | `0` | also record resource specs (YAML) on create/update |
| `talos.trace.duration` | unlimited | stop tracing after e.g. `90s`, keeping the file small |

In a container-based cluster (`talosctl cluster create --provisioner docker`) set the
`TALOS_TRACE=1` environment variable on the container instead.

For a QEMU cluster:

```sh
talosctl cluster create \
  --extra-boot-kernel-args "talos.trace=1 talos.trace.specs=1 talos.trace.duration=3m"
```

Pull the trace off the node once it is up:

```sh
talosctl -n <node> read /run/talos-trace.jsonl > talos-boot.jsonl
```

## Handle the file with care

`talos.trace.specs=1` records the YAML of every resource created or updated, and unlike the
API the tracer does **not** redact the types Talos marks `Sensitivity: sensitive` — a secret
you cannot read is not much use when you are debugging how it came to exist. So a capture with
specs contains root CA private keys, machine tokens and the rest, in the clear. This is why the
tracer is a `WITH_DEBUG=1` facility aimed at throwaway clusters.

The file is written `0600`, but once it is off the node it is just a file. Even without specs
it records resource IDs — node names, cluster membership, affiliate IDs, certificate SANs.
Don't hand a capture around, and don't take one from a cluster you care about.

## What is in the file

Newline-delimited JSON, one record per line, in chronological order.

* `{"k":"meta","op":"start"}` — header: format version, Talos version, wall clock of `t=0`.
* `{"k":"graph","op":"dependencies"}` — the static controller/resource graph, the same one
  `talosctl inspect dependencies` renders. Embedding it makes the trace self-contained.
* `{"k":"ctrl",...}` — controller lifecycle: `start`, `wake` (the controller picked up a
  reconcile event), `queue`, `inputs`, `stop`, `crash`.
* `{"k":"op",...}` — a state operation: `get`, `list`, `create`, `update`, `destroy`,
  `watch`, `watchkind`, with the resource coordinates, owner, version, phase, finalizers,
  duration and error.
* `{"k":"meta","op":"stop"}` — footer: events written and dropped.

`t` is microseconds since the tracer started. Field names are short on purpose — a boot
produces a lot of these.

## How the attribution works

Writes are attributed by the resource owner, but reads are not owned by anybody. To attribute
them, `WrapController` tags the context it hands to each controller's `Run` with the controller
name; that tag rides along into every `Get`/`List`/`Modify` the controller makes and is read
back by the state wrapper. Operations with no tag (the API server, the v1alpha1 sequencer) are
recorded for writes only — reads from those would drown out the controllers.

The runtime restarts a failed controller by calling `Run` again with the *same* context and
runtime values, and controllers may rely on that identity — `internal/pkg/dns.Manager`
panics outright if `ServeBackground` is handed a different context on a restart. So the
wrapper builds the tagged context and the runtime wrapper once per controller and reuses
them; re-tagging per `Run` breaks that contract, and would also leave a second event relay
behind on every restart, competing with the first for the same upstream channel.

Controller *wake-ups* are exact: the wrapper relays the reconcile event channel, and an
unbuffered hand-off completes precisely when the controller enters a new reconcile cycle. Where
that cycle *ends* is not observable from outside the controller, so it is reconstructed offline
from the controller's last state operation before its next wake-up.

Run the package tests with the tag; without it only the stub test runs:

```sh
go test -tags sidero.debug ./internal/pkg/ctrltrace/...
```

## Caveats

* A spec longer than 8 KiB is cut off, and the tail is marked `# ... truncated`.
* Events are dropped rather than blocking a controller when the queue (64k) overflows; the
  footer reports how many.
* Tracing starts before `/run` is mounted, so early events sit in the queue until the file
  can be opened.
* With the tracer on, the state wrapper does not implement the optional `Teardowner` /
  `TeardownAndDestroyer` fast paths, so teardowns decompose into `get` + `update`. This is
  visible in the trace and is the only behavioral difference.

## Viewing a trace

[`hack/ctrltrace-viewer`](../../../hack/ctrltrace-viewer/) is a single-page viewer for
these files: open its `index.html` and drop the capture onto it. It draws the controllers
and resource types as a graph clustered by namespace and replays the capture — controllers
flash as they reconcile, resource nodes grow with their live instance count, read and write
pulses travel the edges, and selecting a node shows what reads and writes it.

---

### Overview (`internal/pkg/efivarfs/README.md`)

# efivarfs

This package was copied from the upstream repository at [efivarfs](https://github.com/monogon/monogon/tree/main/osbase/efivarfs)

There were minimal changes done to comply with the linter and gofmt rules.

The major change was to remove the EFI variable file immutable attribute before writing to it.

---

### Overview (`internal/pkg/msguid/README.md`)

# msguid

This package was copied from the upstream repository at [msguid](https://github.com/monogon/monogon/tree/main/osbase/msguid)

There were minimal changes done to comply with the linter and gofmt rules.

---

### Overview (`internal/app/machined/pkg/controllers/hypervisor/testdata/libvirt/README.md`)

# libvirt domain schemas

Generated by `make generate` from libvirt 12.7.0.
Source archive SHA-256: `7ec1a04e7e4f4069353d4daac117bbe869287f5b202695de61fe1b079efb6cb6` (pinned by siderolabs/extensions/hypervisors/vars.yaml).
The included RNG files form the transitive domain.rng include closure.
COPYING and COPYING.LESSER are the upstream license texts.
Update LIBVIRT_VERSION and LIBVIRT_SHA256 in Makefile together, then run `make generate`.

---

--- METRICS ---
- Files Extracted: 10
- Estimated Token Budget: ~4877 tokens
- Canonical Reference: https://codewiki.google/github.com/siderolabs/talos
