Skip to content

Security & Abuse Protection

Copy page

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.

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 429 with Retry-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.

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.

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.

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.

modeFlowUse when
noneNo gate — plain anonymous chatYou want the built-in protection without an email step
captcha-onlyTurnstile widget per session, no emailBot deterrent only
captcha+otpTurnstile + email + 6-digit OTPVerified leads (the Divinci.ai homepage reference impl)
captcha+magic-linkTurnstile + email + click-through linkLower friction; depends on inbox delivery

Default (unset or mode: "none") is back-compat: the Release’s existing allowAnonymousChat / maxAnonymousChatMessages rules apply unchanged.

Per-email and per-device counters gate how much a visitor can send before and after verifying. Defaults shown:

FieldDefaultMeaning
quota.perEmailLifetime1Messages a single email may send before verifying
quota.postVerifyWindowCount5Messages per rolling window after verifying
quota.postVerifyWindowHours24Length of that rolling window
quota.perDeviceLifetime3Per-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,
},
},
});

The gate’s Turnstile pairing decides whose Cloudflare account/sitekey verifies the challenge.

turnstile.modeSitekeySecretNotes
platform (default)Divinci’s shared widgetManaged by DivinciDomain binding handled by the embed-origin allowlist.
byositeKey on the config (public, served to the client)secretRef → an env/secret-store key name holding the secretRun your own widget in your own Cloudflare account/analytics.
// Bring your own Turnstile widget
await 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
},
},
});

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:

Terminal window
# Require signed anonymous chat for a release
divinci release update <releaseId> --require-signed-anonymous-chat
# Turn it back off
divinci release update <releaseId> --no-require-signed-anonymous-chat

Or 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.