mljr.eu
Open to opportunities

Hi, I'm Michael.

I build

Master's student (Dipl.-Ing.) in Networks & IT Security at JKU Linz, writing my thesis on permission metamodels at Dynatrace. I care about correctness, performance, and shipping things that actually work.

Michael Reinegger
Michael Reinegger
Linz, Austria
Current focus
Dynatrace thesis
Permission metamodel · Prolog
Near
Linz, Austria
Open to opportunities
MSc / Dipl.-Ing.
Networks & IT Security
JKU Linz · 2026
Homelab
VPS edge + home servers
Caddy · CrowdSec · Tailscale
22
Projects
11+
Years coding
8+
Yrs coding
22
Projects
7
Companies
3
Degrees
Go
Rust
TypeScript
Python
Docker
Linux
Ansible
Tailscale
Grafana
Svelte
Kotlin
Security
Networking
Homelab
IAM / PAM
Embedded
PostgreSQL
SQLite
02

Projects

12 projects
LSTM-Based-Speaker-Diarization 1
LSTM-Based-Speaker-Diarization 2
LSTM-Based-Speaker-Diarization 3
LSTM-Based-Speaker-Diarization 4

LSTM Based Speaker Diarization

Bachelor thesis research implementing LSTM and BiLSTM networks for speaker diarization — the task of labeling who speaks when in an audio recording. Compares d-vector generation architectures and four…

PythonPyTorchMachine LearningAudio
cg-lab-2023-project-reineggersilber 1
cg-lab-2023-project-reineggersilber 2
cg-lab-2023-project-reineggersilber 3

Cg Lab 2023 Project Reineggersilber

A university computer graphics lab project implementing real-time animated 3D scenes directly against raw WebGL (no engine, no three.js) using hand-rolled GLSL shaders. A person wanders a furnished ro…

JavaScriptWebGLGLSLComputer Graphics
face-auth 1
face-auth 2
face-auth 3
face-auth 4

Face Auth

A Linux PAM module for facial recognition login using IR cameras and Windows Hello–compatible hardware. Features liveness anti-spoofing, an egui graphical configuration tool, and plugs into any PAM st…

RustPAMSecurityLinux
jku-master-project-network-devices-power-consumption-analysis 1
jku-master-project-network-devices-power-consumption-analysis 2
jku-master-project-network-devices-power-consumption-analysis 3
jku-master-project-network-devices-power-consumption-analysis 4

Jku Master Project Network Devices Power Consumption Analysis

★ 1

A master's project measuring the power consumption of network devices under different traffic loads. A FritzBox smart plug captures energy data while Go tooling generates UDP, TCP, and Layer-2 loads; …

TeXGoNetworkingData Analysis
Game-of-Life-Multiplatform 1
Game-of-Life-Multiplatform 2

Game Of Life Multiplatform

★ 1

Conway's Game of Life implemented as a Kotlin Multiplatform project for desktop and Android. Developed as a university programming project exploring KMP's shared logic and platform-specific UI capabil…

KotlinMultiplatformSimulation
Lebenslauf 1
Lebenslauf 2
Lebenslauf 3

Lebenslauf

Source files for my CV and cover letter written in Typst — a modern typesetting language. Clean, version-controlled, and trivially re-compilable to PDF.

TypstResumePDF
webcambot 1
webcambot 2
webcambot 3
webcambot 4

Webcambot

A Discord bot that fetches snapshots and video clips from Home Assistant camera entities on command. Configurable clip lengths and instant image delivery make it useful for home monitoring over Discor…

PythonDiscordHome Assistant
Flexbar-Plugin-Youtube-Music-Desktop-App

Flexbar Plugin Youtube Music Desktop App

★ 1

A Node.js plugin for the Flexbar macro pad that controls YouTube Music Desktop App via Socket.IO. Shows the current track, album art, and playback controls directly on the Flexbar display.

