Skip to content
camada
Menu

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.

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

// 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

  1. 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.
  2. 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.
  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) and relays /_cam/fp posts onto the event batch with the resolved client IP.
  5. 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.