Erik HorváthDevOps Engineer · Nitra

Erik Horváth — DevOps Engineer, Nitra, Slovakia

I read the memory of a running virtual machine from the outside.

In my master’s thesis I rebuild, from the host, what is running inside the machine: processes, kernel modules and network sockets. No collection agent runs inside it during capture and the machine is not paused. I compared the result with what the machine reports about itself.

Prerequisite: a kernel profile (kallsyms + BTF) from the same boot of the guest, taken from it beforehand.

81 of 81

processes stable during the snapshot — the reconstruction from memory found every one of them: none missing, none extra.1 Plus 51/51 kernel modules and 10/10 listening sockets (+2 explained extras). One validation session, compared with ps, lsmod and ss in the guest.

Table 1. Key facts

The short version in one place. Details and sources follow below.

Now
DevOps Engineer, Marketeam, s. r. o., Nitra · since 02/2026Symfony 3.4 → 6.4 migration, staging; now building Docker and CI/CD. Details
Looking for
full-time, contract or B2B
Where
Nitra · remote · hybrid
Start
immediately or by agreement
Languages
native Slovak · English B2
Education
Bc. (BSc) in Applied Informatics, UKF Nitra (2025)Master’s programme in progress
Stack
Docker/Compose, GitHub Actions, GHCR, Nginx · Linux, Bash · Python, C, SQL, PHP/Symfony · Django/DRF, PostgreSQL, Redis · eBPF, KVMOnly what code or work shows; tools I know mainly from AI-assisted projects are left out.

81 of 81

processes stable during the snapshot — the reconstruction from memory found every one of them: none missing, none extra.1 Plus 51/51 kernel modules and 10/10 listening sockets (+2 explained extras). One validation session, compared with ps, lsmod and ss in the guest.

Fig. 1

How an address in the machine’s memory becomes one specific byte of RAM

A piece of the thesis: one step the tool repeats over and over during reconstruction — finding where in physical memory a given address lives. Step through it, with the code.

Walk through the four x86-64 page-table levelsVirtual address 0xffffffffc0a3f040 split into indices 511, 511, 5, 63 and a page offset; state: Result.Virtual address · 64 bits0xffff ffff c0a3 f04063:48copy of b4747:3951138:3051129:21520:126311:00x040Rootkernel page tablesinit_top_pgtpa 0x280a000symbol from the profile+ VA→PA offsetPML4Linux: PGD512 × 8 B511not usedPDPTLinux: PUD512 × 8 B511not usedPDLinux: PMD512 × 8 B5not usedPTLinux: PTE512 × 8 B63not usedPage4 KiB+0x040not reached

Without JavaScript all steps of the first example are shown at once.

  1. Addressstep 1 of 7

    The guest kernel remembers where each module lives as a virtual address. From the outside we only see physical memory, so the address has to be translated the way the CPU does it: through four page tables.

    va = 0xffffffffc0a3f040 · the module area (from 0xffffffffc0000000) is not linearly mapped, hence the walk · root: table = self._pgt_root() = 0x280a000

  2. PML4Linux: PGDstep 2 of 7

    Bits 47 to 39 pick one of the 512 rows of the top-level table. The row holds no data, only the physical address of the next table.

    idx = (va >> 39) & 0x1FF = 511 · entry 0x000000000280d067 · P=1 RW=1 NX=0 → next table 0x280d000

  3. PDPTLinux: PUDstep 3 of 7

    The next 9 bits pick a row in the second table. Modules sit at the very top of the address space, so the index is 511 here too.

    idx = (va >> 30) & 0x1FF = 511 · entry 0x000000000280f067 · P=1 RW=1 PSE=0 NX=0 → 0x280f000

  4. PDLinux: PMDstep 4 of 7

    Third table. If the row had the PSE bit set, the walk would end here in a 2 MiB page; it is not set.

    idx = (va >> 21) & 0x1FF = 5 · entry 0x0000000004a1e067 · P=1 RW=1 PSE=0 NX=0 → 0x4a1e000

  5. PTLinux: PTEstep 5 of 7

    The last table points at one specific 4 KiB page of physical memory.

    idx = (va >> 12) & 0x1FF = 63 · entry 0x8000000007b3c163 · P=1 RW=1 PAT=0 NX=1 · the mask 0x000FFFFFFFFFF000 also drops the NX bit → 0x7b3c000

  6. Pagestep 6 of 7

    The remaining 12 bits are the offset inside the page. Now we know exactly which byte of memory to read.

    pa = table + (va & 0xFFF) = 0x7b3c000 + 0x040 = 0x7b3c040

  7. Resultstep 7 of 7

    At this physical address lies the module’s structure, name included. This is how the reconstruction walks the whole module list; in one validation session all 51 matched lsmod in the guest.1

    modules(): mpa = self.walk_pa(nxt - o_list) → name = self.cstr(mpa + o_name, 56) · view.py:415, 419

guestparse/view.pyhyptcn3 · private repo · excerpt · lines 204–252 · ref 97b0c43 · AI-assisted
204 def walk_pa_detail(self, va):
222 out = {"pa": None, "level": None, "index": None,
223 "reason": None, "reason_code": None}
224 table = self._pgt_root()
225 if table is None:
226 out["reason_code"] = "bez_korena"
227 out["reason"] = ("neznamy posun jadra alebo profil nema "
228 "init_top_pgt")
229 return out
230 # bity 47:39 / 38:30 / 29:21 / 20:12
231 for level, shift in enumerate((39, 30, 21, 12)):
232 idx = (va >> shift) & 0x1FF
233 name = self.PGT_LEVELS[level]
234 ent = self.u64(table + idx * 8) if self._page_present(table) else None
235 if ent is None:
236 out.update(level=name, index=idx, reason_code="chyba_tabulka",
237 reason="tabulka %s na 0x%x nie je v snimke"
238 % (name, table))
239 return out
240 if not (ent & self.PTE_PRESENT):
241 out.update(level=name, index=idx, reason_code="chyba_polozka",
242 reason="polozka %s[%d] nie je pritomna"
243 % (name, idx))
244 return out
245 phys = ent & 0x000FFFFFFFFFF000
246 if level in (1, 2) and (ent & self.PTE_PSE):
247 size = 1 << shift
248 out["pa"] = phys + (va & (size - 1))
249 return out
250 table = phys
251 out["pa"] = table + (va & 0xFFF)
252 return out
More excerpts in the code browser

