api.soubiran.dev owns the dynamic data behind soubiran.dev. The personal website remains statically generated, while this Laravel application stores user accounts, comments, reactions, votes, feedback, messages, and notifications.

It also imports stable records for content published by soubiran.dev and infra.soubiran.dev. Stable page IDs keep those records connected when a title or URL changes.

Why the dynamic state is separate

Most requests to my personal website only need static HTML and assets. Comments and reactions have different requirements. They need authenticated writes, authorization, persistence, and an administration interface. Putting those concerns in a separate application keeps them out of the static publishing path.

Laravel provides the data model and request lifecycle for that mutable state. The website can still deploy as static files on Cloudflare, and its browser code calls the API only for interactive features. This split fits a content site that benefits from static delivery but still needs accounts and user-generated data.

The API stores a local page record rather than copying article content. Published records use the UUID from the source website as their API route key. Interactions point to that record, so a later content build can change the page title or canonical URL without breaking its comments or reactions.

How content and interactions meet

Both websites publish a meta.json catalog. During deployment, the API fetches those catalogs and upserts their entries by UUID. The soubiran.dev catalog supplies the title, URI, canonical URL, and optional release metadata. The infra.soubiran.dev catalog supplies the stable identity and location of each wiki page.

The browser addresses published-page interaction routes with that UUID. Public endpoints return emoji definitions, vote totals, feedback summaries, reactions, and comment threads. They also accept page feedback without creating an account. Comment responses include rendered Markdown, replies, like counts, author data, and capability flags, which lets the website render the thread without reproducing Laravel’s authorization rules.

Voting is the exception to UUID routing. A candidate article can exist in the API before publication, so vote mutations use its internal database ID. Once the article is published, a metadata import attaches the public UUID to the same record.

Authenticated routes cover user-specific data and mutations. They return the current account, votes, reactions, and notifications, and they handle comments, likes, reactions, votes, Markdown previews, and ask-me-anything messages. The API only includes private identity fields when a signed-in user reads their own account.

Laravel Sanctum treats the website as a stateful first-party client. Before an unsafe request, the browser obtains a CSRF cookie and sends the request with its session credentials. Users sign in through GitHub, Google, or Discord with Laravel Socialite. This uses the browser session for the first-party website instead of exposing long-lived API tokens to client code.

Filament provides the private administration panel. New accounts and user activity can create queued database notifications for administrators, while Laravel policies decide which comment and account actions each request may perform.

Deployment ordering

Forge deploys the application on a server. Its deployment script enables maintenance mode, pulls the selected branch, installs production PHP dependencies, optimizes Laravel, reloads PHP-FPM, and runs database migrations. It then imports metadata from both websites before bringing the application back online.

Those imports make deployment order part of the data contract. A normal API deployment must fetch catalogs that are already live, not files from the previous website build. After a push to either website, redeploy.soubiran.dev waits for the corresponding Cloudflare Worker deployment to reach production before it calls the API deploy hook.

This sequence keeps static content and mutable records independent without letting their shared page IDs drift. Website builds publish the source metadata first. The API deployment imports it second. Browser requests can then attach current interaction data to the new pages.

Ecosystem