JavaScriptTypeScriptPluginDesktop
WordWiz 1
WordWiz 2
WordWiz 3

WordWiz

A SvelteKit word finder and unscrambler: type a bag of letters and it returns every word that can be made from them, in English or German, filtered to a chosen length range. Backed by 450k+ and 670k+ …

JavaScriptTypeScriptSvelteKitWeb
nightscout-display 1
nightscout-display 2

Nightscout Display

A Stream Deck plugin that shows live Nightscout CGM glucose readings on a Stream Deck key. Supports numeric display and a scrolling sparkline graph, with configurable time ranges and color-coded alert…

TypeScriptGoNightscoutStream Deck
AdventOfCode2022_MrCode

AdventOfCode2022 MrCode

Advent of Code 2022 solutions in Kotlin, exploring algorithmic challenges across the December puzzle calendar.

KotlinAlgorithmsPuzzles
AOC2024

AOC2024

Advent of Code 2024 solutions written in Kotlin, working through all December puzzles with clean, readable implementations.

KotlinAlgorithmsPuzzles
03

Live Tools

3 live tool

Regex Lab

LIVE

An interactive regex workbench built in Go + Datastar. Uses regexp2 (PCRE-compatible engine) for backreferences, lookahead, and lookbehind. Matches highlight as you type via server-sent events, with a built-in cheat sheet and 20+ preset examples.

GoRegexPCREDatastarSSE

Cron Explorer

LIVE

An interactive cron expression explorer built in Go + Datastar. Supports standard 5-field and 6-field (with seconds) syntax, @aliases, and common presets. Shows a field-by-field breakdown and the next 10 execution times with countdown.

GoCronDatastarSSE

Codec

LIVE

A multi-mode encode/decode tool built in Go + Datastar. Hash mode computes all four digests simultaneously. Encoding modes support Base64, URL percent-encoding, and HTML entity encoding — all processed server-side via SSE.

GoCryptoBase64DatastarSSE
04

Playground

old side projects, rebuilt

AI Snake

Snake plays itself by following a fixed loop through every cell on the board — a mathematical guarantee it can never trap itself.

Snake

The classic, keyboard-controlled. Eat apples, grow longer, don't hit the wall or yourself.

Minenfeld

Cross a minefield one row at a time — one wrong step and it's over.

Double Pendulum

A chaotic n-segment pendulum — tune the physics and watch tiny changes explode into wildly different motion.

Fourier Drawing

Draw any shape and watch a stack of rotating circles reconstruct it from pure sine waves.

Boids

A flock that organizes itself from three simple local rules — no leader, no plan, just steering.

Particle Life

Colored particles that attract and repel each other by simple pairwise rules — and organize into swarming, chasing clusters on their own.

Maze

Watch a maze carve itself out cell by cell, then watch a breadth-first search solve it.

Sorting Visualizer

Race classic sorting algorithms as animated bars and watch how differently each one earns its comparisons and swaps.

05

Open Source

live data
Contributions

A year of commits

