Quickstart
Django quickstart
Package camada, on PyPI.
1. Install
pip install camada2. Environment
The key is printed once when you create a project in the app. Set these as environment variables: export them in the shell that starts your app, or add them in your host's settings. Python reads no .env file on its own.
export CAMADA_KEY=<ingest_token>.<snap_token>
export CAMADA_INGEST_URL=http://localhost:8787 # dev only; defaults to production ingest3. Wire it in
# settings.py
MIDDLEWARE = ["camada.django.CamadaMiddleware", *MIDDLEWARE]- Put it first, so blocked requests never reach the rest of the stack.
- Sync and async stacks alike; the matched route pattern rides the event.
4. Add the beacon to HTML pages
Optional but recommended. The beacon cuts false positives by showing which sessions are real browsers. Use the helper.
from camada.django import script_tag
return f"<head>{script_tag(request)}</head>…"5. Send app outcomes
Login failures, signups and checkout declines are the signals no edge position sees. The identifier is HMAC-hashed in your process. How they catch credential stuffing.
from camada.django import track
track(request, "login_failed", user=email)Runtime
What it does per request
- Refreshes the snapshot off-path on a daemon thread (30 s poll with ETag, gzip on the wire). A cold start fails open.
- Resolves the client IP per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it.
- Your ordered custom rules first, then allow, block, challenge. A block gets a 403 with x-block-reason before your app runs; a challenge serves the proof-of-work page on a page load, and an API call reaches your app with an x-camada-challenge: 1 response header. The event still ships.
- Serves /_cam/b.js (the beacon, first-party) and relays /_cam/fp posts onto the event batch with the resolved client IP.
- Sets x-rid and the _sfp session cookie, and when the response is done ships one batched, redacted event.
Limits
Caveats at this position
- Enforcement at this position covers IP, path, user-agent and request-header conditions. ASN, country and TLS-fingerprint entries fail open in-app.
- WSGI loses the wire's header order; the analyst knows this tap cannot see it and never scores its absence.
- Every worker process polls its own snapshot (about 5 MB each) — the server's poll_seconds keeps the cadence honest. Under gunicorn --preload the threads restart after the fork.
- CAMADA_SERVERLESS=1 switches to lazy refresh with no poll thread.
Kill switch: CAMADA_DISABLED=1, checked per request. All other variables are listed in the docs index; the package's options and deployment notes are on the Python page.