NoteVirtual-to-physical translation through the four x86-64 page-table levels, as done by the custom walk_pa_detail function (no libvmi).2 Addresses and entry values are illustrative, because the memory snapshot is not part of the repository. The bit fields, the flow and the code are real.

Master’s thesis

My main project. Solo; the Master’s programme is in progress. Private repository hyptcn3, 09/2026.

What is running inside a virtual machine, worked out from its memory

A tool that runs inside a virtual machine can be switched off or fooled by an attacker in that same machine. So I look from the outside: from the memory of a running KVM guest I rebuild its processes, kernel modules and network sockets.

It is not blind: the reconstruction needs a profile of the guest kernel (kallsyms + BTF), taken from the guest beforehand, once per boot.4 I compared the result with what ps, lsmod and ss reported when run inside the guest.1

Fig. 2How v3 fits together, from guest memory to the model. The box style separates adopted code from code written for v3. All of v3 was built with AI assistance; the box style does not say who wrote the code, only whether it was adopted.

Key to figure 2

  • code written for v3 (AI-assisted)
  • adopted code that I fixed
  • implemented, untrained
  • validation path: checked against the guest’s own view
  • data and files
Block diagram of v3: guest memory, eBPF collector, snapshots, reconstruction, validation and the TCN model. The same content follows below as text.Guest · KVMDebian 12, 2 vCPUno collection agentps · lsmod · ssground truthkallsyms + BTFkernel profileGuest RAM2.02 GiBVM keeps runningread from it:– page tables– processes– kernel modules– socketseBPF programvmic_kvm.bpf.creads KVM memslots4 load fixesvmicollect (C)adopted, ~83% of C linesSHA-NI,streaming hashper-bin features.vmicdfull + deltasnapshotssidecarper-binfeaturesReconstructionguestparse · Python– kernel shift (KASLR)– page tables, 4 levels– processes, modules, sockets– cross-view checksValidationagainst ps, lsmod, ss81/81 processesstable during thesnapshot51/51 kernel modules10/10 sockets (+2)one session,binary from beforethe hashing changeTCN modelimplemented,testeduntrainedground truth: run in the guest over SSH, around the snapshotkernel profile taken from the guest beforehand, once per bootfeature vectorBlock diagram of v3: guest memory, eBPF collector, snapshots, reconstruction, validation and the TCN model. The same content follows below as text.Guest · KVMDebian 12, 2 vCPUno collection agentps · lsmod · ssground truth, over SSHkallsyms + BTFkernel profile for this bootGuest RAM, 2.02 GiBVM keeps runningeBPF programvmic_kvm.bpf.creads KVM memslots4 load fixesvmicollect (C)adopted, ~83% of C linesSHA-NI,streaming hashper-binfeatures.vmicdfull + deltasnapshotssidecarper-binfeaturesReconstructionguestparse · Python– kernel shift (KASLR)– page tables, 4 levels– processes, modules, sockets– cross-view checksValidationagainst ps, lsmod, ss81/81 processesstable during thesnapshot51/51 kernel modules10/10 sockets (+2)one session,binary from before the hashing changeTCN modelimplemented,testeduntrainedfeature vector
The diagram as text
  1. Guest: Debian 12 on KVM, 2 GiB RAM. No collection agent runs in it while a snapshot is taken, and the VM keeps running. Its memory holds the page tables, processes, kernel modules and sockets that are read.
  2. The eBPF program vmic_kvm.bpf.c in the host kernel reads guest memory through the KVM memslots. The program is adopted; mine are the four fixes without which it would not load on kernel 7.1.Code: eBPF: bpf_loop() instead of a loop the verifier rejected
  3. The vmicollect collector in C is adopted (~83% of its C lines, author unknown). For v3 it gained the per-bin feature module and a streaming hash with a SHA-NI path; an AI agent wrote the hashing change under my direction, and its intrinsic ordering follows the Intel/noloader reference.5Code: SHA-NI with CPUID runtime dispatch
  4. The output is full and delta .vmicd snapshots plus a sidecar with the per-bin features.
  5. The custom guestparse reconstruction (Python, AI-assisted) uses the kernel profile taken from the guest for that boot, finds the kernel-image shift (KASLR), walks the page tables (with a known PAT mask bug for huge pages) and rebuilds processes, modules and sockets. Cross-view checks compare independent kernel structures (rootkit invariants).426Code: Finding the kernel’s KASLR shift in a snapshot · x86-64 4-level page-table walk · Cross-view process check (rootkit invariant)
  6. Validation: the result is compared with ps, lsmod and ss run inside the guest over SSH. One validation session, still with the binary from before the hashing change: 81/81 processes stable during the snapshot, 51/51 kernel modules, 10/10 listening sockets (+2 explained extras).1
  7. The per-bin features feed a TCN model. It is implemented and tested but deliberately untrained, so I claim no detection accuracy.7
