MCPcopy Index your code
hub / github.com/enix/topomatik

github.com/enix/topomatik @v1.3.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.3.1 ↗ · + Follow
123 symbols 377 edges 18 files 4 documented · 3%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Topomatik

<a href="https://opensource.org/licenses/MIT" alt="MIT License">
    <img alt="License MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg" /></a>
<a href="https://enix.io/fr/blog/" alt="Brought to you by ENIX">
    <img alt="Brought to you by ENIX" src="https://img.shields.io/badge/Brought%20to%20you%20by-ENIX-%23377dff?labelColor=888&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAQAAAC1QeVaAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfkBAkQIg/iouK/AAABZ0lEQVQY0yXBPU8TYQDA8f/zcu1RSDltKliD0BKNECYZmpjgIAOLiYtubn4EJxI/AImzg3E1+AGcYDIMJA7lxQQQQRAiSSFG2l457+655x4Gfz8B45zwipWJ8rPCQ0g3+p9Pj+AlHxHjnLHAbvPW2+GmLoBN+9/+vNlfGeU2Auokd8Y+VeYk/zk6O2fP9fcO8hGpN/TUbxpiUhJiEorTgy+6hUlU5N1flK+9oIJHiKNCkb5wMyOFw3V9o+zN69o0Exg6ePh4/GKr6s0H72Tc67YsdXbZ5gENNjmigaXbMj0tzEWrZNtqigva5NxjhFP6Wfw1N1pjqpFaZQ7FAY6An6zxTzHs0BGqY/NQSnxSBD6WkDRTf3O0wG2Ztl/7jaQEnGNxZMdy2yET/B2xfGlDagQE1OgRRvL93UOHqhLnesPKqJ4NxLLn2unJgVka/HBpbiIARlHFq1n/cWlMZMne1ZfyD5M/Aa4BiyGSwP4Jl3UAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjAtMDQtMDlUMTQ6MzQ6MTUrMDI6MDDBq8/nAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIwLTA0LTA5VDE0OjM0OjE1KzAyOjAwsPZ3WwAAAABJRU5ErkJggg==" /></a>







<img alt="Topomatik logo" src="https://raw.githubusercontent.com/enix/topomatik/refs/heads/main/artworks/topomatik.png" />

Topomatik automatically reflects your underlying infrastructure in Kubernetes node topology labels, because manually updating topology is about as fun as untangling holiday lights 🎄