MonWedFriOctNovDec10 contributions on Dec 1011 contributions on Dec 123 contributions on Dec 161 contributions on Dec 174 contributions on Dec 187 contributions on Dec 1911 contributions on Dec 209 contributions on Dec 2113 contributions on Dec 2210 contributions on Dec 2311 contributions on Dec 275 contributions on Dec 2813 contributions on Dec 2911 contributions on Dec 307 contributions on Dec 31Jan3 contributions on Jan 13 contributions on Jan 21 contributions on Jan 33 contributions on Jan 45 contributions on Jan 59 contributions on Jan 1120 contributions on Jan 125 contributions on Jan 1349 contributions on Jan 147 contributions on Jan 1515 contributions on Jan 1613 contributions on Jan 1743 contributions on Jan 1819 contributions on Jan 205 contributions on Jan 2121 contributions on Jan 2223 contributions on Jan 2314 contributions on Jan 2416 contributions on Jan 253 contributions on Jan 261 contributions on Jan 271 contributions on Jan 284 contributions on Jan 295 contributions on Jan 30Feb12 contributions on Feb 11 contributions on Feb 21 contributions on Feb 814 contributions on Feb 932 contributions on Feb 1022 contributions on Feb 112 contributions on Feb 142 contributions on Feb 202 contributions on Feb 2313 contributions on Feb 2412 contributions on Feb 253 contributions on Feb 2611 contributions on Feb 2717 contributions on Feb 28Mar15 contributions on Mar 13 contributions on Mar 23 contributions on Mar 112 contributions on Mar 133 contributions on Mar 143 contributions on Mar 171 contributions on Mar 205 contributions on Mar 213 contributions on Mar 222 contributions on Mar 31Apr6 contributions on Apr 42 contributions on Apr 121 contributions on Apr 164 contributions on Apr 216 contributions on Apr 22May1 contributions on May 24 contributions on May 87 contributions on May 91 contributions on May 107 contributions on May 1315 contributions on May 1418 contributions on May 151 contributions on May 1617 contributions on May 1717 contributions on May 189 contributions on May 197 contributions on May 208 contributions on May 225 contributions on May 2311 contributions on May 2416 contributions on May 2514 contributions on May 2611 contributions on May 278 contributions on May 291 contributions on May 31Jun4 contributions on Jun 11 contributions on Jun 21 contributions on Jun 105 contributions on Jun 1121 contributions on Jun 1221 contributions on Jun 138 contributions on Jun 146 contributions on Jun 1526 contributions on Jun 1628 contributions on Jun 177 contributions on Jun 186 contributions on Jun 194 contributions on Jun 205 contributions on Jun 236 contributions on Jun 281 contributions on Jun 29Jul2 contributions on Jul 11 contributions on Jul 26 contributions on Jul 34 contributions on Jul 42 contributions on Jul 144 contributions on Jul 201 contributions on Jul 211 contributions on Jul 2620 contributions on Jul 2822 contributions on Jul 291 contributions on Jul 3012 contributions on Jul 31Aug8 contributions on Aug 14 contributions on Aug 36 contributions on Aug 410 contributions on Aug 521 contributions on Aug 71 contributions on Aug 84 contributions on Aug 912 contributions on Aug 1017 contributions on Aug 1111 contributions on Aug 1241 contributions on Aug 1322 contributions on Aug 143 contributions on Aug 1614 contributions on Aug 1711 contributions on Aug 1814 contributions on Aug 191 contributions on Aug 201 contributions on Aug 2114 contributions on Aug 2249 contributions on Aug 2321 contributions on Aug 2428 contributions on Aug 2530 contributions on Aug 2611 contributions on Aug 274 contributions on Aug 2850 contributions on Aug 2926 contributions on Aug 3014 contributions on Aug 31Sep25 contributions on Sep 148 contributions on Sep 219 contributions on Sep 318 contributions on Sep 720 contributions on Sep 83 contributions on Sep 17
24%GO
20%SVELTE
11%JAVASCRIPT
22
Public repos
3
GitHub stars
1281
Commits / year
19d
Longest streak
06

Experience

9 positions
1
2
3
Dynatrace
Dynatrace
Nov 2025 – Present · 10 months

Master Thesis · Software Engineer

Master thesis: abstracting complex IAM systems into a Prolog-based unified permission metamodel

GoPrologIAMSecurity
Dynatrace
Dynatrace
Aug 2025 – Sep 2025 · 2 months

Software Engineering Intern

Software engineering internship at Dynatrace Engineering Headquarters.

GoJavaSecurity
Johannes Kepler University Linz
Johannes Kepler University Linz
Oct 2023 – Jun 2026 · 2 yrs 9 mo

Student Assistant · Tutor

Tutor for Digital Circuits and Computer Architecture across six semesters

TeachingElectronicsArchitecture
4
5
6
ventopay gmbh
ventopay gmbh
Jul 2024 – Jul 2024 · 1 month

