<p>
    Pitchbar reads its configuration from the standard Laravel
    <code>.env</code>, with overrides for some keys available via the
    platform admin's <strong>App Settings</strong> page so you can swap
    Stripe / mail / LLM credentials without redeploying.
</p>

<h2>Required</h2>

<table>
    <thead><tr><th>Variable</th><th>Why</th></tr></thead>
    <tbody>
        <tr><td><code>APP_KEY</code></td><td>Encryption master key. Generate with <code>php artisan key:generate</code>. Never rotate without a re-encrypt migration.</td></tr>
        <tr><td><code>APP_URL</code></td><td>Public URL, used to build widget snippets, OAuth callbacks, signed URLs.</td></tr>
        <tr><td><code>DB_*</code></td><td>Postgres connection.</td></tr>
        <tr><td><code>REDIS_HOST</code></td><td>Cache, sessions, queue, hot-path retrieval cache, conversation history cache.</td></tr>
        <tr><td><code>QUEUE_CONNECTION</code></td><td>Set to <code>redis</code> in production.</td></tr>
        <tr><td><code>SESSION_DRIVER</code></td><td><code>redis</code> in production.</td></tr>
        <tr><td><code>CACHE_DRIVER</code></td><td><code>redis</code>.</td></tr>
        <tr><td><code>WIDGET_JWT_SECRET</code></td><td>HS256 signing secret for visitor JWTs. ≥ 32 random bytes.</td></tr>
    </tbody>
</table>

<h2>LLM provider</h2>

<p>
    At least one of the following:
</p>

<table>
    <thead><tr><th>Variable</th><th>Provider</th></tr></thead>
    <tbody>
        <tr><td><code>CLOUDFLARE_ACCOUNT_ID</code> + <code>CLOUDFLARE_API_TOKEN</code></td><td>Cloudflare Workers AI (preferred). Auto-binds Llama 3.3 70B + bge-base-en-v1.5.</td></tr>
        <tr><td><code>OPENAI_API_KEY</code></td><td>OpenAI direct.</td></tr>
        <tr><td><code>OPENROUTER_API_KEY</code></td><td>OpenRouter (router across many providers).</td></tr>
    </tbody>
</table>

<p>
    Set <code>LLM_PROVIDER</code> to force a binding (<code>cloudflare</code>,
    <code>openai</code>, <code>openrouter</code>). When unset, the resolver
    picks based on which keys are available, in the priority above.
</p>

<h2>Vector store</h2>

<table>
    <thead><tr><th>Variable</th><th>Provider</th></tr></thead>
    <tbody>
        <tr><td><code>VECTORIZE_INDEX</code></td><td>Cloudflare Vectorize index name. Uses <code>CLOUDFLARE_ACCOUNT_ID</code> + token.</td></tr>
        <tr><td><code>QDRANT_URL</code> + <code>QDRANT_API_KEY</code></td><td>Qdrant.</td></tr>
    </tbody>
</table>

<p>
    Set <code>VECTOR_PROVIDER</code> to force. Auto-binds based on
    available keys (Vectorize preferred).
</p>

<h2>Crawler</h2>

<table>
    <thead><tr><th>Variable</th><th>Strategy</th></tr></thead>
    <tbody>
        <tr><td>(uses <code>CLOUDFLARE_*</code>)</td><td>Cloudflare Browser Rendering. Preferred.</td></tr>
        <tr><td><code>BROWSERLESS_TOKEN</code> + <code>BROWSERLESS_URL</code></td><td>Browserless fallback.</td></tr>
        <tr><td>(none)</td><td>Plain HTTP. Free; no JS rendering.</td></tr>
    </tbody>
</table>

<h2>Stripe</h2>

<table>
    <thead><tr><th>Variable</th><th>Purpose</th></tr></thead>
    <tbody>
        <tr><td><code>STRIPE_KEY</code></td><td>Publishable key.</td></tr>
        <tr><td><code>STRIPE_SECRET</code></td><td>Secret key. Used for plan sync and Cashier.</td></tr>
        <tr><td><code>STRIPE_WEBHOOK_SECRET</code></td><td>Signing secret for incoming webhooks (<code>whsec_…</code>).</td></tr>
        <tr><td><code>CASHIER_CURRENCY</code></td><td>Defaults to <code>usd</code>.</td></tr>
    </tbody>
