What started as a practical effort to replace an aging CityDesk-based workflow has become something much more substantial: a modern, server-authoritative publishing system with desktop and browser authoring, deterministic publication, immutable releases, protected backups, recovery procedures, and a production process we can actually trust.
The site is no longer dependent on obsolete software or the old iPage hosting workflow. EZ-Blog is now running on our own hosted architecture, backed by SQLite, published through a controlled pipeline, monitored continuously, and protected by both server-side and off-host backups.
That is a satisfying place to arrive.

What We Accomplished
The original goal was simple: make it easy to write a new article, preview it, publish it, and update the site without depending on CityDesk.
We accomplished that goal, but we also built the foundation correctly.
EZ-Blog 2.0 now includes:
- A modular Java architecture separating publishing, persistence, desktop, and server concerns.
- SQLite as the authoritative source of content and publication state.
- A JavaFX desktop client for article creation and editing.
- Browser-based authoring that also works from a phone.
- Server-side owner authentication and scoped access tokens for desktop access.
- Private article previews before publication.
- Support for images and linked video content.
- Server-authoritative Menu and Template management.
- Deterministic publication scopes so changing one article does not unnecessarily rebuild the entire site.
- Immutable generated site releases, preserving prior versions for rollback and evidence.
- Publication verification before a new release becomes active.
- Explicit recovery procedures when selector, database, or publication state disagree.
- A hardened Lightsail deployment behind Caddy.
- Automated health monitoring.
- Daily qualified SQLite backups on the server.
- Daily off-host backup retrieval to my local external drive, including SHA-256 verification and retention.
- A production runbook that documents the actual system we successfully deployed.
- A tagged EZ-Blog 2.0 production baseline.
Most importantly, we successfully performed the real production migration.
The existing site remained available while the application was upgraded, the database moved through its schema migrations, a fresh production baseline was generated and verified, hosted authority was enabled, and the first article was successfully published through the new system.At that point, this stopped being a development experiment. It became the production publishing system.
The Architecture We Wanted
One of the most important decisions during the project was not to settle for something that merely worked. Publishing needed to be understandable and recoverable. A publication now creates a candidate from a known predecessor, changes only what the operation requires, verifies the resulting output, and promotes it only when the evidence agrees. Unchanged content can remain physically shared through hard links, while changed files receive new immutable versions. Previous completed runs remain untouched.
That gives us a useful property:
publication is additive rather than destructive.
The live site is simply a selector pointing at a verified immutable release. If something goes wrong, we have evidence about what happened and something known-good to return to. The same philosophy applies to persistence and backups. SQLite holds the authoritative content. Server backups are independently qualified. Off-host copies are hash verified before they are accepted.
We deliberately chose simple mechanisms where simple mechanisms were sufficient. That became increasingly important as the project progressed.
The Human–ChatGPT–Codex Ping-Pong
Another important result of this project was the development process itself.
We did not use AI as a one-shot code generator.
Instead, the process evolved into a deliberate three-way loop:
Human → ChatGPT → Codex → Human
My role remained ownership of the problem: goals, architecture, constraints, acceptance decisions, and judgment about whether the system was becoming better or merely more complicated.
ChatGPT acted primarily as architect, reviewer, and reasoning partner. We discussed alternatives, challenged assumptions, reduced unnecessary complexity, designed verification steps, interpreted failures, and turned architectural decisions into bounded implementation tasks.
Codex then operated at the repository level: inspecting the existing implementation, making focused changes, adding tests, running verification, and reporting exactly what changed.
Then the result came back for review.
Sometimes it passed.
Sometimes production behavior exposed an assumption the tests had missed.
When that happened, we did not paper over the failure. We returned through the loop:
observe → understand → simplify → change → verify
That ping-pong process was especially valuable during the production cutover. Several issues surfaced only when independent pieces of the architecture finally interacted under real production constraints: backup permissions, HTTPS boundary enforcement, health monitoring, Caddy forwarding behavior, and protected off-host retrieval.
Each problem improved the process because we treated production evidence as authoritative.
Quality Gates Instead of Optimism
A recurring principle became:
Do not assume a change works because the code looks correct. Prove the behavior that matters.
That meant using several levels of verification:
- focused unit and integration tests;
- full Maven regression runs;
- static analysis;
- manual desktop testing;
- real mobile-browser testing;
- disposable-host rehearsal;
- filesystem ownership and permission checks;
- database integrity checks;
- publication-state checks;
- SHA-256 verification;
- immutable predecessor comparisons;
- health/readiness verification;
- and finally real production publication.
Even our final backup automation was not accepted simply because a Scheduled Task existed. We manually invoked it, verified a zero exit result, and confirmed that a new qualified SQLite database actually appeared on the external drive.
That is the difference between automation and trusted automation.
Simplicity Became a Design Requirement
One of the strongest lessons from the project was that sophisticated engineering does not mean accumulating machinery.
In fact, several times we found ourselves moving toward mechanisms that were technically defensible but operationally unnecessary.
We stopped.
We simplified.
For example, we abandoned an overly elaborate cutover coordinator and authority-state mechanism in favor of a much clearer production sequence: backup, stop, migrate, generate, verify, select, start read-only, verify again, create the final recovery point, then explicitly enable writes.
That simpler model was easier to reason about, easier to test, and easier to recover.
The lesson was straightforward:
complexity is not evidence of rigor.
Often, eliminating unnecessary state, indirection, or automation increases rigor because there are fewer hidden interactions to fail.
A Better Definition of “Vibe Coding”
The phrase vibe coding is often used to describe giving an AI a rough idea and accepting whatever code comes back.
That is not the process we developed. What emerged here is a much stronger form of AI-assisted engineering.
The “vibe” is still important. There is enormous value in being able to explore an idea conversationally, iterate rapidly, ask “what if?”, try an implementation, reject it, and redirect without the traditional friction of translating every thought into a formal development artifact first.
But speed is only one half of the equation. Our version adds engineering discipline:
Intent + conversation + implementation + verification + human judgment.
The human defines the outcome and owns the architecture.
The reasoning model challenges and refines it.
The coding agent implements bounded changes.
Tests challenge the implementation.
Production evidence challenges the tests.
And the human decides whether the result is actually acceptable.
That combination has been remarkably effective.
So perhaps the better definition is:
High-quality vibe coding is rapid, conversational software development constrained by architecture, evidence, quality gates, and human ownership.
It keeps the creativity and velocity that make AI-assisted development exciting, while adding the disciplines that make software dependable.
That is the process we used to build EZ-Blog 2.0.
And now we have something better than a successful migration.
We have a repeatable way of building software together.
My Corner of the Web