Skip to content
camada
Menu

Frameworks

Security middleware for your framework

Pick your framework. Each snippet is the install for that stack: one package and one middleware on most, a few files for Next.js, and PHP, Java and .NET install from GitHub. The variables are the same everywhere, and the key is printed once when you create a project. Every SDK reads them from the process environment; your framework's quickstart says how to set them there.

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

More JavaScript runtimes, each published on npm with install steps in its README:

Node.js

Express @camada/node

npm i @camada/node
import camada from '@camada/node';
app.use(camada.express());
  • Works for Connect-style stacks too.
  • Register it before your routes so blocked requests never reach them.
Express quickstart →

Fastify @camada/node

npm i @camada/node
import { camadaFastify } from '@camada/node/fastify';
await app.register(camadaFastify);
  • Register the plugin at the root instance so every route is covered.
Fastify quickstart →

NestJS @camada/node

npm i @camada/node
import { CamadaModule } from '@camada/node/nest';

@Module({ imports: [CamadaModule.forRoot()] })   // Nest ≤10: CamadaModule.forRoot({ routes: '*' })
export class AppModule {}
  • Default Express platform. On the Fastify platform register the Fastify plugin instead.
NestJS quickstart →

Koa @camada/node

npm i @camada/node
import { camadaKoa } from '@camada/node/koa';
app.use(camadaKoa());
  • Mount it first in the middleware chain.
Koa quickstart →

Next.js @camada/next

npm i @camada/next
// middleware.ts (Next ≤15) or proxy.ts (Next 16) at the project root
import { camada } from '@camada/next';
export default camada();
export const config = { matcher: ['/((?!_next/|favicon.ico).*)'] };

// app/api/camada/[...camada]/route.ts
import { camadaRoute } from '@camada/next';
export const { GET, POST } = camadaRoute();

// app/layout.tsx, inside <head>
import { CamadaBeacon } from '@camada/next';
<CamadaBeacon />
  • App router. Three files, one env var.
  • On Vercel the middleware sees x-vercel-ja4-digest, a real TLS fingerprint, and Vercel's X-Forwarded-For is trusted by default.
  • Events ship before the response, so status is unknown at this position. Blocked requests ship with 403.
  • Keep the matcher as broad as the example. Every route it excludes is invisible to camada.
Next.js quickstart →

Python

FastAPI camada

pip install camada
from camada.fastapi import CamadaMiddleware
app.add_middleware(CamadaMiddleware)   # any Starlette app too
  • A pure ASGI middleware: streaming responses and lifespan are untouched.
  • Add it last, so it runs outermost and answers before anything else.
FastAPI quickstart →

Django camada

pip install camada
# 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.
Django quickstart →

Flask camada

pip install camada
from camada.flask import init_app
init_app(app)   # wraps app.wsgi_app, so camada answers before routing
  • Wraps the WSGI app rather than registering a before_request hook: the beacon and challenge endpoints answer before routing.
Flask quickstart →

Go

Go github.com/camada-app/camada-go

go get github.com/camada-app/camada-go
import "github.com/camada-app/camada-go"

mux := http.NewServeMux()
mux.HandleFunc("GET /{$}", home)
http.ListenAndServe(":8080", camada.Handler(mux))   // outermost, so camada answers before routing
  • net/http: any router built on http.Handler sits behind it.
  • The first request builds the engine and is answered cold; camada.Default().Stop(ctx) before main returns drains the last batch.
Go quickstart →

Ruby

Rails camada

bundle add camada
# config.ru — Sinatra, Hanami, or any Rack app
require "camada"
use Camada::Rack      # first, so camada answers before routing
run App

# Rails — nothing to add: the Railtie inserts Camada::Rack at the top of the middleware stack
  • One gem, one Rack middleware; the Railtie mounts it for Rails.
  • Never request.ip: the middleware reads REMOTE_ADDR and applies your trusted-proxy rules itself.
Rails quickstart →

PHP

PHP camada/camada

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.

install
composer config repositories.camada vcs https://github.com/camada-app/camada-php
composer require camada/camada:dev-main
<?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.
PHP quickstart →

Laravel camada/camada

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.

install
composer config repositories.camada vcs https://github.com/camada-app/camada-php
composer require camada/camada:dev-main
// bootstrap/app.php (Laravel 11+), inside the existing ->withMiddleware(function (Middleware $middleware) { … })
$middleware->prepend(\Camada\Laravel\Middleware::class);   // first in the global stack

// or app/Http/Kernel.php (Laravel 10): the first entry of protected $middleware = [ … ]
\Camada\Laravel\Middleware::class,
  • Prepend it so blocked requests never reach the rest of the stack.
  • terminate() is the post-response phase: the spool ships and the snapshot refreshes after the kernel has sent the response.
Laravel quickstart →

Java

Spring Boot dev.camada:camada

Not on Maven Central yet. From your project's folder: clone it next to the project, build it into your local Maven repository, then add the dependency.

