Quickstart
Spring Boot quickstart
Installed from GitHub (camada-app/camada-java).
1. Install
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.
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>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 starts your app, or add them in your host's settings. camada reads only the environment, and Spring Boot does not pass application.properties or a .env file to it.
export CAMADA_KEY=<ingest_token>.<snap_token>
export CAMADA_INGEST_URL=http://localhost:8787 # dev only; defaults to production ingest3. Wire it in
// 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.
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.
// PageController.java, beside CamadaConfig.java: the tag goes in the <head> of the pages you render
package com.example.demo; // your application's package
import dev.camada.Camada;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
class PageController {
@GetMapping(value = "/", produces = "text/html")
String home(HttpServletRequest request) {
return "<html><head>" + Camada.scriptTag(request) + "</head><body>…</body></html>";
}
}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.
// in a PageController handler, once your own sign-in check fails (email: the account it was for)
Camada.track(request, "login_failed", email);Runtime
What it does per request
- Refreshes the snapshot off-path on a daemon thread at the cadence your tenant config sets (ETag/304, gzip on the wire). A cold start fails open.
- Resolves the client IP from getRemoteAddr() per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it; keep server.forward-headers-strategy=none.
- 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 before the chain runs, and when the response is done (or in AsyncListener.onComplete) ships one batched, redacted event carrying the Spring MVC route pattern.
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.
- The event carries the header order as the container hands it over.
- The request that builds the engine is answered cold. Camada.getDefault().warmUp(5000) at startup waits for the boot poll, bounded, and never throws.
- Both threads are daemons; pending events drain from a JVM shutdown hook, on SIGTERM as well as a normal exit.
- CamadaFilter handles the original REQUEST dispatch only; ASYNC, ERROR, FORWARD and INCLUDE pass through. CAMADA_SERVERLESS=1 hands refreshes to the daemon thread from a per-request staleness check.
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 Java page.