Software Development Intern

Angular and C# development of an existing cashless payment system

AngularC#UI/UX
Bosch
Bosch
Aug 2023 – Sep 2023 · 2 months

Software Development Intern

Development of a dashboard for team-relevant information with SvelteKit and TailwindCSS

SvelteKitTailwindCSSREST
Bosch Rexroth
Bosch Rexroth
Jun 2021 – Jun 2021 · 1 month

Assembly & Testing Intern

Assembly of cable ducts and switch cabinets

HardwareHydraulics
7
8
9
HerzReha Bad Ischl
HerzReha Bad Ischl
Sep 2020 – May 2021 · 9 months

Civil Servant

Support of office staff

Healthcare
ENGEL
ENGEL
Jul 2018 – Jul 2018 · 1 month

Mandatory Internship

Data processing with Excel

Manufacturing
ENGEL
ENGEL
Jul 2017 – Jul 2017 · 1 month

Mandatory Internship

Assistance in production

ManufacturingElectronics
DynatraceNov 2025 – Present · 10 months
Master Thesis · Software Engineer
Dynatrace

Master thesis: abstracting complex IAM systems into a Prolog-based unified permission metamodel

DynatraceAug 2025 – Sep 2025 · 2 months
Software Engineering Intern
Dynatrace

Software engineering internship at Dynatrace Engineering Headquarters.

Johannes Kepler University LinzOct 2023 – Jun 2026 · 2 yrs 9 mo
Student Assistant · Tutor
Johannes Kepler University Linz

Tutor for Digital Circuits and Computer Architecture across six semesters

ventopay gmbhJul 2024 – Jul 2024 · 1 month
Software Development Intern
ventopay gmbh

Angular and C# development of an existing cashless payment system

BoschAug 2023 – Sep 2023 · 2 months
Software Development Intern
Bosch

Development of a dashboard for team-relevant information with SvelteKit and TailwindCSS

Bosch RexrothJun 2021 – Jun 2021 · 1 month
Assembly & Testing Intern
Bosch Rexroth

Assembly of cable ducts and switch cabinets

HerzReha Bad IschlSep 2020 – May 2021 · 9 months
Civil Servant
HerzReha Bad Ischl

Support of office staff

ENGELJul 2018 – Jul 2018 · 1 month
Mandatory Internship
ENGEL

Data processing with Excel

ENGELJul 2017 – Jul 2017 · 1 month
Mandatory Internship
ENGEL

Assistance in production

Education

3 degrees
Johannes Kepler University Linz
Johannes Kepler University Linz
Oct 2024 – Present

MSc Networks and IT Security

Coursework and thesis work in network security, cryptography, and formal methods.

Johannes Kepler University Linz
Johannes Kepler University Linz
Oct 2021 – Jul 2024

BSc Computer Science

Computer Science fundamentals with specialization in software engineering.

HTL Steyr
HTL Steyr
Sep 2015 – Jun 2020

Matura · Mechatronics and Robotics

Robotics, automation engineering, embedded systems, and practical technical project work.

Papers & Theses

research work
HTL

HTL Diploma Thesis

Diploma thesis at HTL Steyr (Mechatronics, Robotics & Automation).

Bachelor

Bachelor Thesis

Bachelor thesis in Computer Science at JKU Linz — LSTM-based speaker diarization.

Master

Master Thesis

Abstracting permission systems into a Prolog-based metamodel, written at Dynatrace. Coming soon — currently in progress.

Coming soon
Project

JKU Master Project Report

Network devices power consumption analysis — a Go-based test runner measuring UDP/TCP/L2 throughput against real-time power draw via FritzBox smart plug.

Places

work · education · home
07

Homelab

live via tailscale
Services

Self-hosted fleet

