How This Site Works, and Why It Exists

This is the first real post on this blog, so it should earn the slot. Not a project update. An explanation of the thing you are reading right now: what it is built from, why it exists at all, and what building it reminded me I still enjoy.
Why build a website again
I’ve done this before. Running FierceHound meant handling domains, hosting, DNS, development and content writing, often all on the same day, for the same client. That was the muscle. It has just been a long time since I used it.
For the last five years I’ve been a CTO in financial services, and what that role actually means day to day is closer to being a consultant to the business than a person writing code. I solve specific problems at a different scale: architecture decisions, vendor calls, cost and risk trade-offs, the occasional production fire. I still get my hands dirty plenty. I just don’t often get to take something from an empty folder to a live URL and own every call in between, and I missed it.
Two things made this project worth doing now rather than filed away as “someday.” The first was that gap. The second was wanting a real, sustained reason to work with AI tools on something end to end, not a five-minute demo but an actual build with production stakes, a database, and real users leaving real comments. That part alone has been worth the project.
What is genuinely new here, and not a returning skill, is using GitHub itself as the CMS. I’ll build a proper one eventually, as its own learning exercise, but working this way in the meantime has been its own small discovery. Cloudflare and Astro are new to me too. Enterprise work has always meant Azure; FierceHound ran on HostGator shared Linux hosting with Apache and cPanel. My comfort zone has always centred on .NET or PHP. None of that is anywhere in this stack, which is most of the point.
Step one, and why it is still live
sumitn.com, what I now call v1, was step one of this. A static site,
simpler by every measure, built to get something real onto the internet on
unfamiliar tooling and see what broke. It taught me the current basics:
this era’s domains and DNS, a first deploy on infrastructure I hadn’t
touched before, what “static” actually means once you have to explain it to
yourself again.
v1 is still live, deliberately, alongside this one. I could have replaced it. I didn’t, because the record of how I got here is part of what I am trying to build, the same reason the memoir on this site is organised around turning points rather than a polished highlight reel. A learning project that quietly erases its own earlier chapters has misunderstood the assignment. Two Cloudflare Workers, two zones, two repositories, both live, both honest about which one came first.
This site, v2, sumitnangia.com, is what step one turned into once I
understood enough to ask for more: a database, live data, a comment system,
a moderation workflow, an admin panel behind real authentication.
Everything below is what that turned into.
The stack, briefly
- Astro generates the site, mostly as static HTML at build time, with a handful of routes that opt into running on demand because they need to read a database or check a request.
- Cloudflare Workers serves it, at the edge, close to whoever is reading.
- Cloudflare D1 (a SQL database) holds everything that has to persist: testimonials, comments, contact messages, and per-post view counts.
- Cloudflare Turnstile stands in front of the comment form so it isn’t trivial to spam.
- Cloudflare Access sits in front of the admin panel, so there is no login page, no password, and no authentication code anywhere in this project for me to get wrong.
- GitHub holds all of it, and, this is the part worth a diagram, is also the publishing tool and the deployment pipeline.
Static by default, live where it matters
The single idea that shapes the most decisions here: pages stay static and fetch their live numbers from the browser. A blog post is a plain, cacheable HTML file. Its view count and its comment thread are not baked into that file. They are fetched by the reader’s browser, after the page has already loaded and rendered.
That sounds like a small technical preference. It isn’t. It means a slow or even fully broken database costs a number in the corner of the page, not the article itself. The reading experience and the live data are two different failure domains, on purpose.
The blog, and how a post actually gets published
There is no CMS behind this blog, and that absence is the deliberate part, not a corner cut (yet, see above). A post is a Markdown file with a small amount of frontmatter: title, date, a description, a category from a fixed list. The filename becomes the URL. Writing one, start to finish, is: copy an existing post, rename it, write in Markdown, commit.
That is also the entire publishing workflow, which is what the diagram below is showing.
GitHub plays two different roles here and both are load-bearing. As a
CMS, its web editor and mobile app are where a post gets written and
saved, no separate login, no separate app, and the full edit history of
every post is just git history, which I get for free. As CI/CD, a push
to main is the entire deployment trigger: Cloudflare picks it up, runs
the build, and the new version is live, usually inside a couple of minutes,
with no server for me to SSH into and no manual “publish” button anywhere.
The build itself does real work, not just a file copy. Posts are validated against a schema. A misspelled category fails the build with the valid options listed, rather than quietly creating a category of one with no page behind it. Images referenced from a post go through the same build-time optimisation pipeline as the site’s photographs, arriving as properly sized files rather than whatever JPEG I dropped in.
Comments, and the moderation model behind them
Comments are open by default, closable per post in that post’s own frontmatter, an editorial decision that lives with the writing and is versioned with it, rather than sitting in a database where it could be flipped in a panic. Nothing a reader submits appears publicly on its own. Every comment lands as pending and sits in a queue until I approve it in the admin panel. Rejecting doesn’t delete anything, it just changes a status, so every moderation call stays reversible.
The admin panel itself is three queues (testimonials, comments, and contact messages) behind Cloudflare Access rather than any login system of my own. I would rather depend on infrastructure built and hardened by people who do that for a living than write my own authentication and hope I got the edge cases right.
What “no tracking” actually means here
There is a privacy page linked in the footer, and I want what it says to be a claim I can stand behind rather than marketing copy. The view counter keeps exactly one row per post: a slug, a count, a timestamp. No IP address, no user agent, no cookie, no session id, and no row per visit. There is nothing in that table that identifies a reader, and nothing to mine later even if I wanted to. The counter increments from the reader’s own browser rather than from the server logging a request, which is also what keeps search-engine crawlers and link-preview bots out of the number. It’s an honest vanity counter, nothing downstream depends on it, and that’s the right amount of engineering for what it is.
The one place this site collects an email address deliberately is the contact form, and even that has a boundary built into it: outgoing mail can only ever be sent to one address, mine. If a bug or a bad actor ever reached that code path, the worst outcome is that I get an email. Small architectural decisions like that one are cheap to make up front and expensive to retrofit later, which is a lesson transformation work teaches you about a lot more than software.
That is where privacy stands as this is written. It will move as the site does, so the privacy page in the footer is the current statement. This section is a snapshot of August 2026, not the policy.
What building it actually reminded me of
The honest answer is: mostly small, invisible things that only show up once
a page is real and someone else is looking at it. A generic CSS selector
written for one part of the site quietly reaching into a component built
months later. An element marked hidden that stays visible anyway because
something else on the page is more specific about what display it should
have. A stylesheet that looks broken in the browser when the actual problem
is that the dev server never noticed the file changed underneath it.
None of these are dramatic. All of them cost real time, and all of them were only visible from the inside. It’s the same argument for doing end-to-end work at all, even now, even at a different altitude at the day job: some things only teach you anything once you’ve built them yourself.
What’s next
The blog you’re reading this on is genuinely young, this is close to the first real post. There’s a backlog of things worth writing about once they’ve been lived a bit longer: the FierceHound story properly told, some of the public-facing writing that used to live directly on the memoir and now belongs here instead, and probably a few more posts like this one, the plumbing, told honestly, because a career built on “creating order from chaos” should be able to explain its own website.
Comments
Loading the discussion…
Thank you — your comment has been received. I read every one before it appears, so it will not show up on the page straight away.