install
git clone https://github.com/camada-app/camada-java ../camada-java
mvn -q -f ../camada-java install -DskipTests

<!-- then, in pom.xml -->
<dependency>
  <groupId>dev.camada</groupId>
  <artifactId>camada</artifactId>
  <version>0.2.0</version>
</dependency>
// CamadaConfig.java, beside your @SpringBootApplication class
package com.example.demo;   // your application's package

import dev.camada.servlet.CamadaFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

@Configuration
class CamadaConfig {
  @Bean
  FilterRegistrationBean<CamadaFilter> camadaFilter() {
    var reg = new FilterRegistrationBean<>(new CamadaFilter());
    reg.setOrder(Ordered.HIGHEST_PRECEDENCE);   // first, so camada answers before routing
    return reg;
  }
}
  • A plain jakarta.servlet.Filter: Tomcat, Jetty and Undertow take it as is, from web.xml or addFilter at the front of the chain.
  • Keep server.forward-headers-strategy=none (the default) so camada judges X-Forwarded-For itself.
Spring Boot quickstart →

.NET

ASP.NET Core Camada

Not on NuGet yet. From the folder that holds your .csproj: clone it next to the project, then reference it.

install
git clone https://github.com/camada-app/camada-dotnet ../camada-dotnet
dotnet add reference ../camada-dotnet/src/Camada/Camada.csproj
// Program.cs
using Camada.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCamada();      // one engine from the environment, drained on shutdown

var app = builder.Build();
app.UseCamada();                   // FIRST: camada answers before routing, static files, anything
// your routes go here, the beacon one below included: app.Run() blocks, so nothing after it is mapped
app.Run();
  • Put UseCamada() before UseForwardedHeaders(), if you use it, so camada judges the raw header itself.
  • .NET 8; nothing beyond the ASP.NET Core shared framework.
ASP.NET Core quickstart →

Positions

What each position sees

@camada/node

Sees the true wire header order, the final status and latency, and app outcomes from track(). Does not see a TLS fingerprint or ASN; those rules skip here. Enforces IP and path entries, and the user-agent and header conditions of your custom rules, with a socket-peer or trusted-proxy IP. ASN, country and TLS-fingerprint entries fail open here.

@camada/hono

Runs inside a Cloudflare Worker. request.cf carries the client's ASN, country and TLS fingerprint, so ASN, country and TLS-fingerprint entries genuinely enforce here, along with IP, path, user-agent and header conditions; events ship through waitUntil, off the response path. Workers normalise header order, so the raw wire order is not available. Docs.

camada (Python)

The Node position, in a Python process: final status and latency, app outcomes from track(), and the wire's header order under ASGI (FastAPI, Starlette, async Django) but not under WSGI (Flask, sync Django). No TLS fingerprint or ASN; those rules skip here. Enforces IP, path, user-agent and header conditions with a socket-peer or trusted-proxy IP. Docs.

github.com/camada-app/camada-go (Go)

The Node position, in a Go process: final status and latency, the route pattern, and app outcomes from Track(). net/http parses headers into a map, so the wire's header order is not available; no TLS fingerprint or ASN either, those rules skip here. Enforces IP, path, user-agent and header conditions with a socket-peer or trusted-proxy IP. Docs.

camada/camada (PHP)

The Node position, one request at a time: final status and latency and app outcomes from track(), with the snapshot and event spool in a file-backed cache every worker shares, refreshed after the response has left. getallheaders() yields a hash, so no header order; no TLS fingerprint or ASN; those rules skip here. Enforces IP, path, user-agent and header conditions with REMOTE_ADDR or a trusted-proxy IP. Docs.

camada (Ruby)

The Node position, in a Ruby process: final status and latency, the matched route, and app outcomes from Camada.track. The Rack env carries no wire header order; no TLS fingerprint or ASN; those rules skip here. Enforces IP, path, user-agent and header conditions with REMOTE_ADDR or a trusted-proxy IP — never request.ip. Docs.

dev.camada:camada (Java)

The Node position, in a JVM: final status and latency, the Spring MVC route pattern, app outcomes from Camada.track(), and the header order as the container hands it over. No TLS fingerprint or ASN; those rules skip here. Enforces IP, path, user-agent and header conditions with getRemoteAddr() or a trusted-proxy IP. Docs.

Camada (.NET)

The Node position, in an ASP.NET Core process: final status and latency, the matched route, and app outcomes from CamadaTrack(). Header order is what IHeaderDictionary reports, not the wire's under Kestrel; no TLS fingerprint or ASN; those rules skip here. Enforces IP, path, user-agent and header conditions with the connection peer or a trusted-proxy IP. Docs.

@camada/next

Runs in middleware before the cache. On Vercel it sees a JA4 TLS digest and a trustworthy client IP. Headers arrive sorted, and events ship before the response, so status is unknown except for blocks. Enforces IP and path entries and the user-agent and header conditions of your custom rules; ASN, country and TLS-fingerprint entries fail open. Needs a trusted-proxy setting off Vercel or IP rules do not enforce.