# DareBuild Generated Project Patterns

Use this file when a user asks an AI coding agent to add authentication, Google auth, project-local login, payments, Stripe Checkout, subscriptions, or webhooks to a generated DareBuild project.

Required public docs:
- Full agent README: `https://elonman.darebuild.com/agents.md`
- Project user invitations: `https://elonman.darebuild.com/agents/project-users.md`
- Machine-readable manifest: `https://elonman.darebuild.com/.well-known/darebuild-agent.json`
- OpenAPI contract: `https://elonman.darebuild.com/.well-known/darebuild-openapi.json`

## Runtime Structure

Generated project code is user-controlled application code. Do not add platform-only behavior to the public project page when the user is asking for account or project administration.

Current generated project structure:
- `public/homepage.py` contains the default `handler(request)` for the homepage.
- `public/homepage.html`, `public/homepage.css`, `public/homepage.js`, and public assets are user-facing files.
- Shared Python helpers can live under `shared/`.
- SQL migrations are created through the project `migrations/` endpoint.
- Background jobs are created through the project `tasks/` endpoint.
- Public routing supports the homepage and single-slug pages such as `/checkout/` or `/stripe_webhook/` when that page exists.

Current generated project request/runtime contract:
- `handler(request)` may return a context dict, `(context, status_code)`, or a Django `HttpResponse`.
- Use `request.method`, `request.GET`, `request.POST`, `request.COOKIES`, `request.path`, `request.get_host()`, `request.is_secure()`, and `request.csrf_token`.
- Include `<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">` in POST forms.
- Use `request.user.is_authenticated` for DareBuild account-session state. When authenticated, `request.user.email` and `request.user.username` are available; use `getattr(request.user, "email", "")` when handling anonymous visitors.
- Use `request.darebuild.has_backend_access` before relying on project database access, migrations, or background tasks.
- Use `django.db.connection` for tenant database queries after backend access is unlocked.
- CRITICAL COOKIE RULE: only cookies whose names start with `project_` or `darebuild_project_` are forwarded to and from generated project code.
- The filter applies in both directions: DareBuild drops an unprefixed project `Set-Cookie` response and omits an unprefixed browser cookie from the next runner request.
- App-owned session, magic-link session, remember-me, and CSRF cookies must all use an allowed prefix. Django's default `sessionid` and `csrftoken` names are not generated-project session storage.
- Available runner dependencies include Django, psycopg2, FastAPI, Uvicorn, `requests`, TikTokLive, and `modal`; do not assume `stripe`, `django-allauth`, or `django-sesame` are importable inside generated project code.
- Available core relay helpers are `runner_service.sdk.send_email`, `runner_service.sdk.cache_get`, `runner_service.sdk.cache_set`, `runner_service.sdk.cache_delete`, `runner_service.sdk.ws_publish`, `runner_service.sdk.bridge_start`, `runner_service.sdk.bridge_status`, `runner_service.sdk.bridge_stop`, `runner_service.sdk.modal_function_spawn`, `runner_service.sdk.modal_function_remote`, and `runner_service.sdk.modal_function_get`.
- Browser realtime should use public project WebSockets. Build a URL from `request.darebuild.websocket_url_template` by replacing `{channel}` with a channel name matching `[A-Za-z0-9_-]`, then open it with `new WebSocket(url)`.
- Generated Python can broadcast to browser subscribers with `runner_service.sdk.ws_publish(channel, event="event-name", data={...})`. Delivered messages are JSON objects with `channel`, `event`, and `data`.
- Public WebSocket channels are visible to anyone who can access the generated project URL. Do not put secrets or private user data on predictable public channels.

## Project Login And Cookie Contract

Follow this contract before implementing any app-owned login flow:

1. Define one cookie-name constant whose value starts with `project_` or `darebuild_project_`.
2. Use that exact constant when setting the login response cookie, reading `request.COOKIES`, and deleting the cookie on logout.
3. Return a Django `HttpResponse` or `HttpResponseRedirect`; the runner serializes its `Set-Cookie`, and Core forwards the cookie only when the prefix is allowed.
4. Set `HttpOnly`, `SameSite=Lax`, `Path=/`, and `Secure` whenever `request.is_secure()` is true.
5. Store only a hash of the random session token in the tenant database. Keep the raw token only in the browser cookie.
6. If the app validates CSRF itself, use a separate prefixed cookie such as `project_myapp_csrf`, put the matching token in the POST form, and compare them server-side. Do not assume the default `csrftoken` reaches generated project code.
7. Test the real round trip: login response, redirected authenticated request, a protected POST, logout, and a final anonymous request.

Minimal session-cookie pattern:

```python
import secrets
from django.http import HttpResponseRedirect

SESSION_COOKIE_NAME = "project_myapp_session"

def login_response(request, save_session_hash):
    raw_token = secrets.token_urlsafe(32)
    save_session_hash(raw_token)
    response = HttpResponseRedirect("/dashboard/")
    response.set_cookie(
        SESSION_COOKIE_NAME,
        raw_token,
        max_age=60 * 60 * 24 * 14,
        path="/",
        secure=bool(request.is_secure()),
        httponly=True,
        samesite="Lax",
    )
    return response

def request_session_token(request):
    return (request.COOKIES or {}).get(SESSION_COOKIE_NAME, "")

def logout_response():
    response = HttpResponseRedirect("/")
    response.delete_cookie(SESSION_COOKIE_NAME, path="/", samesite="Lax")
    return response
```

