An empty development site can make a product look healthier than it is. Before the next Bonumark Stream build, I wanted dev.bonumark.org populated, tested, and backed up well enough to expose real failures before they reached a release candidate.
The Dev Site Needed to Become Useful Again
Bonumark Stream had already reached v0.8.1, and I knew I would be returning to active development this week.
The problem was that I did not want the first development session to turn into hours of preparation.
I did not want to wonder whether the files on the VPS matched GitHub. I did not want an empty Stream that made every layout look artificially clean. I did not want to rebuild a Profile every time I needed to test it. I did not want ActivityPub testing to begin with setting up another remote relationship.
I wanted a known starting point.
The requirement was simple: when I opened dev.bonumark.org to start the next Bonumark pass, the environment needed to already contain enough real application state to test the product properly.
That meant source, database state, content, media, Profile data, local interactions, federation, scheduling, responsive stress cases, and a rollback path all had to be part of the baseline.
First, Prove the Source Baseline
Before changing the development site, I had the repository and VPS state inspected independently. The baseline was clear.
Both main and develop pointed to:
2d31fcf0f72c297f7bf501e26a4deac96f6c2980
That is Bonumark Stream v0.8.1.
There was no difference between the long-lived branches, so there was no ambiguity about which code the development environment should be running.
The VPS installation was already almost completely current. The only missing package-managed file was the GitHub compatibility workflow. That exact file was restored from the verified repository source.
After synchronization, all 337 release-manifest-managed files matched. All 338 tracked files matched the clean source checkout.
There were no intentional source deviations.
That matters because the next time a test fails on dev, I do not have to begin by asking whether the server is running some half-forgotten development copy.
I now know exactly what the starting source is.
Preserve the Installation Before Filling It With Test Data
I had already decided that I was willing to wipe and reseed the dev database if the existing data was useless.
But I did not want “cleaning up dev” to accidentally destroy something that was actually part of the application’s identity.
ActivityPub made that especially important.
The existing actor identity, signing key, Admin identity, installation configuration, migration ledger, federation relationships, and hostname-specific settings all needed to be treated differently from disposable test posts.
The inspection showed that the existing database was not bad enough to justify a full wipe. There was useful federation history and a small amount of existing content, so I chose to preserve the installation and expand it instead.
The ActivityPub identity and existing signing key stayed intact. Existing follower and following relationships stayed intact, and the hashes of the Bonumark configuration and installation lock did not change.
All 28 current database migrations were already recorded, with nothing pending. That gave me a clean application baseline without pretending the development database had no history.
Build Fixtures That Can Actually Break Things
The next step was the part I cared about most.
I did not want 40 copies of “test post.”
I wanted a Stream full of cases that make bad assumptions visible.
The final fixture set contains 44 published Stream posts, enough to force three pagination batches at the current 20-post page size.
There are very short posts, normal posts, long posts, multiple paragraphs, repeated line breaks, Unicode, emoji, punctuation-heavy text, long URLs, multiple links, internal links, external links, long strings that stress wrapping, and deliberately searchable phrases.
Media coverage includes image-only publishing, portrait media, landscape media, square media, and two-, three-, and four-image galleries.
Alt text and captions are populated.
The Profile has an avatar, cover image, additional photos, display name, headline, biography, location, website, About text, Now text, interests, a Featured item, and profile links. The fixture set also includes drafts, a future scheduled post, a post successfully published from the scheduler, revision history, a trashed post, a post restored through the normal Admin workflow, a pinned post, a Page, a Local Place, local comments and Likes, and known search phrases with expected results.
That gives me something far more valuable than filler content: repeatable test cases.
Federation Needed to Be Part of the Baseline
Bonumark is no longer just a local publishing application.
ActivityPub is now part of the product, so a useful dev environment needs real federation state.
I did not want to blast dozens of fixture posts at remote servers just to populate dev, so the local fixture set was created without turning the whole exercise into federation spam.
Once the local environment was ready, federation was reactivated and tested deliberately.
The local actor remained:
@jimlunsford@dev.bonumark.org
WebFinger and the ActivityPub actor document both worked. The existing RSA signing key passed its decrypt, sign, and verify self-test.
The development actor already had an accepted relationship with my Mastodon account, @disciplinedoperator@mastodon.social, so there was no reason to destroy a working relationship merely to repeat the Follow ceremony.
I published one deliberate federation test post. Bonumark created three delivery attempts for its existing followers, and all three succeeded on the first attempt with HTTP 202 responses. The post appeared remotely on Mastodon, where I replied to it. The reply reached Bonumark, entered moderation, was approved through Bonumark Admin, and appeared publicly in the Conversation.
I also sent a remote Like. Bonumark received it and showed it in the owner-facing fediverse reaction area.
That gave the dev environment something I have wanted for future development work: a stable post with both local and remote interaction evidence attached to it.
Testing Found Exactly the Kind of Problems I Wanted Dev to Find
This pass was not valuable because everything passed.
It was valuable because realistic data exposed weaknesses that an empty installation would not have shown.
The clearest one is the public comment-count contract.
The federation test post has one approved, publicly visible remote reply. The Conversation says “1 Comment,” while the permalink action pill says “0 Comments.”
That is a core product problem. Different parts of Bonumark are counting different things.
Local comments exposed a related problem. After submitting a local comment, the Conversation count can update immediately while the action pill remains stale until the page reloads.
Remote Likes expose another separation. Bonumark receives and records the remote Like, but it does not feed the same public count used for local Likes.
These are not theme problems. They are application-state and aggregation problems.
The new fixture environment now gives me reproducible cases for fixing them correctly.
Publishing Failures Exposed a Bigger UX Problem
Another test found something I consider more serious.
An emoji-rich post caused publishing to fail with MariaDB error 1366 while Bonumark was generating title or description metadata.
The database environment reported utf8mb4, so I do not yet have enough evidence to claim the exact root cause.
But the failure exposed a second problem that matters even more from the user’s point of view.
When publishing failed, the composer could lose the text the user had entered.
That means the real defect is bigger than one emoji triggering a database failure.
It is also:
A publishing failure can destroy the author’s unsaved work.
That is the kind of problem I want development fixtures to uncover.
I do not want someone writing a real post to discover it first.
Media Testing Found Another Bad Failure Path
Image metadata testing found a similar problem.
A reasonably sized alt-text value saved normally.
An approximately 270-character alt-text value did not.
Instead of clearly telling the user that the value was too long and preserving what they entered, Bonumark produced a generic update failure and discarded the attempted value.
Again, the useful finding is larger than the exact limit.
The application needs explicit validation and recovery behavior when an input is invalid.
A user should not have to discover a hidden constraint by losing what they typed.
Media inspection also found that usage reporting can miss images that are visibly attached to published galleries. That is less urgent than content loss, but it is now another concrete test case waiting for the next development pass.
The Environment Had to Be Safe Too
A populated development environment is not useful if preparing it weakens the VPS.
The work preserved Bonumark’s dedicated PHP-FPM identity: bonumark-dev:bonumark-dev. The site remained isolated from unrelated web roots, and no 777 permissions were introduced.
SSH remained key-only with root login disabled. UFW remained limited to the expected SSH, HTTP, and HTTPS ports, and Fail2ban remained active.
The development database account remained limited to the development database, and private application paths continued to return intentional denial responses.
The environment also received a development-only indexing header:
X-Robots-Tag: noindex, nofollow, noarchive
That keeps the site publicly reachable for ActivityPub and realistic browser testing without inviting normal search indexing.
TLS remained on the established Certbot architecture, and the existing scheduled-task mechanism remained intact. System Check finished with 39 passing, 0 warnings, and 0 failures.
That is a much stronger signal than simply seeing the homepage return HTTP 200.
Backups Were Part of the Acceptance Test
I also wanted both sides of the work preserved.
Before changing the environment, the VPS-wide backup system created a rollback snapshot:
7baed34e46cbe5b84cbdfdf563f89b0364eec4d8ead0813cc19d52c51c0e7e07
After the work was complete, the populated baseline was captured in:
3c455224b93e0ffc6ac9d592198725d5a993f1e550a2fcf88f7c8fd524927ceb
The Bonumark dev database appeared in dynamic database discovery and in the Restic snapshot. The final SQL dump was recovered from Restic and matched the local backup byte for byte by SHA-256. Representative application, configuration, TLS, PHP-FPM, Nginx, and media files were also restored and compared.
I did not perform a complete SQL import into a disposable MariaDB database during this pass, so I am not claiming that as tested. That distinction matters. The backup evidence proves that the finished environment exists in the off-server backup and that representative recovery works. It does not magically turn this readiness pass into a complete disaster-recovery drill.
The Laboratory Is Ready
The result is not just a cleaner dev site. I now have a known Bonumark development baseline with an exact source commit and a known migration state.
I have 44 posts covering multiple content and media boundaries, a completed Profile, drafts, scheduling, revisions, trash, restoration, Pages, Local Places, comments, Likes, galleries, search fixtures, and pagination. I also have a real federated relationship and a test post with a remote reply and remote Like attached to it.
System Check is clean. I have pre-change and post-change recovery points, and I have several real product defects waiting for the next development pass.
There are two things I am intentionally not claiming. First, I am not claiming exact 390 × 844 or 360 × 800 phone acceptance because the browser available during this work could not set those exact viewports. Second, I am not claiming a complete SQL-import restore test. Those can be tested when the work calls for them.
The important part is that I no longer have to begin the next Bonumark session by building the laboratory.
Now I can start breaking things on purpose.
New Here?
Read Next:
- Why I Built Bonumark Stream
- Builder Receipt: Bonumark Hosting Portability
- Builder Receipt: Bonumark’s Theme Architecture