20 / 20 up
SMTP: mail.mljr.eu23 ms
authelia75 ms
cglab83 ms
gameoflife93 ms
glance63 ms
goaccess148 ms
homeassistant86 ms
homepage214 ms
immich119 ms
kuma163 ms
mailcow143 ms
nas474 ms
nextcloud295 ms
ntfy67 ms
nuc-webui161 ms
projects102 ms
sudoku107 ms
syncthing183 ms
ui-showcase177 ms
wordwiz119 ms
Tailscale mesh

Live infra topology

5 / 5 online
tailnethomeassistant — homeassistantLNXhomeassistant1 svcmljr — authelia, canarytokens, glance, ntfy, goaccess, mailcow, crowdsec, cockpit, diun, hellpot, endlessh, spidertrap, homepage, ui-showcase, regex, cron, codec, newsletter, weather-cli, statusLNXmljr20 svcnas — nas-alloy, iperf3-nas, netronome-agent-nas, ollama, crowdsec-nas, smartctl-exporter-nas, nas, immich, auto-media-sort, nextcloud, dockhand, syncthing, projects, dawarichLNXnas14 svcnuc — nocturne, kuma, healthreport, crowdsec-nuc, backup-dashboard, grafana, mail-archiver, dmarc-monitor, csp-reports, umami, forgejo, speedtest, smartctl-exporter-nuc, nuc-webui, wordwiz, sudoku, gameoflife, cglab, godrive-demo, service-templateLNXnuc20 svcugreen — syncthing-ugreen, oxicloud, smartctl-exporterLNXugreen3 svc

Avg response time

145 ms
746.5143.324h agonow

Avg CPU · all hosts · 24h

9%
9.65.8

CrowdSec perimeter

24172
Active bans
913
Blocked · 24h
977
Alerts · total
6
Hosts online
Community blocklist · 24140caught here · 32

Top threats · active bans

HTTP scanning23290
HTTP exploits420
HTTP brute force412
Aggressive crawling18
http sensitive files14
http probing9
CrowdSec · last 12 months

Attacks blocked per day

89990 total
MonWedFriOctNovDecJanFebMarAprMayJun2221 attacks blocked on Jun 12661 attacks blocked on Jun 131439 attacks blocked on Jun 142496 attacks blocked on Jun 15922 attacks blocked on Jun 16642 attacks blocked on Jun 17495 attacks blocked on Jun 18342 attacks blocked on Jun 19502 attacks blocked on Jun 20367 attacks blocked on Jun 21311 attacks blocked on Jun 221051 attacks blocked on Jun 232332 attacks blocked on Jun 242341 attacks blocked on Jun 255537 attacks blocked on Jun 264163 attacks blocked on Jun 272399 attacks blocked on Jun 283256 attacks blocked on Jun 29710 attacks blocked on Jun 30Jul814 attacks blocked on Jul 1530 attacks blocked on Jul 2659 attacks blocked on Jul 3561 attacks blocked on Jul 41194 attacks blocked on Jul 53790 attacks blocked on Jul 64029 attacks blocked on Jul 73268 attacks blocked on Jul 82075 attacks blocked on Jul 91260 attacks blocked on Jul 10321 attacks blocked on Jul 11506 attacks blocked on Jul 12667 attacks blocked on Jul 13462 attacks blocked on Jul 14449 attacks blocked on Jul 151004 attacks blocked on Jul 16966 attacks blocked on Jul 17667 attacks blocked on Jul 18987 attacks blocked on Jul 19342 attacks blocked on Jul 20200 attacks blocked on Jul 21387 attacks blocked on Jul 22276 attacks blocked on Jul 23927 attacks blocked on Jul 24672 attacks blocked on Jul 25484 attacks blocked on Jul 26522 attacks blocked on Jul 27280 attacks blocked on Jul 28272 attacks blocked on Jul 29176 attacks blocked on Jul 3093 attacks blocked on Jul 31Aug646 attacks blocked on Aug 1607 attacks blocked on Aug 2415 attacks blocked on Aug 3360 attacks blocked on Aug 4458 attacks blocked on Aug 5265 attacks blocked on Aug 6377 attacks blocked on Aug 7457 attacks blocked on Aug 8740 attacks blocked on Aug 91106 attacks blocked on Aug 10800 attacks blocked on Aug 111208 attacks blocked on Aug 12578 attacks blocked on Aug 13396 attacks blocked on Aug 14263 attacks blocked on Aug 15552 attacks blocked on Aug 16478 attacks blocked on Aug 17434 attacks blocked on Aug 18425 attacks blocked on Aug 19337 attacks blocked on Aug 20152 attacks blocked on Aug 21391 attacks blocked on Aug 22530 attacks blocked on Aug 23268 attacks blocked on Aug 24291 attacks blocked on Aug 25377 attacks blocked on Aug 26975 attacks blocked on Aug 27814 attacks blocked on Aug 28815 attacks blocked on Aug 29523 attacks blocked on Aug 30432 attacks blocked on Aug 31Sep585 attacks blocked on Sep 1551 attacks blocked on Sep 2619 attacks blocked on Sep 31437 attacks blocked on Sep 4704 attacks blocked on Sep 5664 attacks blocked on Sep 6608 attacks blocked on Sep 7309 attacks blocked on Sep 8439 attacks blocked on Sep 9519 attacks blocked on Sep 10404 attacks blocked on Sep 11580 attacks blocked on Sep 12569 attacks blocked on Sep 13692 attacks blocked on Sep 141189 attacks blocked on Sep 151716 attacks blocked on Sep 16908 attacks blocked on Sep 17
live — uptime kuma + victoriametrics over tailscale · updated 15:33:09
Architecture

