← Back to Blog
· Joshua

May 31 — Infrastructure Day

The last day of May turned into a full infrastructure push across every project: Symfony CVE patches, Dependabot, CI/CD pipelines live, and a round of feature work — Velvet Radio's schedule page, Hollow Press blog polish, Lunar Blood's dark mode, The Velvet Pulse's rate limiting, and Graveyard Jokes' Google Places fallback.

The last day of the month became an infrastructure day. Every project in the portfolio got touched. Some got features. All of them got security patches, Dependabot, and working CD pipelines. This is what that looked like.


Cross-Project — Symfony CVE Patches

Three Symfony CVEs dropped: CVE-2026-48736, CVE-2026-48784, and CVE-2026-46644. All of them affect components that every Laravel project in the portfolio pulls in transitively. The fix was a composer update targeting the affected packages — symfony/http-foundation, symfony/http-kernel, and related — across Graveyard Jokes, Hollow Press, Lunar Blood, Noteleks, SynthVeil, The Velvet Pulse, Velvet Radio, and the Auth System.

All eight projects are now on patched versions. The auth system had a slightly different set of CVEs (CVE-2026-45075, 45068, 45070, 45067) and was patched separately.


Cross-Project — Dependabot and CD Pipeline Fixes

Dependabot was added to every project. It will now open automated PRs when npm or Composer dependencies have updates available. This removes the manual tracking burden for dependency hygiene — instead of remembering to check, the project creates a PR.

The CD deploy scripts also got a consistent fix applied everywhere. The previous scripts included a block that tried to commit and push from the server after running migrations, which fails in a CI context where the working tree is clean and there is nothing to commit. The fix replaces that block with a git fetch origin main && git reset --hard origin/main pattern, which pulls the latest code without attempting any writes back to the remote. Clean, safe, and idempotent.


Velvet Radio — Schedule Page

Velvet Radio now has a /schedule page. Shows can be assigned a day of the week and a broadcast time via two new fields — schedule_day and schedule_time — added to the shows table in a migration.

The schedule page groups shows by day and renders them in order. The header and mobile nav both link to it. The episodes index page gains show filter pills: clicking a show name appends ?show=ShowName to the URL and filters the results. Both the shows listing and the schedule route use Cache::remember with a five-minute TTL. The admin ShowController exposes the schedule fields in the form and busts the cache on any write.


Hollow Press — Blog Polish

The Hollow Press blog got a substantial update. Featured image uploads were added to posts — images are stored via the existing storage disk and referenced in the post record. Tags were added as a JSON column, with the index response decoding them to an array so the frontend can use them directly.

Email notifications now fire when a post is published. Caching was added to the posts index query. Structured logging records post creation and update events for observability.

Alongside those backend changes, the post create and edit components had TypeScript errors that were resolved, and the frontend was auto-formatted. The full suite — mobile responsiveness, WCAG accessibility, and performance improvements — was applied in a single pass: responsive breakpoints, focus management, reduced motion support, and image lazy loading.

Toast notifications replace the previous inline flash messages. Flash data is now centralised through a single shared composable, so every page picks it up automatically without any per-page wiring. Loading states were added to form submission buttons to prevent double-submits.

Security headers were also tightened for production — stricter CSP, X-Frame-Options, and Referrer-Policy values.


Lunar Blood — Dark Mode, Analytics, and Accessibility

Lunar Blood now has a dark/light theme toggle. The preference is persisted in localStorage and applied on page load, so there is no flash of the wrong theme. The toggle is accessible — it has a label and keyboard support.

Accessible confirm dialogs were added for destructive actions. Previously, destructive operations used the browser's native confirm() call, which cannot be styled and is blocked in some embedded contexts. The new dialogs are modal, focus-trapped, and dismissible with Escape.

Google Analytics was wired up and a stray console.warn that was firing in production was removed. Pagination got an accessibility pass — page controls now have proper ARIA labels. CI/CD and security headers were added in the same session.


The Velvet Pulse — Form Requests, Dark Mode, and Rate Limiting

Form validation on The Velvet Pulse was moved out of controllers into dedicated FormRequest classes. This keeps controllers thin and makes the validation rules testable in isolation. A dark mode toggle matching the Lunar Blood implementation was added. Rate limiting was applied to the relevant API-facing routes to prevent abuse. Security headers and a CD pipeline were also added in the same pass.


Noteleks — Security Headers and CI/CD

Noteleks received security headers via a dedicated middleware and a CI/CD pipeline. The PHPStan baseline was cleaned up — an array_filter call that was generating a false positive in the AddSecurityHeaders middleware was removed. The game test suite had two failing assertions where the expected world height was out of sync with the actual GameConfig value; both were corrected.


SynthVeil — Security Headers and CD

SynthVeil received the same security headers and CD pipeline treatment. The CD workflow was triggered with SSH secrets configured and the deployment ran clean.


Graveyard Jokes — Google Places Fallback

The Business Profile endpoints on Graveyard Jokes now have a Google Places API fallback. The primary source for reviews and business info is the Google Business Profile API. When that is unavailable or returns an error, the controller falls back to the Places API using a new GooglePlacesService.

The service handles both the reviews and business info endpoints. It reads GOOGLE_PLACES_API_KEY and GOOGLE_PLACES_PLACE_ID from the environment. Feature tests were added for the fallback behavior and unit tests for the service itself — 176 lines of coverage for the service alone.

A SocialPostsWeekSeeder was also added to make it easy to seed a week of scheduled social content on demand, rather than writing one-off migrations each time.


Auth System — CI/CD

The shared auth system got its own CI and CD workflows. It was the last project in the portfolio without automated deployment. That is now corrected.


Closing Out May

That is eight projects updated in a single day. Not all of it is glamorous — security patches and pipeline fixes are maintenance, not features. But maintenance done consistently is what keeps a portfolio of live projects from accumulating debt that eventually blocks everything else.

The feature work — Velvet Radio's schedule page, Hollow Press's blog improvements, Lunar Blood's dark mode and analytics, The Velvet Pulse's rate limiting, Graveyard Jokes' Places fallback — is the kind of incremental progress that compounds. Each one is a small addition. Across a month, they add up to something substantially more capable than what existed at the start.

May closes out with every project healthy, patched, and deploying automatically. That is the foundation for June.

If you are looking for web development, design, or digital marketing — Graveyard Jokes Studios has the portfolio and the rates. Reach out.

— Joshua, Graveyard Jokes Studios