Learn more about topology in Kubernetes:

  • [https://kubernetes.io/docs/reference/labels-annotations-taints/#topologykubernetesiozone]
  • [https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/]

🎤 Watch our talk at CND France 2026

We were at the Cloud Native Days France 2026 to present topomatik. The video and slides are in French, but the video also has English subtitles.

<a href="https://www.youtube.com/watch?v=CBrj0YF6KzE">
    <img alt="Topomatik talk at CND France 2026" src="https://img.youtube.com/vi/TCLYktup5TU/maxresdefault.jpg" width="640" />
</a>







<a href="https://www.youtube.com/watch?v=CBrj0YF6KzE">▶️ Video (French with English subtitles)</a> · <a href="https://drive.google.com/file/d/1avsxPE0UAZ0y49dM4Hs4eP0oafd3C2lw/view">📑 Slides (French)</a>

✨ Features

  • Automagically updates the topology.kubernetes.io/zone and topology.kubernetes.io/region node labels based on autodiscovered infrastructure
  • Multiple auto-discovery engines (LLDP, DMI, local file, hostname, etc.).
  • Works with both virtualised and bare-metal nodes
  • Runs as a DaemonSet; updates topology even when nodes are live-migrated

📦 Installation

TL;DR

helm install my-release oci://quay.io/enix/charts/topomatik

Flux usage

When using Flux, you can use this HelmRepository object as repository for all ENIX projects:

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: enix
  namespace: default
spec:
  interval: 24h
  type: oci
  url: oci://quay.io/enix/charts

Then, you can create a HelmRelease object pointing to this repository:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: topomatik
  namespace: topomatik
spec:
  interval: 1m0s
  chart:
    spec:
      chart: topomatik
      sourceRef:
        kind: HelmRepository
        name: enix
        namespace: default
      version: 1.*

Roadmap

  • [x] Refactor discovery engine reconciliation loop #1
  • [x] DMI/BIOS discovery engine #2
  • [x] Local file / HTTP discovery engine #3
  • [x] Hostname / node name discovery engine #4
  • [x] Network config discovery engine #5
  • [x] Taint management #6
  • [ ] Prometheus Exporter #7
  • [ ] Better RBAC / admission webhook #8

Configuration

Topomatik is configured using a YAML file. Here's an example configuration:

minimumReconciliationInterval: 1s

labelTemplates:
  topology.kubernetes.io/zone: "{{ .files.zone }}-{{ .lldp.hostname }}"
  topology.kubernetes.io/region: "{{ .files.region }}-{{ .lldp.description | regexp.Find "location: [^ ]" }}

lldp:
  enabled: true
  interface: auto

files:
  zone:
    path: /etc/zone
  region:
    path: /etc/region
    interval: 5s

Label Templates

The labelsTemplates section defines which Kubernetes labels Topomatik will manage. Each value is a Go template that will be rendered to determine the label value.

The Sprig library is available for advanced template operations, giving you access to string manipulation, regular expressions, and more.

[!NOTE] Labels must be an empty string or consist of alphanumeric characters, "-", "_" or ".", and must start and end with an alphanumeric character. Invalid characters will be replaced by "_" and leading or trailing non alpha-numeric characters will be removed.

Example: "@@@foo+bar.foobar----." will be rendered as "foo_bar.foobar".

Taint Templates

The taintTemplates section defines which Kubernetes taints Topomatik will manage. The map is keyed by the taint key; each entry has a value and an effect, both Go templates (same engine and Sprig functions as label templates).

taintTemplates:
  topology.plaffitt.kubernetes.io/specialized:
    value: "{{ .hostname.zone }}"
    effect: NoSchedule

Allowed rendered effects: NoSchedule, PreferNoSchedule, NoExecute. An effect that renders to an unsupported value preserves the existing taint on the node and logs a warning.

Rendered values are sanitized using the same rules as label values (see the note above).

If effect renders to an empty string, the taint is not applied; if it already exists on the node it will be removed. This lets you conditionally manage a taint, e.g.:

taintTemplates:
  topology.plaffitt.kubernetes.io/specialized:
    value: "{{ .hostname.zone }}"
    effect: '{{ if eq .hostname.zone "eu-west" }}NoSchedule{{ end }}'

[!NOTE] Conditional management of labels is driven by an empty rendered value, but for taints it is driven by an empty rendered effect. A taint with an empty value is still a valid Kubernetes taint, whereas an empty effect is the natural signal that the taint should not exist.

[!NOTE] Topomatik only manages taints whose key appears in taintTemplates. Taints set by other controllers or by the user (e.g. kubectl taint) on different keys are preserved on every reconciliation. Removing an entry from taintTemplates does not remove the corresponding taint from the node; remove it manually with kubectl taint nodes <name> <key>- or render the effect to an empty string.

Auto-Discovery Engine Configuration

Each auto-discovery engine has its own configuration section.

All engines share a common enabled key that allows you to enable or disable the engine. The remaining configuration keys are specific to each engine.

Below you'll find detailed information about each supported engine

LLDP

LLDP (Link Layer Discovery Protocol) is a vendor-neutral Layer 2 protocol that enables network devices to automatically discover and share information about their identity, capabilities, and neighboring device on a local network link (physical cable or veth pair).

It can be used in both bare-metal and virtualised environments to inform nodes about the underlying topology (eg: Proxmox PVE). In bare-metal environments, it must be enabled at the network device level (e.g., switches). In virtualised environments, you'll need to install the lldpd service on your hypervisors.

LLDP configuration
Name Description Default value
enabled Enable or disable this auto discovery engine true
interface Interface name to use for listen to LLDP frames or auto to use any interface with default gateway auto
Available template variables from the LLDP engine
Name Description
lldp.hostname The SysName TLV
lldp.description The SysDescription
Proxmox PVE setup example

Topomatik can be used with Proxmox PVE using the lldpd. Just install it using apt install lldpd. The default configuration should be sufficient to advertise the TLV handled by Topomatik.

Files

Reads the contents of a file. It supports reading files over http(s). It supports either watching or polling files. However, watch is supported for local files only using inotify.

Files configuration
Name Description Default value
path Path or url of the file to watch <required>
interval Use polling instead of watching (using inotify) <required_for_remote_files>

Example:

files:
  # watching a local file
  zone:
    path: /etc/zone
  # polling a local file
  region:
    path: /etc/region
    interval: 5s
  # polling a remote file
  rack:
    path: http://localhost:8080/rackname
    interval: 1m # this is required for remote files
Available template variables from the files engine

Template variables are defined by the name of each file entry in the configuration. For instance, with the above configuration, this engine exposes .files.zone, .files.region and .files.rack.

Hardware

This engine allows to read information about the hardware (equivalent to dmidecode on Linux, but it also supports MacOS and Windows).

Hardware configuration
Name Description Default value
enabled Enable or disable this auto discovery engine false
interval Polling interval of the hardware information <required>
Available template variables from the hardware engine
Name Description
hardware.chassis_serial The chassis serial number
hardware.chassis_asset_tag The chassis asset tag

Hostname

This engine allows to read the name of the host and parse it using regexps.

Example parsing the hostname EU-R1-42

hostname:
  enabled: true
  pattern: "(?P<zone>[A-Z]{2})-(?P<region>R[0-9])-(?P<node>[0-9]+)$"
Hostname configuration
Name Description Default value
enabled Enable or disable this auto discovery engine false
interval Polling interval of the hostname information <required>
pattern Regexp with named groups -
Available template variables from the hostname engine

Template variables are defined by the name of an file entry in the configuration. For instance, with the above configuration, this engine exposes .hostname.zone, .hostname.region and .hostname.node, but since the pattern in the configuration is optional, it also exposes the whole hostname as .hostname.value.

Network

Reads network information of the device in order to group machines by subnet. IPv4-only.

Network configuration
Name Description Default value
enabled Enable or disable this auto discovery engine false
interface Interface name to get the subnet from, or auto (or empty) to use the default route auto
Available template variables from the network engine

The only variable available here is network.subnet, which is in CIDR form, minus the fact that / is replace by _ since Kubernetes labels doesn't support / character (eg. 192.168.1.0_24).

Name Description
network.subnet The subnet associated with the defined interface

🤝 Contributing

Found a bug? Want to add support for another discovery engine? We welcome contributions! Just be sure your code is as clean as your commit messages.

📜 License

Topomatik is open-source software licensed under MIT. Use it, modify it, love it!

Extension points exported contracts — how you extend this code

DiscoveryStrategy (Interface)
(no doc) [6 implementers]
internal/autodiscovery/autodiscovery.go
ReconciliationScheduler (Interface)
(no doc) [3 implementers]
internal/controller/controller.go

Core symbols most depended-on inside this repo

New
called by 8
internal/controller/controller.go
Do
called by 8
internal/schedulers/sometimesDebounce.go
Load
called by 8
internal/config/config.go
Setup
called by 6
internal/autodiscovery/files/files.go
Register
called by 6
internal/controller/controller.go
reconcileNode
called by 6
internal/controller/controller.go
String
called by 5
internal/logging/logging.go
NewSometimesWithDebounce
called by 5
internal/schedulers/sometimesDebounce.go

Shape

Function 59
Method 36
Struct 24
Interface 2
TypeAlias 2

Languages

Go100%

Modules by API surface

internal/controller/controller.go17 symbols
internal/controller/start_test.go10 symbols
internal/controller/reconcile_test.go10 symbols
internal/logging/logging.go9 symbols
internal/autodiscovery/files/files_test.go9 symbols
internal/config/config_test.go8 symbols
internal/autodiscovery/autodiscovery.go8 symbols
internal/schedulers/sometimesDebounce.go7 symbols
internal/autodiscovery/network/network.go7 symbols
internal/autodiscovery/files/files.go7 symbols
internal/schedulers/sometimesDebounce_test.go6 symbols
internal/controller/controller_test.go5 symbols

For agents

$ claude mcp add topomatik \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page