Table 2. v3 measurements and their conditionsSetup: host i7-12650H, Fedora 43, kernel 7.1 · guest Debian 12, kernel 6.1, 2 vCPU, 2 GiB RAM · validation session 18 Sep 2026.
ParameterConditionsValuenSource
Memory capture
Full RAM snapshot, 2.02 GiBchecksum off; the VM is not paused~0.39 s (median 393.9 ms; 5,240 MiB/s)38
Snapshot cycle with SHA-256checksum on; before and after the change (streaming hash in feed() + a SHA-NI path); measured on uncommitted changes13.4 s → 2.5 s (5.4×)3 + 39
Cost of the faster cyclesame measurement, checksum onmemory read window 388 → 2,288 ms; recorded in the measurement log as a trade-off3 + 39
eBPF program on kernel 7.1the adopted program would not load; 4 separate causes4 fixes: 1 via an opaque struct definition (kfunc BTF FWD/STRUCT mismatch), 3 via bpf_loop() (8193 jumps, the 1M-instruction limit twice)–10code
Reconstruction and validation
Validation against the guestagainst ps, lsmod and ss -tulpn in the guest (over SSH); one validation session; binary from before the hashing change81/81 processes stable during the snapshot (0 missing, 0 false positives) · 51/51 kernel modules · 10/10 listening sockets; +2 extra: an unbound UDP socket and a DNS query that ss -tulpn does not list11
More measurements (8): SHA-NI, the slowdown cause, periodic capture, KASLR, features and tests
Table 2, continued: more measurements
ParameterConditionsValuenSource
Memory capture
SHA-NI vs scalar SHA-256hashing alone; the path is picked by CPUID at run time357 → 2,151 MiB/s (6.0×); bit-exact vs sha256sum on 223 files; UBSan clean311code
Cause of a 56× slowdownSHA-256 was computed over the whole 4 GiB sparse file, holes included; compared with a run without the hash; not a clean A/B (different times, different amounts written)42.2 s → 0.75 s1 + 112
Periodic capture
Periodic delta capture5 s period, 12 cycles (1 full + 11 delta snapshots)0 missed slots1213
Pages changed between snapshotsidle guest, delta, 5 s period~0.046% (median 245 of 528,417 pages)714
Snapshot → feature vector latency2 s period, delta, features onmedian 604 ms, p95 613 ms1215
Reconstruction and validation
Kernel-image VA→PA offset (phys_base, includes KASLR)found in the validation snapshot−2 MiB13code
Features and tests
C per-bin feature module vs Pythonindependent Python reference over the same snapshotsexact match on 6 snapshots × 131 bins616
Tests (pytest)clean clone; the skipped ones need local snapshots213: 185 pass, 28 skip–17

Each figure holds only under the conditions in its own row.

Three iterations

v1 hypTcn (02–03/2026) → v2 hypTcn002 (03–08/2026) → v3 hyptcn3 (09/2026)
  1. v1hypTcn

    02–03/2026 · now a private repository

    The first code iteration: a C wrapper over libvmi → CGO → Go orchestrator → binary protocol over a Unix socket → a TCN in Python. Synthetic data only; the live-VM Go binary was never tested against a real VM.

    AI-assisted. 5 of 37 commits carry an AI co-author trailer. The missing trailer on the C and Go core does not show that AI did not write it.18

    Code: CGO bridge from Go to C/libvmi (v1)

  2. v2hypTcn002

    03–08/2026 · second GitHub account (avUnitfan)

    C/libvmi → CGO → Go → binary frames → a PyTorch TCN. It added an eBPF agent inside the guest, so v2 as a whole is not agentless; a KVM dirty ring is implemented but was blocked in practice (the probe ran in 200 ms polling mode). I retracted its metrics.

    Code written by an AI agent. 81% of 408 commits on develop carry an AI co-author trailer — AI agents wrote the code under my direction. The count includes 37 commits imported from v1.1920

    Code: Custom V2P walk over libvmi (v2) · KVM dirty-ring consumer (v2)

  3. v3hyptcn3

    09/2026 · private repository

    An adopted and fixed eBPF collector over KVM memslots and a custom reconstruction in Python, validated against what the guest itself reports. No Go, CGO or libvmi.

    v3 was built with AI assistance (~24 h of history, large commits, review documents written by AI; an AI agent wrote the SHA-NI hashing change); my part: design, directing and checking the measurements, debugging, checking the validation, retracting wrong results.21

    Code: x86-64 4-level page-table walk · Cross-view process check (rootkit invariant)

I found a flaw in my own results and withdrew them

Invalid, retracted: F1 99.70 AUC 99.98

The second version (v2) had these detection metrics. On review I found that the way I measured distorted the result, so these numbers are invalid. There were two flaws in the method: labels belonged to whole sessions rather than to individual windows (a session-level confound), and windows from the same session ended up in both the training and the test set (leakage). I therefore do not present them as a result anywhere.22

What I built afterwards

  • scripts/check_claims.sh — a claims linter: it flags retracted numbers from a blacklist and checks {{res:…}} provenance tags against the result JSON files. No CI job or hook runs it, and it still reports findings at the current commit (see What I don’t claim).23
  • A test: a signal that carries only the session identity must not beat chance. If the split ever let the same session into both training and test again, the test would fail.24

Work

Employment and projects besides the thesis. For each project I say how it was built and link to code excerpts. The thesis is described above.

Employment · since 02/2026

Marketeam: Symfony 3.4 → 6.4, staging and Pretix memory

  • I migrated the company’s Symfony application across three major versions, from 3.4 to 6.4, including the changes for new APIs, dependencies and configuration.25
  • I built a staging environment and am building the deployment infrastructure (Docker, CI/CD).25
  • Pretix, a ticketing system, ran in a container that used 6.7 GB of memory. I limited the Celery worker concurrency to two and memory dropped to ~0.9 GB (measured at work).26 In code, it is a one-line change to the supervisord configuration.27

Public change:PR #1 in Marketeam-SK/pretixcommit 3e0dc2f07

The PR title says “Increase concurrency”; the change actually adds a fixed --concurrency 2 limit where none was set before.

Team project · 3 people · 10/2025 – 01/2026 · SIprojekt (team repo)

Internship management system: backend and custom OAuth 2.0 server

The Django/DRF backend is mine: 98.75% of the Python lines by git blame, 96.4% with move/copy detection. My teammates built the frontend and the SQL schema.28

How it was built: 0 commits with an AI co-author

Known gap: PKCE still accepts the plain method, and it is the default; a private_key_jwt assertion need not carry exp, and jti is not checked against replay.2930

SIprojekt: Code: 5 excerpts
Details and stack: SIprojekt
  • A custom OAuth 2.0 authorization server with no OAuth server library (SimpleJWT mints the tokens, PyJWT checks the assertions): 4 grant types, PKCE S256 and private_key_jwt client authentication per RFC 7523.30
  • An end-to-end test generates an RSA key pair, signs a JWT assertion and calls a protected API with the token it receives.31
  • An 11-step GitHub Actions release pipeline builds the backend and frontend images to GHCR; it ran successfully on 13 January 2026.32
  • Redis caching with signal-based invalidation, and a refactor from fat views to a service layer.

Why it matters: I implemented OAuth 2.0 at the protocol level rather than configuring a library — and I know which gaps it still has.

Stack: Python · Django · DRF · SimpleJWT · PyJWT · PostgreSQL · Redis · Docker Compose · Nginx · GitHub Actions · GHCR

Excerpts in the code browser

