> ## Documentation Index
> Fetch the complete documentation index at: https://docs.forepost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Security model

> How Forepost protects authentication, traffic, and infrastructure.

Plain-English account of Forepost's security posture. Detailed engineering reference (with code citations) is in the internal Engineering Reference; this page is the customer-facing summary.

## Authentication

Forepost uses [Clerk](https://clerk.com) on a custom domain (`clerk.forepost.ai`). Every API request from the SPA carries a Clerk-issued JWT:

* Verified server-side using the JWKS public keys from Clerk
* Algorithm pinned to `RS256`
* `iss` claim must match Forepost's configured issuer
* `azp` (authorized party) claim must be `https://app.forepost.ai`, no other Clerk application's tokens are accepted
* `exp` and `nbf` claims enforced
* JWKS keys cached for 1 hour, then re-fetched (so revoked keys cannot be honoured indefinitely)

When auth fails, the worker returns 401 with a structured `reason` field, `iss_mismatch`, `expired`, `bad_signature`, `azp_not_allowed`, etc. This makes deploys debuggable without leaking internal state.

## Authorization

There's currently one role: the workspace owner. A user can only read or write their own workspace, subscription, briefs, and history, keyed by Clerk user ID. There is no team or admin role yet.

## Email ownership

Subscribing the Daily Brief to an email address requires that address to be **verified** on your Clerk account. Forepost queries Clerk's Backend API to confirm before saving. This stops the system being used to send mail to addresses you don't own.

## Rate limiting

Per-user, sliding-hour windowed counters in D1:

| Endpoint                  | Limit     |
| ------------------------- | --------- |
| AI proxy (`POST /`)       | 60 / hour |
| `POST /brief/send`        | 5 / hour  |
| `POST /brief/send-digest` | 5 / hour  |

Rate-limited responses return HTTP 429.

## Transport security

* All traffic is HTTPS only.
* HSTS is set with `max-age=63072000; includeSubDomains; preload`.
* TLS 1.3 with modern ciphers (managed by Cloudflare and Vercel).

## Browser security headers

The SPA at `app.forepost.ai` ships with:

* **Content-Security-Policy**: strict allowlist; `default-src 'self'`; explicit `script-src` and `script-src-elem`; `frame-ancestors 'none'` (clickjack-proof).
* **X-Content-Type-Options: nosniff**
* **X-Frame-Options: DENY**
* **Referrer-Policy: strict-origin-when-cross-origin**
* **Permissions-Policy**: camera, microphone, geolocation, FLoC all disabled.

## Input validation

Every write endpoint validates incoming data before any database write or upstream API call:

* Workspace JSON: allowlisted keys, type checks per field, 16 KB total cap, 50-agent maximum
* Brief subscription: email regex + 254 char cap, IANA timezone, hour 0-23
* AI proxy: system prompt and message content size caps, max-token cap of 1024

Unknown keys are rejected outright. No silent acceptance.

## Email integrity

Outbound mail is sent via Resend with full DNS alignment on `forepost.ai`:

* **SPF**: `v=spf1 include:amazonses.com ~all`
* **DKIM**. Resend-managed, signed with `d=forepost.ai`
* **DMARC**: published; currently `p=none` with reporting (`rua=`); will tighten to `quarantine` then `reject` after a clean reporting period

Every email carries a `List-Unsubscribe` header for one-click opt-out.

## Unsubscribe tokens

Unsubscribe links are HMAC-SHA-256 signed with a server-side secret. Forging or replaying a token to disable someone else's subscription is computationally infeasible. Verification is constant-time.

## CORS

The API is locked to `app.forepost.ai` (and local-dev origins). Cross-site browser calls from any other origin are rejected.

## Cross-app token isolation

Even if a token were issued by Clerk for some other application sharing the same Clerk instance, our `azp` allowlist rejects it. There's no way for a Clerk JWT not minted for Forepost to authenticate against the worker.

## What's not yet in place

* **SOC 2 Type II.** Planned. The controls outlined here are the substrate; the audit confirms them externally.
* **SAML SSO for Enterprise.** On the roadmap; Clerk supports it natively.
* **Bug bounty.** Not yet stood up. For now, security disclosures go to [security@forepost.ai](mailto:security@forepost.ai).

## OWASP Top 10 (2021) — stance

The Forepost Worker has been spot-audited against each category. Summary below; details on request.

| Category                                 | Stance                                                                                                                                                                                                                                                                            |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **A01 Broken Access Control**            | Every endpoint resolves the caller via Clerk JWT → user\_id, then keys all reads/writes on that user\_id. Admin endpoints additionally gated by an `ADMIN_USER_IDS` env-var allowlist. Workspace members get scoped read access via the membership table, never cross-workspace.  |
| **A02 Cryptographic Failures**           | JWT verification with RS256 + JWKS rotation (1-hour cache); HMAC-SHA-256 on unsubscribe tokens with constant-time verification; helpdesk OAuth secrets encrypted with AES-GCM at rest; HTTPS only on every surface.                                                               |
| **A03 Injection**                        | All D1 queries use prepared statements with `bind()` — no string-concatenated SQL anywhere. HTML in outgoing email rendered via a dedicated `escapeHtml`. Slack webhook URLs validated against a strict regex (`hooks.slack.com/services/...` only) on both save and send.        |
| **A04 Insecure Design**                  | Honest-no-data principle prevents fabricated outputs. Email-ownership check (against Clerk's verified addresses) before subscribing. Per-user rate limits per endpoint scope. Workspace JSON capped at 16 KB; agent count capped at 50.                                           |
| **A05 Security Misconfiguration**        | CORS strictly allowlisted to `app.forepost.ai`. Strict CSP, X-Frame DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, Permissions-Policy locked down. Generic 500 returned on uncaught errors — no stack traces in responses.                |
| **A06 Vulnerable + Outdated Components** | Dependabot enabled (npm + GitHub Actions); weekly minor/patch grouped PR, monthly Action bumps. The worker's runtime dependencies are minimal (Cloudflare runtime + Anthropic SDK pinned via package-lock).                                                                       |
| **A07 Authentication Failures**          | Clerk handles authentication. Forepost verifies `iss`, `azp` (cross-app token isolation), `exp`, `nbf` on every request. No password storage of any kind.                                                                                                                         |
| **A08 Software + Data Integrity**        | Production worker deploys exclusively via GitHub Actions on merge to `main`, using a scoped Cloudflare API token. `npm ci` enforces package-lock hashes. No arbitrary code execution paths.                                                                                       |
| **A09 Logging + Monitoring**             | All admin write actions append to `admin_audit_log` in D1. Cloudflare Workers observability is enabled for traffic + error metrics. Gap: no on-call alerting on error-rate spikes yet — operational rather than security, but flagged.                                            |
| **A10 Server-Side Request Forgery**      | The worker makes outbound requests to a small set of hardcoded destinations (Anthropic, Clerk, Resend, helpdesk OAuth endpoints, JWKS). The only user-controlled outbound URL is the Slack webhook, host-restricted to `hooks.slack.com`. No `fetch(userControlledUrl)` anywhere. |

The audit is repeated at least quarterly and after any change touching authentication, the cron, or outbound integration code.

## Reporting a vulnerability

Email [security@forepost.ai](mailto:security@forepost.ai). We respond within 24 hours, fix critical issues within 7 days, and credit reporters in the changelog if they want acknowledgement.
