LUXII: Building a Site With SEO Automation Tools From Zero

LUXII: Building a Site With SEO Automation Tools From Zero

Rex Jones··2 min read

LUXII was one of those sites that technically existed and practically didn't. Two blog posts, both old enough that I'd forgotten writing them. No keyword research on file. A handful of categories that looked more like leftovers from a setup wizard than an actual content plan. It was live, it had a domain, it just didn't have anything behind it yet.

So the fix wasn't "write a post." It was "figure out what this site is actually supposed to be about, then build the shelf before putting anything on it."

How much research is actually enough to start

I pulled real keyword data before writing a single headline. Not because I love spreadsheets, but because guessing at topics for a site with zero history is the fastest way to spend a month writing content nobody was searching for. That research turned into 117 real keywords sitting in the site's keyword bank, sorted by volume and intent, which is a lot more than I expected to need for what's still a small site.

The honest answer to "how much keyword research do you need before you start" is: enough that every post you write has a reason to exist beyond "I felt like writing something today." For LUXII that meant six new categories built from actual demand instead of vibes, and a full backfill plan running from the site's real July 11 launch date through the end of the year. Not because weekly content is magic, but because a content calendar with actual gaps in it is worse than no calendar at all — it just tells visitors and search engines that something started and stopped.

The bug that had nothing to do with any of that

While I was in there, I tried to log into the admin panel and couldn't. Wrong password, except it wasn't the wrong password — it was the right one, sitting in Coolify's environment variables exactly where it should be. The site just didn't agree with it anymore.

That's a specific kind of annoying. Not a "did I typo something" bug. A "two systems that are supposed to describe the same fact have quietly drifted apart" bug. The environment variable said one thing. The database said something else. Neither one was corrupted, they'd just stopped matching, probably from an earlier reset that touched one and not the other.

LUXII hashes passwords with PBKDF2-SHA512 through the Web Crypto API — 100,000 iterations, a 64-byte derived key, stored as a salt and hash pair joined by a colon. That's a solid scheme, and it's also not something you can eyeball. I couldn't just look at the stored hash and know whether it matched the password I had. I had to prove it.

So I wrote a small Python script that replicates the exact same hashing math using hashlib.pbkdf2_hmac, fed it the known-good password from Coolify, and generated a hash using the same salt already stored in the database. It didn't match the existing hash. That confirmed the drift — the database's stored hash belonged to some password that wasn't the one in the environment variables anymore.

The fix was smaller than the diagnosis. Once I had a hash I could prove was correct for the known password, I updated the database row directly. No password reset flow, no "forgot password" email, no need to invent a brand new credential and update it in three different places. Just replace the one value that had drifted with one that was provably right.

What actually stuck with me

Neither of these problems was hard, exactly. The keyword research was patient work, not clever work. The password bug was a five-minute fix once I understood it — the hour before that was all diagnosis. But both of them are the kind of thing that's invisible when a site is working and completely blocking when it isn't, and I think that's most of what running a handful of sites actually is. Less heroic debugging, more "notice the small thing before it becomes the reason nothing works."

LUXII has a real content engine now and a login that works. Neither of those facts is exciting on their own. Together they're the difference between a site that's alive and one that's just parked.