Solo prototype · 01/2026 · ~10 days, 14 PRs · scanforeshop (private repo)

scanforeshop: scripts injected into e-shops at runtime

In Magecart-style attacks, a foreign script gets into an e-shop page and skims payment details. scanforeshop opens the page in a browser (Playwright) and, before the page’s own JavaScript runs, wraps two of the DOM functions that insert scripts (appendChild, insertBefore). That lets it separate external scripts the server sent in the HTML from those added at runtime.33

How it was built: AI-assisted (Codex). The trace in the code is identifiers with “codex” in their names.34

Status: prototype. It wraps only appendChild and insertBefore, so other insertion methods are missed; downstream only scripts with a src are kept, so an inline injection never counts as dynamic. Scoring still ignores the dynamic flag.3335

scanforeshop: Code: 4 excerpts
Details and stack: scanforeshop
  • Every script gets a SHA‑256 identity. A deterministic diff of two scans feeds alerts, a timeline and an HTML or PDF report.36
  • Risk is weighted by page type (landing, shop, checkout), with tests that calibrate false positives.

Why it matters: a script injected in the browser is not in the server’s HTML, so it has to be caught at the moment it is inserted.

Stack: Python · Django · DRF · Playwright · SQLite · Next.js (frontend prototype) · TypeScript · Docker Compose

Solo proof of concept · 06/2026 · sovereign-ai-poc (private repo)

Signed supply chain and egress monitor (PoC)

A proof of concept of an EU-sovereign MLOps setup on docker-compose: Forgejo, MinIO, PostgreSQL, MLflow, a Zot registry and DVC data versioning.

How it was built: code written by an AI agent — Claude, acceptance scripts included, under my direction; the specification and architecture are mine.

Status: PoC. Phase 6 is unfinished, the model is a toy (iris), and phases 2, 4 and 5 cannot run from a clean clone because the dataset is not in the repo.37

sovereign-ai-poc: Code: 4 excerpts
Details and stack: sovereign-ai-poc
  • An image has to pass a Trivy gate (no CRITICAL vulnerabilities); cosign then signs it by digest with an offline key and an SPDX SBOM attestation is attached.38
  • Negative tests check that an unsigned or vulnerable image is rejected.39
  • A bpftrace monitor scoped to one container’s cgroup tells ordinary outbound connections from a deliberate exfiltration test.40

Why it matters: the negative tests are written to fail unless the gate rejects a bad image — not only to check that a good one passes.

Stack: Docker Compose · Forgejo · MinIO · PostgreSQL · MLflow · Zot · DVC · cosign · Trivy · SPDX SBOM · bpftrace · Bash

Excerpts in the code browser

Solo MVP · 07/2026 · dolt-platform (private repo)

dolt: assessing DevOps skills from work traces

An MVP that assesses junior DevOps engineers by how they actually work. A stdlib-only Go CLI keeps a SHA‑256 hash-chained JSONL work log with an ed25519 device key; a FastAPI server re-verifies the chain and a deterministic scorer rates the log.

How it was built: code written by an AI agent — mine are the product idea, the architecture decision log D‑001 to D‑015 and a 27-check acceptance harness that I specified and froze; the agent placed it in the repo.

Limits: the freeze was by instruction only, not enforced technically — in one commit the agent changed the file mode of eval/eval.sh. The server does not yet verify the ed25519 signature.4142

dolt-platform: Code: 3 excerpts
Details and stack: dolt-platform
  • I reproduced the result: 27 of 27 checks pass.43

Why it matters: when an agent writes the code, the acceptance harness is the contract — the agent is meant to change the code, not the yardstick. Here that rule was enforced by instruction only.

Stack: Go (stdlib) · Python · FastAPI · Bash · ed25519 · SHA-256 hash chain · Docker Compose (test scenario)

Code

Most of the repos are private, so this shows selected excerpts only.

Each excerpt is pinned to a commit and carries line-by-line git blame: my commits, imported code and commits with an AI co-author trailer are marked in the gutter. The note above the code says why it matters, known bugs included.

A static snapshot from the GitHub API, generated at build time. Excerpts only — we’ll go through the full code on a technical call. As of 25 September 2026.44

  • Languages: Python 51% · C 37% · Shell 11% · other 0.9%

    How it was built: Table 3

Thesis: earlier versions (v1 → v2 → v3)

3 more repositories appear only in Table 3, without code excerpts.

Files 305 files at 97b0c43

guestparse25 files
__init__.py
__main__.py
checks.pyexcerpt
cli.py
image.py
profile.py
README.md
validate.py
view.py2 excerpts
204–252 x86-64 4-level page-table walk
76–112 Finding the kernel’s KASLR shift in a snapshot
vmicollect31 files
bpf2 files
vmic_bpf_abi.h
vmic_kvm.bpf.cexcerpt
src22 files
backend.c
backend_ebpf.c
backend_file.c
collector.c
config.c
hooks.c
internal.h
log.c
log.h
main.c
meta.c
perbin.c
perbin.h
retention.c
sched.c
sha256.cexcerpt
sha256.h
util.c
util.h
writer.c
writer_delta.c
writer_raw.c
Makefile
README.md
vmicollect.example.conf
.gitignore
HONESTY.md
README.md

Filtered: 90 of 305 files are not shown.

locked: private code, we can go through it on a technical call excerpt: readable part of the file

x86-64 4-level page-table walk

hyptcn3/guestparse/view.py

lines 204–252pinned to 97b0c43snapshot generated 25 September 2026no drift: main is still at this commit

Why it matters

A custom page-table walk (no libvmi) that translates guest-kernel virtual addresses to physical ones straight from the RAM snapshot, including 2 MiB / 1 GiB huge pages (PSE bit). Instead of a bare None it returns the failing level and a reason code, so it can tell “table was not collected” from “the kernel does not map this address” — the profile diagnosis depends on that. Known bug: for a huge page the mask keeps bit 12 (PAT) in the address, so the physical address is wrong whenever PAT is set.

Git blame:b574b8d 19 September 2026, commit from my account, 34 lines18d4eaf 18 September 2026, commit from my account, 15 lineshighlighted lines