Wrong examples that DareBuild filters: `sessionid`, `csrftoken`, `auth_session`, and `myapp_session`.

Diagnostic rule: if email delivery succeeds and the login handler redirects but the next page asks the user to log in again, inspect the cookie name first. Do not keep changing the magic-link lifetime or redirect page until the cookie uses an allowed prefix and is visible in the next `request.COOKIES`.

## TikTokLive Bridges

Use Core-managed bridges for TikTokLive streams that need to run longer than a page request.

Pattern:
- In a logged-in/backend-unlocked page handler, call `runner_service.sdk.bridge_start("@handle", channel="tiktok-live", duration_seconds=3600)` to queue a bridge and return immediately.
- Use `bridge_status(channel="tiktok-live")` for polling and `bridge_stop(channel="tiktok-live")` to request a clean stop.
- The browser listens on `request.darebuild.websocket_url_template` with the same channel and receives `comment` events shaped as `{text, word, user, comment_count}` plus `bridge-status` events.
- Do not call `TikTokLiveClient.run()` or keep a TikTokLive loop inside `handler(request)`. Public page renders are request-bound and must finish quickly.

## Modal GPU Background Tasks

Use background tasks for long Modal GPU or image-analysis work. Short interactive backend handlers, such as live audio analysis, may use the same scoped Modal helpers when they must return immediately to the current request. Never call Modal from browser JavaScript.
Keep all launcher code, UI code, and Modal deploy source in the DareBuild project itself. Do not depend on `/Users/jamessteinberg/Downloads/projects/stonemountain`, deleted local folders, or legacy Django app code for MTA, Modal, or generated project behavior.

Eligibility:
- The project must have backend access through a paid/unlocked project, or be owned by the free backend-access account `jamespsteinberg@gmail.com`.
- The Modal function must be project-owned or explicitly granted to that DareBuild project.
- Project-owned Modal app names must be exactly `darebuild-build-<build_id>` or start with `darebuild-build-<build_id>-`.
- Project-owned Modal app/functions do not need a preexisting grant; first successful runner use records the grant and queues a Slack message for monitoring.
- Shared Modal functions must be registered as enabled project grants in DareBuild before generated task code can invoke them.
- If backend access is locked, show `billing.payment_url` from the project manifest or API error and wait for the human to pay in the DareBuild GUI.

Runner pattern:
- If the Modal app/function does not already exist, call MCP `project_deploy_modal` or POST `modal/` with `action=deploy`, a `slug`, and `source_code` defining `def handler(*args, **kwargs)`.
- DareBuild generates the Modal app name as `darebuild-build-<build_id>-<slug>`, deploys it from the runner, records the function grant, and queues Slack monitoring.
- Create or update a project task through `tasks/` and queue it with `tasks/<task_id>/run/`.
- In task code or short backend handler code, import `runner_service.sdk.modal_function_spawn`, `modal_function_remote`, or `modal_function_get`.
- Prefer `modal_function_spawn("modal-app-name", "function_name", *args, **kwargs)` for long GPU work; it returns a Modal call id immediately.
- Use `modal_function_get(call_id, timeout=0)` from a later task run to poll for a spawned result.
- Use `modal_function_remote(...)` only for short calls that can finish within the runner timeout.
- The runner rejects Modal app/function names that do not match the project-owned prefix or an explicit grant for the same project.
- The runner notifies DareBuild after first successful project-owned Modal use so the project grant and Slack monitoring stay in sync.
- Use MCP `project_destroy_modal` or POST `modal/` with `action=destroy` to stop a DareBuild-managed Modal deployment for the project.
- Modal credentials live in the runner service environment (`MODAL_TOKEN_ID` and `MODAL_TOKEN_SECRET`). Do not print, store, or embed Modal tokens in generated project files.

Example task code:
```python
from runner_service.sdk import modal_function_spawn

image_url = PARAMS["image_url"]
call_id = modal_function_spawn("darebuild-build-<build_id>-photo", "analyze_photo", image_url)
print("Modal call:", call_id)
```

## Authentication

When a user asks for "auth", "login", "Google auth", or "sign in", first decide whether they mean a DareBuild/platform account session or app-owned end-user accounts.

Platform account auth:
- Use this when the generated project can rely on the user's DareBuild account identity.
- This matches the `allauth-login-links` starter.
- Link directly to platform routes from generated project templates:
  - `/accounts/login/`
  - `/accounts/google/login/`
  - `/accounts/logout/`
  - `/accounts/password/reset/`
  - `/account/email/`
  - `/create/account/`
  - `/magic/`
