blog.iankulin.com

Posts

VS Code Dev Containers

In this walkthrough, I set up a VS Code Dev Container, beginning with my use of Remote-SSH on homelab LXCs and my goal of running AI coding agents like Claude Code in an isolated, easily recreated environment. I cover writing a Dockerfile and devcontainer.json, how the extension automatically handles the VS Code server and workspace bind mounts, and the difference between UI and workspace extensions, including declaring them in the config for reproducible setups. I leave handling SSH keys for pushing code to a future post...

Getting Ghostty to Work on Synology

Trying the Ghostty terminal, I hit a problem over SSH to a Synology NAS: arrow-key history recall and clear don’t work, because Ghostty sets TERM to xterm-ghostty and Synology lacks the matching terminfo. The practical fix is overriding TERM to xterm-256color via an ssh config SetEnv entry, which can be wildcarded across multiple hosts. The terminfo installation method Ghostty officially recommends fails on Synology’s minimal DSM...

State of AI tooling (for me)

A follow-up to my earlier article on AI-assisted coding, describing my move from autocomplete tools to agentic coding with Cline, Claude Code, and Gemini CLI. I cover token costs, comparisons between Claude, OpenAI, DeepSeek, and Gemini models, and practical tips such as plan-then-act workflows, guardrails, and keeping context lean...

Writing a Browser Extension

I walk through building a simple Firefox extension that adds a right-click menu option to open an image in a new tab with its query parameters stripped, useful when CDNs serve resized or converted versions instead of the original. I cover the manifest, permissions, and background script, plus how to test the extension via about:debugging and enable it in private windows, with brief notes on publishing to the Add-ons store...

End to end testing - Cypress basics

A walkthrough of end-to-end testing a web app with Cypress, which I demonstrate against a small Express app with customers and orders. I cover installing Cypress, writing tests for navigation, deletions and cascading deletes, element selection with data-test attributes, custom assertions using invoke and then, running tests in the browser, and handling state resets between runs...

Express router for better code organisation

In this beginner-level walkthrough, I tidy up a Node/Express app whose server.js has grown unwieldy by moving routes into separate files with Express Router. I show how to mount routers as middleware with app.use, note that a routes folder is a common convention, and highlight the gotcha that the mounted path prefix gets stripped from the request URL. A consistent naming convention for route groups makes the split easier, and sample code is available on GitHub...

Functional Javascript array methods

I take a look at JavaScript’s reduce() method, walking through how the accumulator works with examples like summing and finding the maximum in an array, and explaining why reduce counts as a “functional” array method...

Moving a domain from Wordpress

A step-by-step guide to moving my domain registration from WordPress.com to Porkbun, motivated by my unease over recent WordPress drama. I cover the transfer process—including a required 60-day wait, disabling the transfer lock, and a gotcha where private registration had to be turned off before the authorization code would work—plus copying DNS records and switching nameservers. In a closing update, I note that WordPress later blocked external domain pointing, pushing me toward self-hosting...

Clear explanation of how transformer AI works

I recommend Ishan Anand’s YouTube series Spreadsheets are all you need, which teaches how generative AI works using an Excel spreadsheet implementing most of GPT-2. I highlight Anand’s teaching ability, note that the featured lessons are the first three of a paid course, and link to a downloadable version of the spreadsheet...

Authentication basics for Node apps

I walk through building user authentication in an Express.js app from the ground up, starting with how HTTP requests, cookies, and sessions work before moving to session IDs and the express-session library. I then cover a login flow with bcrypt-hashed passwords and file-based session persistence, laying groundwork for a future post that will use Passport.js...

LLM coding question comparison using Ollama

Having downloaded a pile of large language models to run locally with Ollama on my M1 MacBook, I compare how several of them (codeqwen, deepseek-coder, phi3, dolphin-mistral, and llama3 at different quantisation levels) answer a Docker question about CMD versus ENTRYPOINT. I time each response and judge it subjectively, with caveats that the test is unscientific and benchmark charts usually reflect full-size models on expensive hardware. My writeup includes a layman’s explanation of quantisation, brief notes on each model’s character, and I conclude that llama3 and most others gave usable answers while phi3 fell short...

dockerfile - CMD vs ENTRYPOINT

I offer a short explainer on the difference between ENTRYPOINT and CMD at the end of a Dockerfile, with examples showing that command-line arguments replace CMD entirely but are appended to ENTRYPOINT. I also cover combining the two, and note that both execute at container launch, unlike RUN, which happens during the image build...

Using LLMs for coding

I survey the rise of AI coding tools, from ChatGPT and GitHub Copilot to open source ecosystems, alongside skeptic concerns about training data, code quality, model collapse, and code exfiltration. I explain why these assistants suit my situation as an experienced developer new to modern languages, and trace my journey from paid Copilot to the free Codium and finally to running local models via Ollama and the Continue VSCode extension to keep client code private...

Virtual Hosts on "Static Web Server"

My VPS runs NGINX Proxy Manager, which offers no way to serve static virtual hosts, so I walk through running Static Web Server—a lightweight Rust-based web server—in Docker alongside it. I cover the directory layout, a docker-compose setup, and the config.toml virtual host entries, with the result verified using Tailscale addresses as stand-in hostnames...

Deploying a Node app in Docker

I walk through turning my small Node.js Markdown server into a Docker image, explaining the Dockerfile line by line, building the image locally, and pushing it to Docker Hub. I finish with a sample docker-compose file so self-hosters can deploy the app with a single command...

Hosting Your Own Docker Registry

I walk through hosting a private Docker registry on my homelab network using Docker’s official registry image, motivated by the free tier’s single-repository limit and Docker Hub’s pull/push rate limits. I cover setting up the registry with docker-compose, building a small test image, and pushing it to the registry without TLS by adding an insecure-registries exception to daemon.json. A third machine pulls and runs the image to confirm it all works...

Certbot - removing a domain

After I moved a domain to a new host, the original certbot certificate on the old host still included it, causing renewal errors. I explain that domains can’t be deleted from a certificate directly; instead you renew the certificate specifying only the domains you want to keep, and certbot warns about the ones being dropped. I include the commands for listing certificates and their domains, and for renewing with a reduced domain set...

Beginning Node App Security

I cover simple steps to secure a Node.js web app exposed to the public internet on a VPS, including putting it behind an Nginx reverse proxy with basic auth, enforcing HTTPS, and using Fail2ban to automatically ban brute-force attempts. I also cover restricting ports via a cloud firewall, disabling root SSH login, using SSH keys, keeping the system updated, and basic monitoring with Uptime Kuma...

User Sessions & Cookies in Node

In this tutorial, I build a user login and session management system from scratch in Node and Express, starting with a simple cookie-based session counter and progressively adding file-based persistence, user accounts, logout, and password authentication using bcrypt. I also cover input sanitisation, secure cookies, and enforcing HTTPS behind an NGINX proxy. The tutorial is aimed at readers with basic familiarity with Node and Express...

Fly.io, Uptime Kuma & scraping a status page

Because my Uptime Kuma monitoring instance runs on a home network sharing the same flaky 4G connection as the VPS sites it watches, I consider Uptime Robot before deploying a second instance on Fly.io’s free tier. Setting it up as a Docker container proves simple, and to fold the remote status back into my local instance I discover Uptime Kuma already exposes an SVG status badge usable with keyword monitoring, avoiding a custom scraping endpoint...