Skip to content
camada
Menu

Quickstart

PHP quickstart

Installed from GitHub (camada-app/camada-php).

1. Install

Not on Packagist yet. Run these in your project's folder: Composer installs it straight from GitHub, composer.lock pins the commit, and composer update camada/camada moves it.

composer config repositories.camada vcs https://github.com/camada-app/camada-php
composer require camada/camada:dev-main

2. 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 runs php -S; under PHP-FPM, which clears the environment, add env[CAMADA_KEY] = … to the pool config. PHP reads no .env file.

shell
export CAMADA_KEY=<ingest_token>.<snap_token>
export CAMADA_INGEST_URL=http://localhost:8787   # dev only; defaults to production ingest

3. Wire it in

<?php
// public/index.php — any front controller
require __DIR__ . '/../vendor/autoload.php';

use Camada\Camada;
use Camada\Http\Sapi;

$cam = Camada::default();      // built lazily from the environment, once per request
$ctx = Sapi::run($cam);        // null: camada answered (block, challenge, beacon) and the script must stop
if ($ctx === null) {
    return;
}
// your app: x-rid and the _sfp cookie are already on the response
  • Needs nothing but $_SERVER, php://input and header(): FPM, php -S, mod_php.
  • The snapshot and event spool live in a file-backed cache every worker shares; both refresh after the response has left.

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.

echo '<html><head>', $cam->scriptTag($ctx), '</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.

$cam->track($ctx, 'login_failed', $email);

Runtime

What it does per request

  1. Keeps the snapshot in a file-backed cache every worker shares (CAMADA_CACHE_DIR) and refreshes it in the post-response phase, after the client has its bytes. An empty cache answers the first request open.
  2. Resolves the client IP from REMOTE_ADDR per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it; behind nginx real_ip the peer is already the client, so say none.
  3. 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.
  4. Serves /_cam/b.js (the beacon, first-party, from opcache) and relays /_cam/fp posts onto the event spool with the resolved client IP.
  5. Sets x-rid and the _sfp session cookie, and after the response has ended appends one redacted event to the spool; a due spool ships in that same post-response phase.

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.
  • Header order is whatever getallheaders() yields, so the analyst reads no HEADER_ORDER signal from this tap and never scores its absence.
  • Under FPM and FrankenPHP the post-response work runs after fastcgi_finish_request(); under php -S and mod_php the buffer is flushed with a Content-Length first. Run the CLI server with PHP_CLI_SERVER_WORKERS=4 or more.
  • The cache dir must be a local, writable directory (flock and rename are the concurrency model). Each host keeps its own copy and ships its own spool.
  • Events ship when the spool is due, on the next request that finds it due: a quiet site ships its last events on its next hit, not on a timer.

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 PHP page.