<p>
    Pitchbar is built for Laravel Cloud as the primary target — the stack
    is FrankenPHP + Postgres + Redis, all of which Laravel Cloud provisions
    natively. Self-hosting is supported but the operator owns more pieces.
</p>

<h2>Laravel Cloud</h2>

<p>
    <code>infra/cloud.yaml</code> in the repo describes the environments
    and processes. The high-level shape:
</p>

<ul>
    <li><strong>Region:</strong> <code>us-east</code> by default. Choose for proximity to your customers.</li>
    <li><strong>App process:</strong> FrankenPHP, Octane mode. Auto-scaled.</li>
    <li><strong>Worker process:</strong> Horizon, dedicated to <code>crawl</code>, <code>index</code>, and <code>default</code> queues.</li>
    <li><strong>Reverb process:</strong> persistent WebSocket server.</li>
    <li><strong>Postgres:</strong> 16, with daily backups.</li>
    <li><strong>Redis:</strong> 7, persistent.</li>
</ul>

<h2>Environments</h2>

<table>
    <thead><tr><th>Env</th><th>Purpose</th></tr></thead>
    <tbody>
        <tr><td>preview</td><td>Per-PR ephemeral environments. Auto-spun on PR open, torn down on merge / close.</td></tr>
        <tr><td>staging</td><td>Long-lived. Mirrors production config. Used for QA and pre-release verification.</td></tr>
        <tr><td>production</td><td>The customer-facing environment. Releases gated on green CI + manual deploy.</td></tr>
    </tbody>
</table>

<h2>Compute sizing for v1 launch</h2>

<p>
    Starting point. Adjust based on traffic.
</p>

<table>
    <thead><tr><th>Component</th><th>Size</th><th>Why</th></tr></thead>
    <tbody>
        <tr><td>App (Octane)</td><td>2 instances × 2 vCPU / 2 GB</td><td>Hot path is mostly I/O-bound on LLM streaming. Two instances for HA.</td></tr>
        <tr><td>Worker (Horizon)</td><td>2 instances × 2 vCPU / 2 GB</td><td>Indexing throughput. Scale on queue depth.</td></tr>
        <tr><td>Reverb</td><td>1 instance × 1 vCPU / 1 GB</td><td>WebSocket, sticky.</td></tr>
        <tr><td>Postgres</td><td>2 vCPU / 4 GB / 50 GB SSD</td><td>Comfortable until ~10M messages.</td></tr>
        <tr><td>Redis</td><td>1 GB</td><td>Sessions, queue, hot caches.</td></tr>
    </tbody>
</table>

<h2>Domains</h2>

<p>
    You typically need:
</p>

<ul>
    <li><strong>Primary domain</strong> — <code>app.pitchbar.com</code> for the customer / admin app.</li>
    <li><strong>Widget domain</strong> — same or a separate <code>cdn.pitchbar.com</code> serving <code>/widget/widget.js</code>. The bundle has a content-hash query param, so aggressive caching is safe.</li>
    <li><strong>Reverb domain</strong> — <code>realtime.pitchbar.com</code> if you split the WebSocket process onto its own host.</li>
</ul>

<h2>CI/CD</h2>

<p>
    GitHub Actions workflows under <code>.github/workflows/</code>:
</p>

<ul>
    <li><code>tests.yml</code> — PHP setup + Composer + Pest suite (with fakes, no network).</li>
    <li><code>lint.yml</code> — Pint, ESLint, TypeScript <code>tsc --noEmit</code>.</li>
    <li><code>widget.yml</code> — widget bundle build + size budget check.</li>
</ul>

<p>
    Deploys are gated on green CI; the actual deploy step is configured
    on Laravel Cloud (or your hosting equivalent), not in the workflow
    files.
</p>

<h2>Migrations</h2>

<p>
    Laravel Cloud runs <code>php artisan migrate --force</code> on every
    deploy. Migrations should be backwards-compatible — a deploy that adds
    a NOT NULL column to a populated table needs a two-step:
</p>

<ol>
    <li>Deploy 1: add the column nullable, backfill, app code starts writing it.</li>
    <li>Deploy 2: change the column to NOT NULL.</li>
</ol>

<p>
    Same goes for renames and drops — never destructive in a single deploy.
</p>

<h2>Backups</h2>

<ul>
    <li><strong>Postgres</strong> — daily snapshots, retained 30 days. Point-in-time recovery enabled.</li>
    <li><strong>Vector store</strong> — Cloudflare Vectorize / Qdrant don't have a built-in backup; rebuild from the <code>chunks</code> table by re-dispatching <code>IndexDocumentJob</code> for every document. <code>php artisan pitchbar:audit-vectors</code> reports drift between the chunks table and the live vector store; if you need to repair, dispatch the job per row.</li>
    <li><strong>R2 / object storage</strong> — versioning enabled.</li>
    <li><strong>App secrets</strong> — Laravel Cloud's secret store is encrypted; back up <code>APP_KEY</code> separately (it's the master for app_settings encryption).</li>
</ul>

<h2>Rollback</h2>

<p>
    Laravel Cloud keeps the previous release for instant rollback. For
    schema-incompatible rollbacks (rare), restore from the latest snapshot.
</p>

<h2>Self-hosting</h2>

<p>
    The same Docker setup that powers <code>docker-compose.yml</code> works
    for production with a few additions:
</p>

<ul>
    <li>Reverse proxy (Caddy or Nginx) terminating TLS in front of FrankenPHP.</li>
    <li>Managed Postgres + Redis (or self-managed with HA replicas).</li>
    <li>Horizon as a long-running service, monitored by systemd / a process manager.</li>
    <li>Reverb as its own process.</li>
    <li>Sentry / OTEL collector running locally or pointing at a SaaS.</li>
</ul>

<p>
    The <code>composer run dev</code> shortcut starts everything locally
    (Octane, queue worker, Reverb, vite) for development.
</p>
<?php /**PATH /Users/prinnce/Documents/php/superbar/resources/views/documentation/pages/deployment.blade.php ENDPATH**/ ?>