How it hangs together

100% IaC

All public traffic enters through Caddy on the VPS, with CrowdSec banning attackers at the edge and Authelia guarding private apps. Behind that, three machines talk over an encrypted Tailscale mesh — no open ports at home. Every host, container and config file is declared in one Ansible repo: a single make deploy converges the whole fleet.

Internet
→ HTTPS :443 · Caddy ingress · CrowdSec at the edge →
Tailscale mesh · WireGuard
mljr
VPS · public entry
CaddyCrowdSecAutheliapublic apps
nuc
home server
VictoriaMetricsGrafanaLokiinternal services
nas
Unraid NAS
storagebackupsmedia
Ansible provisions all three hosts — inventory, hardening, Docker Compose services, deploy hooks. No snowflakes.
08

Activity

public aggregates
Strava snapshot

Training as data

Distance
255.7 km
run · hike · ski
Moving time
81 h 37 m
logged effort
Sessions
105
year to date
Elevation
2700 m
climbed
Avg HR
138 bpm
session-weighted
Running · 216Cycling · 60Training · 176

Recent public activities

aggregated
Morning Laufen
13.09.2026 · 2:36:32 · 20.9 km · 7:29 /km · ↑ 280 m · ♥ 173 bpm · 2254 kcal
Morning Laufen
09.09.2026 · 1:04:33 · 10.0 km · 6:26 /km · ↑ 83 m · ♥ 167 bpm · 882 kcal
Morning Laufen
06.09.2026 · 1:24:15 · 12.0 km · 7:01 /km · ↑ 113 m · ♥ 167 bpm · 1158 kcal
Morning Laufen
04.09.2026 · 1:17:20 · 11.0 km · 7:01 /km · ↑ 81 m · ♥ 169 bpm · 1074 kcal
Lunch Training
03.09.2026 · 1:01:24 · ♥ 107 bpm · 408 kcal
Lunch Krafttraining
03.09.2026 · 0:09 · 1 kcal
Lunch Training
03.09.2026 · 3:28 · ♥ 110 bpm · 23 kcal
Afternoon Laufen
31.08.2026 · 1:24:07 · 12.1 km · 6:56 /km · ↑ 81 m · ♥ 170 bpm · 1182 kcal

Training load