204    def walk_pa_detail(self, va):205        """206        Preklad virtualnej adresy hosta prechodom tabuliek stranok207        (4 urovne, x86_64). Pokryva aj oblasti, ktore linearne mapovane nie su -208        moduly a vmalloc.209210        Vracia {'pa', 'level', 'index', 'reason', 'reason_code'}; 'pa' je None,211        ked sa prechod nedokoncil. Velke stranky (1 GiB, 2 MiB) sa rozpoznaju212        podla bitu PSE.213214        Kody dovodov (na nich stoji diagnoza, nie na samotnom None):215          bez_korena     - nepozname posun jadra alebo init_top_pgt216          chyba_tabulka  - tabulku na tej urovni snimka neobsahuje (nic to217                           nehovori o profile, iba o tom, co sa zozbieralo)218          chyba_polozka  - tabulka V SNIMKE JE, ale polozka pre tuto adresu219                           v nej nie je pritomna, teda jadro tuto virtualnu220                           adresu nemapuje221        """222        out = {"pa": None, "level": None, "index": None,223               "reason": None, "reason_code": None}224        table = self._pgt_root()225        if table is None:226            out["reason_code"] = "bez_korena"227            out["reason"] = ("neznamy posun jadra alebo profil nema "228                             "init_top_pgt")229            return out230        # bity 47:39 / 38:30 / 29:21 / 20:12231        for level, shift in enumerate((39, 30, 21, 12)):232            idx = (va >> shift) & 0x1FF233            name = self.PGT_LEVELS[level]234            ent = self.u64(table + idx * 8) if self._page_present(table) else None235            if ent is None:236                out.update(level=name, index=idx, reason_code="chyba_tabulka",237                           reason="tabulka %s na 0x%x nie je v snimke"238                                  % (name, table))239                return out240            if not (ent & self.PTE_PRESENT):241                out.update(level=name, index=idx, reason_code="chyba_polozka",242                           reason="polozka %s[%d] nie je pritomna"243                                  % (name, idx))244                return out245            phys = ent & 0x000FFFFFFFFFF000246            if level in (1, 2) and (ent & self.PTE_PSE):247                size = 1 << shift248                out["pa"] = phys + (va & (size - 1))249                return out250            table = phys251        out["pa"] = table + (va & 0xFFF)252        return out

“From my account” means the commit’s author is one of my GitHub accounts. A commit without a trailer does not mean the code was written without AI; the repo’s label says how it was built.4546

How I work with AI

What I do, what the agent does, and where the history shows it.

I start with the design and a written spec with acceptance criteria. AI agents then write much of the code. I read it, test it and, where I can, verify it with measurements on the real system. When a result does not hold up, I retract it — as I did with my own figures from the second version of my thesis.

Table 3. Commits and AI co-author trailers by repository47Counts come from a static snapshot taken on 25 September 2026, up to the commit the snapshot is pinned to, and include all authors. A Co-Authored-By trailer captures only part of AI use: thesis v3 and scanforeshop have 0 trailers but were built with AI assistance. The “How it was built” column therefore rests on the whole history and my own disclosure, not on trailers alone.2134
RepositoryPeriodCommitsWith AI trailerHow it was built
Bally17/SIprojektInternship management system · team of 310/2025 – 01/2026497 commits0 with an AI trailershare of commits with an AI trailer: 0%0 commits with an AI co-author
scanforeshopDetecting scripts injected into e-shops01/202669 commits0 with an AI trailershare of commits with an AI trailer: 0%AI-assisted
hypTcnMaster’s thesis v1 (hypTcn)02/2026 – 03/202637 commits5 with an AI trailershare of commits with an AI trailer: 14%AI-assisted
avUnitfan/hypTcn002Master’s thesis v2 (hypTcn002) · avUnitfan account03/2026 – 08/2026408 commits332 with an AI trailershare of commits with an AI trailer: 81%code written by an AI agent
scrapeitallLookr API — public registry aggregator04/2026 – 08/202625 commits24 with an AI trailershare of commits with an AI trailer: 96%code written by an AI agent
sovereign-ai-pocEU-sovereign MLOps proof of concept (docker-compose)06/20268 commits8 with an AI trailershare of commits with an AI trailer: 100%code written by an AI agent
dolt-platformAssessing DevOps skills from work traces07/202616 commits16 with an AI trailershare of commits with an AI trailer: 100%code written by an AI agent
hyptcn3Master’s thesis v3: agentless KVM guest introspection09/202611 commits0 with an AI trailershare of commits with an AI trailer: 0%AI-assisted
booking-systemMulti-tenant booking SaaS09/202620 commits16 with an AI trailershare of commits with an AI trailer: 80%code written by an AI agent
digitwinDelivery digital twin (demo)09/20263 commits3 with an AI trailershare of commits with an AI trailer: 100%code written by an AI agent
What the labels mean
code written by an AI agent
an AI agent wrote most or all of the code; mine are the spec, architecture and direction, plus acceptance tests and verification where a project says so.
AI-assisted
AI was involved in the code, but authorship is mixed or cannot be established exactly from git.
0 commits with an AI co-author
no commit in the history carries an AI co-author trailer. It describes the git record, not how the code was typed.

What I don’t claim

The main limits of my claims in one place, each with its reason. Other known bugs are described next to the code excerpts.