- In generated project Python, inspect `request.user.is_authenticated`, `getattr(request.user, "email", "")`, and `request.darebuild.is_group_member`.
- Do not use `{% provider_login_url %}` or other allauth template tags in generated project templates; the generated runner does not load allauth template tags.

Project-owned email/password auth:
- Use this when the user wants accounts that belong to the generated application, not to DareBuild itself.
- Start from or copy the `project-auth-password` starter structure.
- Require backend access because it stores users and sessions in the tenant database.
- Create tables like `project_auth_users` and `project_auth_sessions`.
- Use a cookie named `project_auth_session`; it is forwarded because it starts with `project_`.
- Never rename it to an unprefixed name. DareBuild will silently exclude that cookie from both the browser response and the next runner request, causing a login loop.
- Use the same cookie-name constant for `response.set_cookie`, `request.COOKIES.get`, and `response.delete_cookie`.
- For protected state-changing forms, use a separate project-prefixed CSRF cookie/token pair or an equivalent explicit server-side CSRF check; do not assume Django's default `csrftoken` is forwarded to the runner.
- Hash passwords with PBKDF2 or Django password hashers; never store plaintext passwords.
- Send verification email through `runner_service.sdk.send_email`.
- Keep verification tokens, reset tokens, and session ids scoped to the project database.

Project-owned Google OAuth:
- The current generated runner does not provide a generated-project allauth app or allauth template tags.
- If platform Google sign-in is acceptable, link to `/accounts/google/login/` and use `request.user.email`.
- If the user requires app-owned Google OAuth, store client secrets through project secrets and read them from `os.environ` in server-side Python. Then implement the OAuth authorize/callback flow with `requests`, tenant database tables, and `project_` cookies. Do not assume those secrets already exist in generated project code.

## Payments And Stripe

DareBuild billing is separate from app payments:
- DareBuild's backend unlock checkout pays for the project runtime.
- A generated application's Stripe Checkout, subscriptions, customers, and webhooks are app-level features and must be implemented separately.

Current Stripe constraints for generated projects:
- The generated runner does not include the `stripe` Python package.
- Use Stripe's HTTPS API through `requests` if implementing app-level payments inside generated project code.
- Do not put Stripe secret keys, webhook secrets, or price ids into public templates, CSS, JS, or markdown files.
- Store Stripe secret keys and webhook secrets through project secrets and read them from `os.environ` in server-side Python.

Recommended app-level Stripe structure, based on existing DareBuild-family project patterns:
- Add a tenant database table such as `checkout_orders`, `payment_customers`, or `subscriptions`.
- Store status values like `started`, `checkout_created`, `paid`, `payment_failed`, `expired`, and `canceled`.
- Store Stripe ids: checkout session id, payment intent id, customer id, subscription id, amount, currency, email, and local order id.
- On checkout POST, validate the form, create the local row first, call Stripe Checkout Session create, save the returned session id and URL, then redirect to the session URL.
- Use `mode=payment` for one-time checkout and `mode=subscription` for recurring plans.
- Send `success_url`, `cancel_url`, `customer_email`, `line_items`, and metadata with the project/app name, local order id, build id when useful, and `local_dev` or environment.
- For one-time payments, also put the same local metadata in `payment_intent_data.metadata`.

Webhook pattern:
- Project-level webhooks should use an explicit generated project page route such as `/stripe_webhook/` when that page exists; do not use DareBuild's platform `/webhooks/main/`, which is for DareBuild billing.
- Verify the Stripe signature with the app's endpoint secret before processing.
- Reject invalid payloads and invalid signatures.
- Make processing idempotent by storing or caching each Stripe event id before applying side effects.
- Require metadata to match the expected project/app/local order before updating rows.
- Handle at least `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `checkout.session.expired`, `payment_intent.succeeded`, `payment_intent.payment_failed`, and `charge.dispute.created`.
- For subscriptions, also handle `invoice.paid`, `invoice.payment_failed`, `customer.subscription.updated`, and `customer.subscription.deleted`.
- Return a small JSON success response after processing or safely ignoring an unrelated event.

## Agent Decision Rules

- For "add login" with no more detail, use platform account links if the app only needs DareBuild user identity; use `project-auth-password` when the app needs its own users.
- Before declaring app-owned login complete, verify the session cookie begins with `project_` or `darebuild_project_` and survives the login response into the next `request.COOKIES`.
- For "add Google auth", prefer `/accounts/google/login/` unless the user explicitly needs app-owned Google OAuth.
- For "add payments", ask whether they mean DareBuild backend unlock billing or app-level customer payments.
- For app-level Stripe, ask for price ids and tell the user which project secret names to add before writing production code that calls Stripe.
- For webhooks, verify signatures, make handlers idempotent, scope by metadata, and avoid nested routes the current public router cannot serve.
- For photo analysis, AI inference, or other GPU work, create a background task and call only project-owned or explicitly granted deployed Modal functions through `runner_service.sdk.modal_function_spawn`; use short backend-handler `modal_function_remote` only for interactive request/response analysis that fits within the render timeout, never from browser JavaScript.
- If backend access is locked, show `billing.payment_url` from the project manifest or error response and wait for the human to pay in the DareBuild GUI.