Fitness (CTL) · Fatigue (ATL) · Form, last 90 days
Form -9.3
59.4-32.7Jun 20Jul 12Aug 3Aug 25Sep 16
Fitness
Fatigue
Form
09

Skills

depth × breadth
LanguagesWebInfraSecurityEmbeddedOps/Data
self-assessed depth per area · dots match the cards

Languages

8
GoRustTypeScriptJavaScriptPythonKotlinJavaC#

Web

8
HTMLCSSSvelteSvelteKitAngularJSAngularASP.NET.NET

Infra / Homelab

7
DockerAnsibleLinuxUnraidTailscaleCaddyCI/CD

Security

5
CybersecurityNetwork SecurityIAMPrologPAM

Embedded / BLE

5
ESP32BLEDexcom G7NightscoutKotlin Multiplatform

Ops / Data

5
SQLiteVictoriaMetricsGrafanaslogWebGL
Go
Rust
TypeScript
JavaScript
Python
Kotlin
Java
C#
HTML
CSS
Svelte
SvelteKit
AngularJS
Angular
ASP.NET
.NET
Docker
Ansible
Linux
Unraid
Tailscale
Caddy
CI/CD
SQLite
VictoriaMetrics
Grafana
slog
WebGL
Cybersecurity
Network Security
IAM
Prolog
PAM
ESP32
BLE
Dexcom G7
Nightscout
Kotlin Multiplatform
10

Under the hood

this site, in code

Every component on this page is a Go function. No React, no npm, no build pipeline for the markup — the server renders HTML, Datastar adds reactivity, and SVG charts are computed at request time.

Browser
no framework runtime — Datastar (14 kB) only
HTTPS · one round trip
Caddy ingress
TLS, CrowdSec + Authelia in front
reverse proxy
One Go binary
gomponents render HTML · SSE patches fragments · SVG charts computed per request
175
UI components
0
npm runtime deps
4×2
themes × modes
1
binary to deploy
5 real excerpts from this site

The arc math behind the gauges in the open-source section.

ui/data/gauge.go
Go
// Gauge renders a circular gauge as inline SVG.
// Zero JS, zero dependencies — pure server-side Go.
func Gauge(p GaugeProps) g.Node {
	cx, cy := float64(p.Size)/2, float64(p.Size)/2
	radius := float64(p.Size) * 0.38
	strokeW := float64(p.Size) * 0.1

	// Arc spans 270° (from 135° to 45°)
	startAngle := 135.0
	totalAngle := 270.0
	pct := (p.Value - p.Min) / (p.Max - p.Min)

	toRad := func(deg float64) float64 {
		return deg * math.Pi / 180
	}
	arcPt := func(deg float64) (float64, float64) {
		a := toRad(deg)
		return cx + radius*math.Cos(a),
			cy + radius*math.Sin(a)
	}
	arcPath := func(start, end float64, color string) string {
		x1, y1 := arcPt(start)
		x2, y2 := arcPt(end)
		large := 0
		if math.Abs(end-start) > 180 {
			large = 1
		}
		return fmt.Sprintf(
			'<path d="M %.1f %.1f A %.1f %.1f 0 %d 1 %.1f %.1f"
			   fill="none" stroke="%s" stroke-width="%.1f"
			   stroke-linecap="round"/>',
			x1, y1, radius, radius, large, x2, y2,
			color, strokeW)
	}

	// Track (full arc), then value arc on top
	sb.WriteString(arcPath(startAngle, startAngle+totalAngle, p.TrackColor))
	sb.WriteString(arcPath(startAngle, startAngle+totalAngle*pct, p.Color))
	// …
}

The homelab panel polls VictoriaMetrics — range queries over 40 days come back empty, so the attack heatmap is fetched in 30-day chunks.