Table 4. Limits of the claims
AreaLimitWhy
Master’s thesis
Detection accuracyNone claimed. The TCN model is untrained.The model is implemented and tested (causality, receptive field) but deliberately untrained, so no accuracy figure exists. The retracted v2 metrics are invalid.722
Validation scopeOne validation session on one Debian 12 guest (18 Sep 2026).The validation results in Table 2 hold for that session. I do not generalise them to other kernels or to a guest under load.1
Validation and the faster snapshot cycle81/81 holds for the binary from before the hashing change.After the change, with the checksum on, the memory read window grew from 388 to 2,288 ms. How the longer window affects reconstruction accuracy has not been measured; the measurement log lists it as an open item.91
Knowledge of the guestThe reconstruction needs a kernel profile (kallsyms + BTF) taken from the guest for each boot.“Agentless” means that no part of the tool and no collection or security agent runs in the guest during capture. The guest itself keeps running normally. In the validation session the ground truth (ps, lsmod, ss) was collected in the guest over SSH.4
Authorship of the vmicollect collector~83% of its C lines are imported (author unknown); ≈1.5k lines of C come from my account (AI-assisted).I adapted and fixed an existing eBPF collector; I did not write it. My account added the per-bin module and the eBPF load fixes. An AI agent wrote the streaming hash with the SHA-NI path under my direction; its intrinsic ordering follows the Intel/noloader reference.546
Page-table walk in v3Known bug: for huge pages the mask 0x000FFFFFFFFFF000 keeps bit 12 (PAT) in the address.When PAT is set, the physical address comes out wrong. v2 masks huge pages correctly, so v3 is not strictly better here.2Code: x86-64 4-level page-table walk
The check_claims.sh claims linterAt the current commit it reports 38 findings, mostly in docs/notebookLM.md.No CI job or hook runs it, so it blocks nothing. I do not claim the repository passes it.23
Other projects and work
SIprojekt: Nginx and TLSNo claim of TLS in production.Security headers, rate limits and a TLS 1.2/1.3 profile are configured, but the committed production config never served TLS (it fails nginx -t).48
SIprojekt: OAuth 2.0 serverKnown gaps: PKCE also accepts the plain method (and defaults to it), code redemption is not atomic, and a private_key_jwt assertion need not carry exp; jti is not checked.It is a custom implementation with no OAuth server library, pinned to commit 2e96d44. The gaps are visible in the code and described, with line numbers, next to the excerpts.2930Code: Client authentication via private_key_jwt (RFC 7523)
KubernetesBasics (minikube).I have only brought up a cluster in minikube, so I do not list Kubernetes as a skill.
scanforeshopA prototype with known gaps.Scoring ignores the dynamic-insertion flag; only appendChild and insertBefore are wrapped (not append, prepend, innerHTML or document.write), and only a directly inserted script is recorded, not one nested in another element; downstream only scripts with a src are kept; vendors are matched by substring; the app itself has no authentication; the Next.js frontend is only a prototype (next build fails).3335
dolt-platformThe harness freeze was by instruction only. The server does not verify the ed25519 signature.In one commit the agent changed the file mode of eval/eval.sh. The ratchet fails only when the pass count drops; it does not catch trading one check for another. The server re-verifies the hash chain of the log but only stores the device key and the signature.414342
sovereign-ai-pocThe PoC’s “green” status is not backed by a fresh end-to-end run.It rests on commit messages and NEXT.md. Phases 2, 4 and 5 do not run from a clean clone because the dataset is not in the repository.37
Pretix memory (Marketeam)I measured the memory saving at work; there is no public artefact for it.Only the one-line change in the merged PR is public. Its title says “Increase concurrency”, but the change adds a fixed --concurrency 2 where no limit was set before.2627

Experience and education

Most recent first.

Employment

  1. – present

    DevOps Engineer · Marketeam, s. r. o., Nitra

    Symfony 3.4 → 6.4 migration, staging; now building Docker and CI/CD.

    Details in the Work section

  2. –

    AI engineer, internship · HOFITECH s. r. o.

    Details under NDA.

  3. –

    Frontend developer (Angular) · freelance

  4. –

    Programming lecturer for children · EDU PLAY s. r. o., Nitra

Education

  1. – present

    Applied Informatics, Master’s programme (in progress) · UKF Nitra

    Thesis: agentless introspection of a running KVM virtual machine.

  2. –

    Bc. (BSc) in Applied Informatics · UKF Nitra

    Bachelor’s thesis: GraphVisualization, a tool that shows graph algorithms step by step (Python, Tkinter).

  3. –

    Software Engineering · UTB Zlín

    Not completed.

Also: Driving licence (category B) · outside work: boxing and football

Contact

Write or call me. I am equally open to full-time, contract and B2B work.

The next step is a short call; if you want to go deeper, we can walk through any code on this page in a technical conversation, including the private repositories.

Sources

Every numbered mark on the page leads here. Paths are relative to the repository root at the commit the snapshot is pinned to.

Show all 48 sources

Introduction

  1. 1

    Reconstruction compared with in-guest ps, lsmod and ss (one validation session, 18 Sep 2026)

    artefacthyptcn3 @ 97b0c43 · private

    data/results/2026-09-18_zmrazeny_host/summary.json → values.validacia_voci_pozemnej_prave

    processes: 83 in ps, 81 of them stable during the snapshot, 81 found, 0 missing, 0 extra · modules 51/51 · listening sockets 10/10, +2 extra (an unbound UDP socket and a DNS query that ss -tulpn does not list)

Figure 1: the page-table walk

  1. 2

    Custom x86-64 page-table walk (walk_pa_detail) with a failure reason per level

    codehyptcn3 @ 97b0c43 · private

    guestparse/view.py:204-252

    Known bug: for a huge page the mask 0x000FFFFFFFFFF000 keeps bit 12 (PAT) in the address, lines 245–248.

  2. 3

    Kernel-image VA→PA offset (phys_base, includes KASLR) found in the validation snapshot: −2 MiB (−2,097,152 B)

    artefacthyptcn3 @ 97b0c43 · private

    data/results/2026-09-18_zmrazeny_host/validate2.json → snapshot.ktext_shift

