---
title: "ASP.NET Core security middleware: quickstart · camada"
description: "Keep credential stuffers, scrapers and exploit scanners out of your ASP.NET Core app: install from GitHub, two lines and one env var."
url: https://www.camada.app/docs/quickstart/aspnet
last_updated: 2026-10-30
---

Quickstart

# ASP.NET Core quickstart

Installed from GitHub ([camada-app/camada-dotnet](https://github.com/camada-app/camada-dotnet)).

## 1. Install

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

```sh
git clone https://github.com/camada-app/camada-dotnet ../camada-dotnet
dotnet add reference ../camada-dotnet/src/Camada/Camada.csproj
```

## 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 dotnet run, add them to "environmentVariables" in Properties/launchSettings.json, or set them in your host's settings. camada reads only the environment, and ASP.NET does not pass appsettings.json, user-secrets or a .env file to it.

shell

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

## 3. Wire it in

```csharp
// 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.

## 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.

```csharp
// Program.cs, above app.Run(): the tag goes in the <head> of the pages you render
app.MapGet("/", (HttpContext ctx) => Results.Content($"<html><head>{ctx.CamadaScriptTag()}</head><body>…</body></html>", "text/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](https://www.camada.app/use-cases/credential-stuffing).

```csharp
// in a handler that takes HttpContext ctx, once your own sign-in check fails (email: the account it was for)
ctx.CamadaTrack("login_failed", user: email);
```

Runtime

## What it does per request

1. Refreshes the snapshot off-path on a background PeriodicTimer task at the cadence your tenant config sets (ETag/304, gzip on the wire). A cold start fails open.
2. Resolves the client IP from Connection.RemoteIpAddress per your trusted-proxy setting. Raw X-Forwarded-For is never trusted without it.
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 from Response.OnStarting, puts the context in HttpContext.Items["camada"], and when the response is done ships one batched, redacted event carrying the matched 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 header order is what IHeaderDictionary reports, not the wire's under Kestrel; the analyst knows and never scores its absence.
- The request that builds the engine is answered cold. Resolve CamadaEngine from app.Services at startup and wait for the boot poll (the README shows the bounded loop).
- Keep UseCamada() before UseForwardedHeaders() so camada judges the raw header itself.
- Pending events drain on ApplicationStopping and ProcessExit. CAMADA_SERVERLESS=1 switches to a per-request staleness check with no timer.

Kill switch: `CAMADA_DISABLED=1`, checked per request. All other variables are listed in the [docs index](https://www.camada.app/docs); the package's options and deployment notes are on the [.NET page](https://www.camada.app/docs/dotnet).

## Sitemap

[Every page on this site](https://www.camada.app/sitemap.md)