projects/homepage/homelab/homelab.go
Go
// Attacks blocked per day, last ~12 months.
// VictoriaMetrics range queries spanning >40 days can return
// empty results (per-day index cutoff), so fetch the year in
// 30-day chunks and dedupe by day.
seenDay := map[string]bool{}
for chunk := 11; chunk >= 0; chunk-- {
	start := now.AddDate(0, 0, -30*(chunk+1))
	end := now.AddDate(0, 0, -30*chunk)
	pts, err := p.promQueryRange(ctx,
		"sum(increase(cs_bucket_overflowed_total[1d]))",
		start, end, 24*time.Hour,
	)
	if err != nil {
		continue // empty until a year of data exists
	}
	for _, pt := range pts {
		day := time.Unix(int64(pt[0]), 0)
		key := day.Format("2006-01-02")
		if pt[1] > 0 && !seenDay[key] {
			seenDay[key] = true
			s.AttackDays = append(s.AttackDays,
				DayValue{Date: day, Count: int(pt[1])})
		}
	}
}

This very carousel. Pages flip on a Datastar signal; the observer only reacts to display:none→visible, so the animation can never re-trigger itself.

ui/data/pagination.go
Go
// PaginatedPages: each page shown while the shared
// Datastar signal matches its index.
for i, page := range pages {
	wrapped[i] = h.Div(
		g.Attr("data-slot", "page"),
		ui.Show(fmt.Sprintf("$%s === %d", sig, i)),
		page,
	)
}

// Entrance animation replays exactly once per page switch:
// the observer only reacts to style mutations whose OLD value
// was display:none — the CSS animation itself can never
// re-trigger it.
var obs=new MutationObserver(function(muts){
  muts.forEach(function(m){
    var was=(m.oldValue||'');
    var wasHidden=was.indexOf('display:none')>-1;
    if(wasHidden&&m.target.style.display!=='none'){
      m.target.removeAttribute('data-anim');
      void m.target.offsetWidth;      // restart keyframes
      m.target.setAttribute('data-anim','');
    }
  });
});

Live updates without a framework: one attribute on the section, one SSE handler, Datastar morphs the panel in place.

projects/homepage/handlers.go
Go
// One attribute on the section…
h.Section(
	h.ID("homelab"),
	g.Attr("data-on-interval__duration.60s",
		"@get('/api/homelab')"),
	// …
)

// …one SSE handler on the server.
e.GET("/api/homelab", func(c echo.Context) error {
	sse := datastar.NewSSE(c.Response().Writer, c.Request())
	return sse.PatchElements(
		web.RenderToString(pages.HomelabPanel(snapshot())),
	)
})

// Datastar morphs #homelab-panel in place every 60 s:
// no reload, no virtual DOM, no client-side state to sync.

The skills radar — plain trigonometry rendered to an SVG polygon at request time.

ui/data/radarchart.go
Go
// Radar chart: axis i sits at angle 2πi/n, starting at
// 12 o'clock. Values scale along the spoke; the series
// becomes a single <polygon>.
angle := func(i int) float64 {
	return float64(i)*2*math.Pi/float64(n) - math.Pi/2
}
pt := func(radius float64, i int) (float64, float64) {
	a := angle(i)
	return cx + radius*math.Cos(a), cy + radius*math.Sin(a)
}

pts := make([]string, len(p.Axes))
for i := range p.Axes {
	scaled := (s.Values[i] / p.Max) * r
	x, y := pt(scaled, i)
	pts[i] = fmt.Sprintf("%.1f,%.1f", x, y)
}
sb.WriteString(fmt.Sprintf(
	'<polygon points="%s" fill="%s" fill-opacity="0.18"
	   stroke="%s" stroke-width="2"/>',
	strings.Join(pts, " "), color, color))
11

Get in touch

Let's talk

Open to interesting projects, thesis collaborations, and conversations about systems, security, and self-hosting.

Currently

  • MSc thesis @ Dynatrace — Prolog-based permission metamodel
  • Tutor for Digital Circuits @ JKU Linz
  • Building this site with Go · gomponents · Datastar