Master’s thesis

  1. 4

    The guest kernel profile (kallsyms + BTF) is taken from the guest beforehand and is valid for one boot

    codehyptcn3 @ 97b0c43 · private

    scripts/get_profile.sh

    The profile records the guest’s boot_id (profiles/debian12-6.1.0-42-cloud-amd64/README.md) and is fetched over SSH or qemu-guest-agent. Capture itself needs no access to the guest.

  2. 5

    vmicollect is an adopted collector: ~83% of its C lines are imported (author unknown); ≈1.5k lines of C are mine

    git blamehyptcn3 @ 97b0c43 · private

    README.mdcommit 6b9053d

    Imported in commit 6b9053d (“adopted state, unchanged”); README.md states the origin. Lines from my account by git blame: the per-bin module, SHA-NI and streaming hash, the eBPF load fixes; an AI agent wrote the hashing change (data/results/2026-09-18_zmrazeny_host/env.json).

  3. 6

    Cross-view rootkit checks: sys_call_table bounds, task list vs children tree, modules vs module_kset

    codehyptcn3 @ 97b0c43 · private

    guestparse/checks.py

  4. 7

    TCN model: causality and receptive-field tests; the model is deliberately untrained

    testhyptcn3 @ 97b0c43 · private

    tcn/tests/test_tcn.py

    No detection accuracy figure exists.

  5. 8

    Full 2.02 GiB snapshot without checksum: median read 393.9 ms, 5,240 MiB/s, VM not paused

    artefacthyptcn3 @ 97b0c43 · private

    data/results/2026-09-18_zmrazeny_host/summary.json → values.once_raw_hash_none

    vmicollect once, writer=raw, output.hash=none, 3 repetitions; paused: false.

  6. 9

    Snapshot cycle with SHA-256 before and after the change (13.4 s → 2.5 s) and its cost (read window 388 → 2,288 ms)

    artefacthyptcn3 @ 97b0c43 · private

    data/results/optim_hash_20260918.json → values.pred.once_sha256, values.po.once_sha256

    Two changes together: a streaming hash in feed() instead of hashing the sparse file in finish(), plus a SHA-NI path; an AI agent wrote the change. Measured on uncommitted changes; the binary hashes are recorded in the file. docs/MERANIA.md:576-605 describes the trade-off.

  7. 10

    Four reasons the eBPF program would not load on kernel 7.1, and their fixes

    codehyptcn3 @ 97b0c43 · private

    vmicollect/bpf/vmic_kvm.bpf.ccommit 178ce4f

    kfunc BTF FWD/STRUCT mismatch (opaque struct definition); “sequence of 8193 jumps is too complex” and two 1M-instruction limits (bpf_loop()).

  8. 11

    SHA-NI with CPUID dispatch: 357 → 2,151 MiB/s (6.0×), bit-exact vs sha256sum on 223 files, UBSan clean

    artefacthyptcn3 @ 97b0c43 · private

    data/results/optim_hash_20260918.json → values.sha256_priepustnost_mib_s, values.overenie

    The intrinsic ordering in the SHA-NI path follows the Intel / noloader SHA-Intrinsics reference (vmicollect/src/sha256.c:21-26).

  9. 12

    Cause of a 56× slowdown: SHA-256 over the whole 4 GiB sparse file including holes (42.2 s → 0.75 s)

    artefacthyptcn3 @ 97b0c43 · private

    docs/MERANIA.md:135-156commit 18d4eaf

    The measurement log itself notes that the pair of runs is not a clean A/B comparison (different times, 307 vs 493 MiB written) and cannot pass as a repeated measurement.

  10. 13

    Periodic capture at a 5 s period: 12 cycles, 0 missed slots

    artefacthyptcn3 @ 97b0c43 · private

    data/results/2026-09-18_zmrazeny_host/summary.json → values.run_delta_perioda_5s.summary_z_behu

    The run log in run_delta_5s.json ends with “0 zmeskanych slotov” (0 missed slots).

  11. 14

    Idle guest: median 245 changed pages of 528,417 between snapshots (~0.046%)

    artefacthyptcn3 @ 97b0c43 · private

    data/results/2026-09-18_zmrazeny_host/summary.json → values.run_delta_perioda_5s.prirastkove_snimky_bez_sedenia_ssh

  12. 15

    Snapshot → feature vector latency: median 604.3 ms, p95 612.6 ms

    artefacthyptcn3 @ 97b0c43 · private

    data/results/latency_vector_20260918.json → values.suhrn["on_2.0s_delta"].latencia_ms

    From the start of the capture cycle to the written features block; 2 s period, delta writer, hash off.

  13. 16

    The C per-bin feature module matches the Python reference on 6 snapshots × 131 bins

    artefacthyptcn3 @ 97b0c43 · private

    data/results/perbin_crosscheck_20260918.json → values.vsetky_porovnania_ok

  14. 17

    Tests: 213 collected, 185 pass, 28 skip on a clean clone

    testhyptcn3 @ 97b0c43 · private

    python3 -m pytest -q on a clean clone at 97b0c43; the skipped tests need local snapshots that are not in the repository.

  15. 18

    v1 (hypTcn): 5 of 37 commits carry Co-Authored-By: Claude; the live-VM Go binary was never tested against a real VM

    git historyhypTcn @ 9fa11b6 · private

    PROJECT.md:247

    The C/Go core commits carry no trailer, but Claude Code was already in use in the repository, so a missing trailer proves nothing. The repository is private now.

  16. 19

    v2 (hypTcn002): 332 of 408 commits on develop (81%) carry an AI co-author trailer, including 37 commits imported from v1

    git historyavUnitfan/hypTcn002 @ 0acd094 · private

    commit 0acd094

    Second GitHub account (avUnitfan). Across all branches 348 of 424 (82%).

  17. 20

    v2: an eBPF agent inside the guest, and a KVM dirty ring “implemented, blocked” (the probe ran in 200 ms polling mode)

    codeavUnitfan/hypTcn002 @ 0acd094 · private

    internal/extractor/kvm_dirty_ring.c:283-338

    Agent: tools/guest-agent/ and probe/ebpf_per_process/. README.md:117-136 at 0acd094 describes the dirty-ring status (ptrace_scope=1, libvirt 10.0).

  18. 21

    Thesis v3: 0 trailers, yet AI-assisted

    git historyhyptcn3 @ 97b0c43 · private

    About 24 h of history with large commits, review documents written by AI, and an AI agent wrote the SHA-NI hashing change. My role: design, directing and checking the measurements, debugging, checking the validation and retracting wrong results.

  19. 22

    Retracted v2 results and why: session-level labels and window leakage between the training and test sets

    artefacthyptcn3 @ 97b0c43 · private

    HONESTY.md → § 2

  20. 23

    Claims linter: flags retracted numbers and checks {{res:…}} provenance tags against result JSON files

    codehyptcn3 @ 97b0c43 · private

    scripts/check_claims.sh

    No CI job or hook runs it, so it blocks nothing. At 97b0c43 it exits 1 with 38 findings, mostly in docs/notebookLM.md.

  21. 24

    Test: a signal that carries only the session identity must not beat chance

    testhyptcn3 @ 97b0c43 · private

    tcn/tests/test_tcn.py:162-176

    test_split_neprepusta_identitu_session