</table>

<h2>Reverb (realtime)</h2>

<table>
    <thead><tr><th>Variable</th><th>Purpose</th></tr></thead>
    <tbody>
        <tr><td><code>REVERB_APP_KEY</code></td><td>Public app key. Embedded in widget init payload.</td></tr>
        <tr><td><code>REVERB_APP_SECRET</code></td><td>Secret. Server-side only.</td></tr>
        <tr><td><code>REVERB_APP_ID</code></td><td>App identifier.</td></tr>
        <tr><td><code>REVERB_HOST</code></td><td>Public hostname for the Reverb server.</td></tr>
        <tr><td><code>REVERB_PORT</code></td><td>Default 8080.</td></tr>
        <tr><td><code>REVERB_SCHEME</code></td><td><code>wss</code> in production, <code>ws</code> locally.</td></tr>
    </tbody>
</table>

<h2>Mail</h2>

<table>
    <thead><tr><th>Variable</th><th>Purpose</th></tr></thead>
    <tbody>
        <tr><td><code>MAIL_MAILER</code></td><td><code>smtp</code> / <code>postmark</code> / <code>resend</code> / etc.</td></tr>
        <tr><td><code>MAIL_FROM_ADDRESS</code></td><td>Sender address. Required.</td></tr>
        <tr><td><code>MAIL_FROM_NAME</code></td><td>Display name.</td></tr>
        <tr><td><code>MAIL_HOST</code> / <code>MAIL_PORT</code> / <code>MAIL_USERNAME</code> / <code>MAIL_PASSWORD</code></td><td>SMTP credentials.</td></tr>
    </tbody>
</table>

<h2>Branding</h2>

<table>
    <thead><tr><th>Variable</th><th>Purpose</th></tr></thead>
    <tbody>
        <tr><td><code>BRANDING_LABEL</code></td><td>Default "Powered by Pitchbar" label. Override in app_settings for white-label.</td></tr>
        <tr><td><code>BRANDING_URL</code></td><td>Where the label links to.</td></tr>
        <tr><td><code>BRANDING_FOOTER_LOGO_PATH</code></td><td>Storage path to the footer logo image.</td></tr>
    </tbody>
</table>

<h2>Observability</h2>

<table>
    <thead><tr><th>Variable</th><th>Purpose</th></tr></thead>
    <tbody>
        <tr><td><code>SENTRY_DSN</code></td><td>Error reporting.</td></tr>
        <tr><td><code>OTEL_EXPORTER_OTLP_ENDPOINT</code></td><td>OpenTelemetry collector. Honeycomb / Grafana Cloud.</td></tr>
        <tr><td><code>OTEL_SERVICE_NAME</code></td><td>Service name in traces. Default <code>pitchbar</code>.</td></tr>
    </tbody>
</table>

<h2>App Settings overrides</h2>

<p>
    The <code>app_settings</code> singleton row stores plaintext-encrypted
    overrides for:
</p>

<ul>
    <li>Stripe secret + webhook secret + publishable key.</li>
    <li>Mail driver settings.</li>
    <li>Cloudflare / OpenAI / OpenRouter keys.</li>
    <li>Branding (label, URL, logo).</li>
</ul>

<p>
    The <code>AppSettingsOverrideServiceProvider</code> reads this on boot
    and merges into <code>config()</code>. Setting things via the admin
    panel is preferred for production — you don't have to re-deploy when a
    key rotates.
</p>

<div class="callout callout-warning">
    <svg class="callout-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
    <div class="callout-body">
        <code>APP_KEY</code> rotation requires a manual migration. The
        encrypted columns in <code>app_settings</code> were sealed with the
        old key; rotating without re-encrypting renders them unreadable.
    </div>
</div>
<?php /**PATH /Users/prinnce/Documents/php/superbar/resources/views/documentation/pages/env.blade.php ENDPATH**/ ?>