Security & Abuse Protection
Public Releases — especially anonymous, no-login chat — are the surface most exposed to abuse: bot floods, scraping, and wallet-burning. Divinci applies layered, defense-in-depth protection automatically, and gives you several controls to harden a public Release further.
Built-in protection
Section titled “Built-in protection”Every anonymous-chat Release is protected by a defense-in-depth stack in the platform API. You don’t configure these — they apply automatically, and they run before any model call, so blocked traffic never reaches (or bills) the LLM:
- Per-IP velocity limits — un-spoofable rate caps keyed on the Cloudflare client IP; the primary floor against bot floods. Over-limit requests get a
429withRetry-After. - Per-device quotas — monthly plus short-term burst caps per device, so one visitor can’t churn unlimited messages across conversations.
- Aggregate caps — a workspace-wide ceiling tied to your subscription tier.
- Abuse detection — flags device/IP rotation patterns and blocks high-severity offenders.
- Account lockout — authentication endpoints add per-IP rate limiting plus a temporary lockout after repeated failures, to blunt credential stuffing.
The controls below are the knobs you set per Release to harden it further.
Lock embeds to your domains
Section titled “Lock embeds to your domains”If you embed a Release, set the workspace’s allowedDomains so only your origins can load it. Always set it explicitly for a production embed — an unset allowlist is not origin-restricted.
Cap total spend
Section titled “Cap total spend”Set a workspace subscription tier to bound total monthly usage across all users of the workspace — the realistic operator knob for capping spend. Configure it on the WhiteLabel subscription document via the workspace/release APIs.
Free-chat gate (Cloudflare Turnstile)
Section titled “Free-chat gate (Cloudflare Turnstile)”For public landing pages and homepages, route a Release’s anonymous-chat surface through a verification gate that enforces a Turnstile CAPTCHA (and optionally email verification) plus per-email and per-device quotas before any message is served. This is the gate behind the Divinci.ai homepage and DrFuhrman.ai.
Configure it on the Release’s freeChatGate config. It layers on top of the built-in protection above.
mode | Flow | Use when |
|---|---|---|
none | No gate — plain anonymous chat | You want the built-in protection without an email step |
captcha-only | Turnstile widget per session, no email | Bot deterrent only |
captcha+otp | Turnstile + email + 6-digit OTP | Verified leads (the Divinci.ai homepage reference impl) |
captcha+magic-link | Turnstile + email + click-through link | Lower friction; depends on inbox delivery |
Default (unset or mode: "none") is back-compat: the Release’s existing allowAnonymousChat / maxAnonymousChatMessages rules apply unchanged.
Quota knobs
Section titled “Quota knobs”Per-email and per-device counters gate how much a visitor can send before and after verifying. Defaults shown:
| Field | Default | Meaning |
|---|---|---|
quota.perEmailLifetime | 1 | Messages a single email may send before verifying |
quota.postVerifyWindowCount | 5 | Messages per rolling window after verifying |
quota.postVerifyWindowHours | 24 | Length of that rolling window |
quota.perDeviceLifetime | 3 | Per-device lifetime cap — applied in all modes |
await divinci.releases.update("rel_123", { freeChatGate: { mode: "captcha+otp", quota: { perEmailLifetime: 1, postVerifyWindowCount: 5, postVerifyWindowHours: 24, perDeviceLifetime: 3, }, },});Turnstile: platform vs BYO
Section titled “Turnstile: platform vs BYO”The gate’s Turnstile pairing decides whose Cloudflare account/sitekey verifies the challenge.
turnstile.mode | Sitekey | Secret | Notes |
|---|---|---|---|
platform (default) | Divinci’s shared widget | Managed by Divinci | Domain binding handled by the embed-origin allowlist. |
byo | siteKey on the config (public, served to the client) | secretRef → an env/secret-store key name holding the secret | Run your own widget in your own Cloudflare account/analytics. |
// Bring your own Turnstile widgetawait divinci.releases.update("rel_123", { freeChatGate: { mode: "captcha-only", turnstile: { mode: "byo", siteKey: "0x4AAA...", // public sitekey, safe to serve secretRef: "MY_TURNSTILE_SECRET", // NAME of the secret-store key, never the raw secret }, },});Landing-page HMAC
Section titled “Landing-page HMAC”Even with a Turnstile gate, the bare anonymous-chat endpoint can be called directly by anyone who knows the release id — burning your wallet/quota. Landing-page HMAC closes that hole: when enabled, anonymous-chat calls must carry a valid signature, so only your signing proxy (e.g. a Cloudflare Worker holding the shared HMAC key) can reach the endpoint.
This is the one security control with a CLI flag:
# Require signed anonymous chat for a releasedivinci release update <releaseId> --require-signed-anonymous-chat
# Turn it back offdivinci release update <releaseId> --no-require-signed-anonymous-chatOr via the SDK (requireSignedAnonymousChat only has an effect while allowAnonymousChat is true):
await divinci.releases.update("rel_123", { allowAnonymousChat: true, requireSignedAnonymousChat: true, maxAnonymousChatMessages: 12,});Share the HMAC key only with your signing proxy. See the Releases → Anonymous chat page for the full anonymous-chat field reference.