Work

  1. 25

    Marketeam: Symfony 3.4 → 6.4 migration, staging and deployment infrastructure

    statement

    My own statement from my CV; the company code is not public.

  2. 26

    Pretix container memory: 6.7 GB before the change, ~0.9 GB after

    measured at work

    Measured at work; the figures are not in any repository.

  3. 27

    One-line change --concurrency 2 for the pretix taskworker (supervisord)

    git history

    deployment/docker/supervisord/pretixtask.confcommit 3e0dc2f07

    Merged PR #1 in Marketeam-SK/pretix; commit 3e0dc2f07 in the ErikHorvath-git/pretix fork. The PR is titled “Increase concurrency for pretix taskworker”; the diff adds --concurrency 2 to a command that had no limit before.

  4. 28

    Share of the backend: 11,075 of 11,215 Python lines (98.75%)

    git blameBally17/SIprojekt @ 2e96d44 · public

    git blame over the backend Python, excluding migrations; 96.4% with move/copy detection. The frontend (my share 1.7%) and the SQL schema are my teammates’ work.

  5. 29

    PKCE: S256 and plain; plain is the default

    codeBally17/SIprojekt @ 2e96d44 · public

    backend/services/auth/oauth/pkce.py:1-20

    pkce.py:5, 18 and authorization.py:64.

  6. 30

    OAuth 2.0 server: grants and private_key_jwt client authentication

    codeBally17/SIprojekt @ 2e96d44 · public

    backend/services/auth/oauth/token_flow.py:20-71

    See also pkce.py and authorization.py in the same folder. Known gap: jwt.decode (line 57) does not require an exp claim, and jti is not checked against replay.

  7. 31

    End-to-end test of a client with a signed JWT assertion (RFC 7523)

    testBally17/SIprojekt @ 2e96d44 · public

    backend/tests/test_external_integration.py:232-265

  8. 32

    GitHub Actions release pipeline (11 steps, images to GHCR)

    codeBally17/SIprojekt @ 2e96d44 · public

    .github/workflows/release.yml

    Successful run on 13 January 2026.

  9. 33

    Init script: wrapping appendChild and insertBefore before the page’s JavaScript

    codescanforeshop @ 23ba0ea · private

    backend/services/discovery/__init__.py:9-45

    Only these two functions are wrapped (lines 41–42); downstream, only records with a src are kept (lines 130–134).

  10. 34

    scanforeshop was built with AI assistance (OpenAI Codex)

    statementscanforeshop @ 23ba0ea · private

    My statement of 25 September 2026. Git has no AI trailers; the code has identifiers such as __codexDynamicScripts and data-codex-*.

  11. 35

    Score formula: noise, unverified scripts and behaviour penalties, with no term for the dynamic flag

    codescanforeshop @ 23ba0ea · private

    backend/services/scoring/engine.py:151-157

  12. 36

    SHA-256 script identity and a deterministic scan diff

    codescanforeshop @ 23ba0ea · private

    backend/services/normalization/__init__.py:45-78

    See also backend/services/diff/engine.py and pipeline.py.

  13. 37

    PoC status: phase 6 unfinished, “green” phases per commit messages

    git historysovereign-ai-poc @ e9ded40 · private

    NEXT.mdcommit e6a304b

    The dataset for phases 2, 4 and 5 is not committed, so they cannot run from a clean clone.

  14. 38

    Supply chain: Trivy gate → cosign signature by digest → SBOM

    codesovereign-ai-poc @ e9ded40 · private

    scripts/supply_chain.sh:54-92

  15. 39

    Negative tests: unsigned and vulnerable images are rejected

    testsovereign-ai-poc @ e9ded40 · private

    scripts/verify_phase3.sh:53-80

  16. 40

    bpftrace egress monitor: ordinary connections vs. deliberate exfiltration

    testsovereign-ai-poc @ e9ded40 · private

    scripts/verify_phase5.sh:123-172

    The monitor itself is infra/ebpf/egress_monitor.bt.

  17. 41

    eval/ frozen by instruction only; agent commit b1390f9 changed eval/eval.sh’s mode

    git historydolt-platform @ c65abb0 · private

    run-loop.sh:20commit b1390f9

    The instruction is in run-loop.sh:20 and in the agent instructions. eval/eval.sh arrived in commit b2bdee5 (with a Claude trailer); decision D-012 in CONTEXT.md records that I specified it.

  18. 42

    The server re-verifies the chain but only stores the ed25519 signature

    codedolt-platform @ c65abb0 · private

    server/app.py:19-28

    See also server/chain.py.

  19. 43

    Acceptance harness with 27 checks and a ratchet; reproduced 27/27

    testdolt-platform @ c65abb0 · private

    eval/eval.sh:187-212

    The ratchet fails only when the PASS count drops below the best recorded (eval.sh:198–203). Decisions D-001 to D-015 are in CONTEXT.md.

Code

  1. 44

    Repository snapshot from the GitHub API

    git history

    File tree, languages, commit counts and excerpts at each repo’s pinned commit, generated by tools/snapshot at build time. Each excerpt’s text is checked against the git blob hash, and an anchor line must lie inside its range.

  2. 45

    Git blame of the excerpts at the pinned commit (GitHub GraphQL)

    git blame

    Only a short hash, a date and three flags are kept per commit: author is my account, AI co-author trailer, imported commit. No names, e-mails or commit messages are stored. Checked against a local git blame --porcelain: all 960 lines in 25 excerpts match.

  3. 46

    The commit that brought the vmicollect collector into hyptcn3 unchanged

    git historyhyptcn3 @ 97b0c43 · private

    commit 6b9053d

    Commit message: “vmicollect: prevzaty stav k 2026-08-26, bez zmien” (imported state as of 2026-08-26, unchanged). Lines last changed by this commit are marked in the blame as imported code, author unknown.

How I work with AI

  1. 47

    Commit and AI co-author trailer counts for each repository

    git history

    src/data/snapshot.json → repos[].commits.total, repos[].commits.aiCoAuthored

    This site’s static snapshot (tools/snapshot). It counts the commits reachable from the pinned commit; a trailer is a Co-Authored-By line naming Claude, Copilot, Cursor, Codex, GPT, OpenAI or Anthropic.

What I don’t claim

  1. 48

    Nginx: rate limits, a TLS 1.2/1.3 profile and security headers are in the config

    codeBally17/SIprojekt @ 2e96d44 · public

    nginx/nginx.conf:31-66

    The committed production config never served TLS: nginx -t fails on it.