[{"content":" Update (2026-11-07): The fonts are now self-hosted instead of loaded from Google Fonts, because the site\u0026rsquo;s Content Security Policy blocks external stylesheets and fonts. The @font-face rules live in assets/css/extended/fonts.css, and extend_head.html now only preloads the font files.\nMost Hugo theming guides will tell you to fork the theme and modify it directly. I am here to tell you that is not necessary and the solution I propose and utilize here in this blog is much simpler and more maintainable. First I will talk about why forking a theme and using that is not what I see as the ideal solution for this use-case:\nIt\u0026rsquo;s boring! Making your own theme is fun and will give your blog more personality. Having to cater your blog posts to upstream changes in the theme is a shortcut to becoming frustrated with maintaining your blog; it adds unnecessary friction to your life and workflow for blogging. So, for a site running PaperMod as a git submodule, that approach is a maintenance problem waiting to happen and most importantly, isn\u0026rsquo;t fun. The cleaner solution is to use Hugo\u0026rsquo;s override system to inject custom styles without touching the theme directory at all. This gives us full control over the theme and even allows for future post-specific customization.\nThe Override Pattern Hugo\u0026rsquo;s asset pipeline has a well defined load order. Files in your project root take precedence over files in the theme. PaperMod specifically exposes two hooks for this:\nassets/css/extended/: any .css file dropped here gets automatically concatenated into the final stylesheet after PaperMod\u0026rsquo;s base styles.\nlayouts/partials/extend_head.html: gets included in \u0026lt;head\u0026gt; on every page, intended for custom fonts, meta tags, or third-party scripts.\nThis means a full theme restyle requires just two files:\nassets/css/extended/terminal.css # all CSS overrides and animations layouts/partials/extend_head.html # Google Fonts import(s) The theme submodule stays at its original commit, unmodified. Running git submodule update in the future will never touch these customizations because we aren\u0026rsquo;t reliant on any upstream theming, only the underlying structure of Hugo and PaperMod are necessary!\nImplementation extend_head.html just pulls in the fonts with one line:\n\u0026lt;link href=\u0026#34;https://fonts.googleapis.com/css2?family=Share+Tech+Mono\u0026amp;family=Orbitron:wght@400;700;900\u0026amp;family=VT323\u0026amp;display=swap\u0026#34; rel=\u0026#34;stylesheet\u0026#34;\u0026gt; Then, the CSS file overrides PaperMod\u0026rsquo;s CSS variables at the :root level; this itself cascades through the entire theme automatically because we override at root. Setting --theme, --entry, --primary, and --border to your own values replaces PaperMod\u0026rsquo;s color scheme without targeting individual components:\n:root { --theme: #020a04; --entry: #040d06; --primary: #00ff41; --border: rgba(0, 255, 65, 0.18); } From there it\u0026rsquo;s standard CSS-typography overrides, animation keyframes for the scanline effect, syntax highlight class overrides for Chroma. One thing worth noting here is how Hugo\u0026rsquo;s syntax highlighter outputs inline styles by default, which makes it impossible to override via CSS. To fix this, we can set the following in hugo.toml:\n[markup.highlight] noClasses = false This switches Chroma to class-based output, so .chroma .k, .chroma .s, etc. become targetable in the stylesheet.\nResult Three files added to the repo root with the theme submodule untouched:\nassets/css/extended/terminal.css layouts/partials/extend_head.html hugo.toml # updated with noClasses = false and params The site rebuilds on next deploy with the full theme applied and I like to think I did a decent job with my theme; feel free to out-do me yourself though if you think you can do better! ;)\n","permalink":"https://c2thulhu.sh/posts/adding-some-theming-and-aesthetics-to-this-blog-with-a-custom-theme/","summary":"Restyling the PaperMod Hugo theme into a green-on-black terminal look with Hugo\u0026rsquo;s override hooks, without forking or editing the theme.","title":"Adding My Own Theme To This Blog!"},{"content":"Competitive programming often reduces algorithmic efficiency to Big O notation. While theoretically sound, Big O ignores the constants, interpreter overhead, and memory allocation patterns inherent in actual execution. A solution might be O(N) on paper but fail runtime constraints due to Python\u0026rsquo;s specific implementation details.\nI developed Occam to bridge the gap between theoretical complexity and hardware reality. The goal was to build a local profiling tool capable of analyzing solution behavior under stress before submission to platforms like LeetCode.\nDynamic Analysis: Empirical Benchmarking The primary component of Occam is a dynamic analyzer that measures runtime behavior relative to input size (N). Rather than running a single test case, the tool executes the target function against procedurally generated datasets of increasing scale.\nImplementation The analyzer utilizes Python\u0026rsquo;s timeit module for precision clocking and tracemalloc to snapshot peak memory usage. By collecting data points across a range (e.g., N=10 to N=10,000), we establish a dataset representing the algorithm\u0026rsquo;s growth curve.\nTo determine the complexity class programmatically, Occam performs a linear regression on the log-log plot of Time vs N. The resulting slope approximates the polynomial degree:\nSlope approx. 1: Linear O(N) Slope approx. 2: Quadratic O(N^2) Slope approx. 0: Constant O(1) This allows for the validation of algorithmic assumptions against actual hardware performance.\nStatic Analysis: CPython Internals Runtime metrics can sometimes mask inefficient implementation details. To address this, Occam includes a static analysis engine that inspects the raw CPython bytecode using the dis module.\nBytecode \u0026amp; Instruction Counting The tool disassembles the function object to count specific bytecode instructions. This reveals hidden costs, such as implicit loop overheads or expensive global lookups, that high-level Python syntax obscures.\nCyclomatic Complexity Occam calculates the Cyclomatic Complexity number by analyzing the Control Flow Graph (CFG). It iterates through the bytecode to count branching instructions (POP_JUMP_IF_FALSE, FOR_ITER, etc.). This metric provides a quantitative measure of code maintainability and branch density, independent of execution time.\nBattle Mode: Comparative Profiling Optimization is often about trade-offs. To facilitate A/B testing, I implemented a \u0026ldquo;Battle Mode\u0026rdquo; that accepts multiple script files.\nThe tool executes all provided solutions against the exact same generated input vectors. It then overlays the performance graphs and outputs a statistical comparison. This allows for direct analysis of different approaches—for example, comparing the memory overhead of a Recursive DFS against the time complexity of an Iterative BFS—to determine which implementation is optimal for a specific constraint set.\nVisualization Raw numerical data can be difficult to interpret at a glance. Occam integrates scipy for statistical smoothing and matplotlib to generate complexity graphs. These visualizations display not just the average runtime, but also the standard deviation (stability) and peak memory consumption at each interval.\nConclusion Understanding code requires more than looking at the syntax; it requires understanding how that syntax executes on the machine. Occam provides the tooling necessary to dissect Python solutions, offering both the theoretical confirmation of static analysis and the practical reality of dynamic stress testing.\nSource code: https://github.com/antonhibl/occam\n","permalink":"https://c2thulhu.sh/posts/writing-a-pythonic-complexity-scanner/","summary":"Occam, a local Python profiler that combines empirical benchmarking, log-log regression and CPython bytecode analysis to show how a solution really scales.","title":"Writing a Pythonic Complexity Scanner"},{"content":" Update (2026-11-07): This site has since moved from the Caddy server described below to Cloudflare Pages. The Caddyfile and the cron deploy script are no longer in use; the security headers now live in a _headers file in the site\u0026rsquo;s repository. The rest of this post is kept as originally written.\nThe objective for c2thulhu.sh was to establish a publishing platform with a minimal attack surface. Traditional Content Management Systems (CMS) often introduce unnecessary complexity, maintenance overhead, and a broad landscape of potential vulnerabilities.\nTo mitigate these risks, I implemented a \u0026ldquo;Secure-by-Design\u0026rdquo; architecture that prioritizes static content delivery, strict transport security, and minimized server-side dependencies. This post details the transition from a local Emacs workflow to a hardened, Go-based production environment.\nThe Architecture The design philosophy relies on three core principles: Zero dynamic server-side processing, minimized dependencies, and strict edge defense.\nThe stack consists of:\nLocal Machine: Emacs + Org-Mode for content generation. Static Generator: Hugo (Extended) to compile Markdown into pure HTML/CSS. Transport: Git (GitHub) as the source of truth. Web Server: Caddy (written in Go) replacing Nginx. Edge Defense: Cloudflare (Strict SSL + DDoS protection). The Local Workflow Content is generated locally using Emacs\u0026rsquo; Org-Mode. To enforce schema consistency and streamline frontmatter generation, I utilize a custom Emacs Lisp function. This eliminates manual formatting errors and standardizes the drafting process:\n(defvar my/hugo-content-dir \u0026#34;~/Projects/MySecBlob/content-org/\u0026#34; \u0026#34;The directory where new post files will be created.\u0026#34;) (defun my/hugo-new-post (title section tags) \u0026#34;Create a new Org file for a Hugo post by inserting text directly. Prompts for Title, Section, and Tags.\u0026#34; (interactive (list (read-string \u0026#34;Post Title: \u0026#34;) (read-string \u0026#34;Section (default \u0026#39;posts\u0026#39;): \u0026#34; nil nil \u0026#34;posts\u0026#34;) (read-string \u0026#34;Tags (comma separated): \u0026#34;))) (let* ((clean-title (downcase title)) (slug (replace-regexp-in-string \u0026#34; \u0026#34; \u0026#34;-\u0026#34; clean-title)) (slug (replace-regexp-in-string \u0026#34;[^a-z0-9-]\u0026#34; \u0026#34;\u0026#34; slug)) (slug (replace-regexp-in-string \u0026#34;-+\u0026#34; \u0026#34;-\u0026#34; slug)) (filename (concat (file-name-as-directory (expand-file-name my/hugo-content-dir)) slug \u0026#34;.org\u0026#34;)) (date (format-time-string \u0026#34;%Y-%m-%d\u0026#34;))) ;; Create + Open the file (find-file filename) (erase-buffer) ;; Ensure it\u0026#39;s empty if I just created it ;; Insert File-Level Headers (insert \u0026#34;#+HUGO_BASE_DIR: ../\\n\u0026#34;) (insert (format \u0026#34;#+HUGO_SECTION: %s\\n\\n\u0026#34; section)) ;; Insert Headline manually (Safer than org-insert-heading) (insert (format \u0026#34;* DRAFT %s\\n\u0026#34; title)) ;; Insert Properties Drawer manually (insert \u0026#34;:PROPERTIES:\\n\u0026#34;) (insert (format \u0026#34;:EXPORT_FILE_NAME: %s\\n\u0026#34; slug)) (insert (format \u0026#34;:EXPORT_DATE: %s\\n\u0026#34; date)) ;; Only insert tags if I actually typed some (when (not (string= tags \u0026#34;\u0026#34;)) (insert (format \u0026#34;:EXPORT_HUGO_TAGS: %s\\n\u0026#34; tags))) (insert \u0026#34;:END:\\n\\n\u0026#34;) ;; Save and position cursor (save-buffer) (message \u0026#34;Created new post: %s\u0026#34; filename))) ;; Bind it to a key (C-c M-n) for speed (global-set-key (kbd \u0026#34;C-c M-n\u0026#34;) \u0026#39;my/hugo-new-post) This ensures every post follows the same strict schema before it ever hits the compiler.\nDependency Management A compatibility mismatch arose between the operating system\u0026rsquo;s default repositories and the theme requirements. The PaperMod theme requires Hugo v0.146+, while standard repositories often serve outdated binaries (e.g., v0.131).\nTo resolve template rendering errors, the environment was configured using the latest Hugo Extended binary, manually deployed to ensure support for advanced SCSS processing features.\nwget https://github.com/gohugoio/hugo/releases/download/v0.146.0/hugo_extended_0.146.0_linux-amd64.deb dpkg -i hugo_extended_0.146.0_linux-amd64.deb Web Server Configuration Caddy was selected over Nginx for its memory safety (Go) and concise configuration syntax. However, a specific challenge exists when provisioning SSL behind a reverse proxy.\nSince Cloudflare proxies traffic, Caddy cannot complete the standard ACME challenge for Let\u0026rsquo;s Encrypt. This results in SSL handshake failures (ERR_SSL_VERSION_OR_CIPHER_MISMATCH).\nThe Solution: Cloudflare Origin Certificates To resolve this, I implemented Cloudflare Origin Certificates. This establishes a strict chain of trust:\nClient to Edge: Publicly trusted Cloudflare SSL. Edge to Origin: Authenticated via the Origin Certificate (valid for 15 years). This configuration allows Caddy to serve secure traffic without external validation requests. The resulting Caddyfile is minimal and includes hardened security headers:\nc2thulhu.sh:443, www.c2thulhu.sh:443 { root * MySecBlob/public file_server tls \u0026lt;redacted\u0026gt;.pem \u0026lt;redacted\u0026gt;.key { protocols tls1.2 tls1.3 ciphers TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 } header { Strict-Transport-Security \u0026#34;max-age=31536001; includeSubDomains; preload\u0026#34; X-Frame-Options \u0026#34;DENY\u0026#34; X-Content-Type-Options \u0026#34;nosniff\u0026#34; Referrer-Policy \u0026#34;strict-origin-when-cross-origin\u0026#34; Content-Security-Policy \u0026#34;default-src \u0026#39;self\u0026#39;; script-src \u0026#39;self\u0026#39; \u0026#39;unsafe-inline\u0026#39;; style-src \u0026#39;self\u0026#39; \u0026#39;unsafe-inline\u0026#39;; img-src \u0026#39;self\u0026#39; data:; object-src \u0026#39;none\u0026#39;; base-uri \u0026#39;self\u0026#39;; form-action \u0026#39;self\u0026#39;; frame-ancestors \u0026#39;none\u0026#39;;\u0026#34; Permissions-Policy \u0026#34;camera=(), microphone=(), geolocation=(), payment=(), usb=(), vr=()\u0026#34; X-Permitted-Cross-Domain-Policies \u0026#34;none\u0026#34; Cross-Origin-Opener-Policy \u0026#34;same-origin\u0026#34; Cross-Origin-Embedder-Policy \u0026#34;require-corp\u0026#34; Cross-Origin-Resource-Policy \u0026#34;same-origin\u0026#34; -Server } } Automating Deployment To maintain synchronization between the repository and the production environment, a bash-based deployment script handles the build lifecycle:\n#!/bin/bash # Define the project directory PROJECT_DIR=\u0026#34;/var/www/MySecBlob\u0026#34; # Log everything to a file so I can debug if it breaks LOG_FILE=\u0026#34;/var/log/blog_update.log\u0026#34; { echo \u0026#34;==========================================\u0026#34; echo \u0026#34;Starting update: $(date)\u0026#34; # Go to the project directory cd \u0026#34;$PROJECT_DIR\u0026#34; || exit # Pull the latest changes from Git # I use reset --hard to force your server to match GitHub exactly echo \u0026#34;Pulling from Git...\u0026#34; git fetch --all git reset --hard origin/main git submodule update --init --recursive # Rebuild the Hugo site # This generates the new HTML in the /public folder echo \u0026#34;Building Hugo site...\u0026#34; /usr/bin/hugo --minify # Restart Caddy to serve the new files # (Restart is safer than reload here to ensure SSL certs are loaded correctly) echo \u0026#34;Restarting Caddy...\u0026#34; systemctl restart caddy echo \u0026#34;Update Complete: $(date)\u0026#34; echo \u0026#34;==========================================\u0026#34; } \u0026gt;\u0026gt; \u0026#34;$LOG_FILE\u0026#34; 2\u0026gt;\u0026amp;1 This script is executed via cron twice daily (0 0,12 * * *), ensuring the site remains current without manual intervention.\nConclusion This architecture delivers a highly performant platform with negligible hosting costs and a significantly reduced threat landscape. By removing server-side dynamic processing and database dependencies, common web vulnerability classes are effectively mitigated.\nFor documentation and technical writing, a static architecture offers the optimal balance of security and maintainability.\n","permalink":"https://c2thulhu.sh/posts/building-a-simple-secure-blog/","summary":"How c2thulhu.sh was built for a minimal attack surface: an Org-mode writing workflow, Hugo, Caddy behind Cloudflare with origin certificates, and hardened security headers.","title":"Building a Simple \u0026 Secure Blog"},{"content":"I\u0026rsquo;m Anton Hibl. This is where I write about security, systems and software: the tools I build, the projects I am working on, and even info how this site itself is put together.\nElsewhere GitHub: github.com/antonhibl LinkedIn: www.linkedin.com/anton-hibl HackTheBox: www.hackthebox.com/ahibl RSS: c2thulhu.sh/index.xml for new posts ","permalink":"https://c2thulhu.sh/whoami/","summary":"About Anton Hibl and c2thulhu.sh","title":"whoami"}]