# Core Concepts

- [Introduction](#introduction)
- [The Edge](#the-edge)
    - [How Requests Flow](#how-requests-flow)
    - [Clients and Fingerprints](#clients-and-fingerprints)
- [Activity](#activity)
    - [Request Actions](#request-actions)
    - [Analytics and 404s](#analytics-and-404s)
- [Rules](#rules)
    - [Writing Rules](#writing-rules)
    - [Counting Over Time](#counting-over-time)
    - [Available Fields](#available-fields)
    - [Testing Rules](#testing-rules)
- [Tags and Bans](#tags-and-bans)
- [The Bot Gym](#the-bot-gym)
    - [Routines](#routines)
    - [Obstacles](#obstacles)
    - [Exits](#exits)
- [Connecting an Agent](#connecting-an-agent)
    - [Searching in Plain English](#searching-in-plain-english)

## Introduction

NiceTryBot has a small number of moving parts, and once you understand how they fit together, the rest of the control panel will feel familiar. In short: the **edge** sees every request, **Activity** shows you what it saw, **rules** decide who is up to no good, **tags and bans** record those decisions, and the **bot gym** gives the guilty something to do.

## The Edge

The edge is NiceTryBot's front door. It is a build of [Caddy](https://caddyserver.com) that terminates TLS for every domain you protect, records each request, and decides where it should go.

Along with the usual details, such as the path, status, and user agent, the edge records how each visitor connected: its JA3 and JA4 TLS fingerprints, a JA4H fingerprint of its headers, its client hints, and its `Accept-Language` header. It also looks up the visitor's network and approximate location using local databases, so nothing is sent to a third party.

The edge also handles a few chores on its own. It tags requests for paths that only scanners ask for, such as `.env`, `.git`, and `wp-admin`. It introduces itself as `nginx`, with nginx-style error pages, so nothing a visitor sees names NiceTryBot. And WebSockets pass straight through.

## How Requests Flow

When a request arrives, the edge checks it against your rules and the domain's [protection mode](https://docs.nicetrybot.com/docs/0.x/domains.md#protection-modes):

<div class="content-list" markdown="1">

- **Humans** always continue to your origin.
- **Tagged visitors** on a domain in **Observe** mode continue to your origin as well, but are marked in Activity so you can see what NiceTryBot would have done.
- **Tagged visitors** on a domain in **Enforce** mode are sent to the bot gym or blocked.

</div>

If the edge can't reach the control panel for any reason, it fails open and sends everyone to your origin.

### Clients and Fingerprints

NiceTryBot thinks about visitors as **clients**. A client is an IP address together with its JA4 TLS fingerprint. Many people share a browser build, so many people share a JA4 fingerprint, but the fingerprint describes the software that actually connected, which is very hard for a script to fake.

When a client's details don't add up, NiceTryBot gives it a trait:

| Trait | Description |
| --- | --- |
| `spoofed` | Its user agent or client hints disagree with how it connected, such as a user agent claiming to be Chrome from a client whose TLS handshake isn't Chrome's. |
| `outdated` | Its browser is several versions behind the current release. |
| `corporate` | It appears to be behind a corporate security gateway. |
| `hopped` | It took over another IP address's visit. |

NiceTryBot also refreshes the published IP ranges of well-known crawlers every day, so a real crawler can be told apart from an impostor borrowing its name.

## Activity

**Activity** is a live, filterable log of every request that reached the edge, what it asked for, and what NiceTryBot did about it. You may search it using the same `WHERE` syntax as [rules](#writing-rules), save searches you use often, and follow new requests as they arrive.

You may also label a client. Labels give a name to an IP address, fingerprint, user agent, or set of client hints, such as "office" or "uptime monitor", and appear everywhere that client does.

### Request Actions

Each request in Activity shows the action the edge took:

| Action | Description |
| --- | --- |
| **Passed** | The request went to your origin. |
| **Observed** | The request was tagged, but went to your origin because its domain is in **Observe** mode. |
| **In the gym** | The request was sent to the bot gym. |
| **Blocked** | The request was refused. |

### Analytics and 404s

**Analytics** shows who is knocking: the networks, countries, user agents, and TLS fingerprints behind your traffic, along with a flow diagram of where requests went and a live map. **404s** shows which domains get the most "not found" responses, what was asked for, and whether it was a person or a probe. Scanners tend to ask for the same handful of paths, so this page is often the quickest way to spot them.

## Rules

Rules decide which clients deserve attention. They are written in NiceTryBot's rule language, which reads like SQL but isn't SQL. Every rule either **tags** or **bans** the clients it matches, for as long as you specify.

Enabled rules run every minute over newly arrived requests. Single-request rules are also judged at the edge as requests arrive. You will find a gallery of ready-made rules on the **Rules** page, and the free [Rule packs](https://docs.nicetrybot.com/docs/0.x/addons.md#first-party-add-ons) add-on includes more.

### Writing Rules

A rule names an action, the subjects it applies to, a duration, and the requests it matches. For example, the following rule bans any IP address and fingerprint that goes fishing for secrets, admin panels, or exploits:

```rule
-- Anyone fishing for secrets, admin panels or exploits.
BAN ip, ja4 FOR 30 DAYS
WHERE category = 'probe'
```

A rule may tag clients instead of banning them. Tags are named using `AS`, and are a great way to mark clients you would like to send to the gym rather than refuse:

```rule
-- Claims to be Chrome or Firefox, but its TLS offers no ALPN: a script.
TAG ip, ja4 AS 'ua-tls-mismatch' FOR 1 DAY
WHERE (ua LIKE '%Chrome/%' OR ua LIKE '%Firefox/%')
  AND ja4.alpn = '00'
```

Keywords are case-insensitive. Strings use single quotes, `LIKE` ignores case and supports the `%` and `_` wildcards, and `--` starts a comment. `AND` binds more tightly than `OR`, so use parentheses when combining them. Durations may use `SECOND`, `MINUTE`, `HOUR`, `DAY`, or `WEEK`, or `FOREVER`.

### Counting Over Time

Using `WITHIN` and `HAVING`, a rule may count a client's requests over a window of up to 24 hours. For example, the following rule bans an IP address that collects 404 responses across several of your sites:

```rule
-- One IP collecting 404s across several of your sites.
BAN ip FOR 1 HOUR
WHERE status = 404
WITHIN 10 MINUTES
HAVING COUNT(*) >= 2 AND COUNT(DISTINCT host) >= 2
```

Similarly, this rule bans a flood of requests from a cloud or hosting network:

```rule
-- A flood of requests from a cloud or hosting network.
BAN ip, ja4 FOR 6 HOURS
WHERE network.kind = 'hosting'
WITHIN 5 MINUTES
HAVING COUNT(*) >= 60
```

You may also end a verdict early using `UNTIL`, followed by a condition. The complete grammar of the rule language is as follows:

```text
script    := action subjects [AS 'tag'] FOR duration [WHERE expr] [WITHIN span HAVING having] [UNTIL expr]
action    := BAN | TAG
subjects  := subject {, subject}       subject := ip | ja4
duration  := number unit | FOREVER     span := number unit
unit      := SECOND | MINUTE | HOUR | DAY | WEEK, or the plural
expr      := term {OR term}            term := factor {AND factor}
factor    := NOT factor | ( expr ) | field predicate
predicate := op literal | [NOT] LIKE 'pattern' | [NOT] IN (literal, …) | IS [NOT] NULL
op        := = | != | < | <= | > | >=
having    := hterm {OR hterm}          hterm := hfactor {AND hfactor}
hfactor   := NOT hfactor | ( having ) | COUNT ( * | DISTINCT field ) [FILTER ( WHERE expr )] op number
```

### Available Fields

Rules and Activity searches may use the following fields. Add-ons may add more.

| Field | Description |
| --- | --- |
| `ip` | The client's IP address. Accepts CIDR ranges, such as `'66.249.64.0/19'`. |
| `ja4` | The client's JA4 TLS fingerprint. |
| `ja4.alpn` | The ALPN the client offered. `'00'` means none: a script, not a browser. |
| `ja3` | The client's JA3 TLS fingerprint. |
| `ja4h` | The client's header fingerprint. |
| `ua` | The user agent. |
| `hints` | The client hints, such as `LIKE '%HeadlessChrome%'`. |
| `client` | The client's trait: `'spoofed'`, `'outdated'`, `'corporate'`, or `'hopped'`. |
| `lang` | The `Accept-Language` header. A missing value is a tell-tale sign of a script. |
| `host` | The domain requested. |
| `path` | The path requested. |
| `category` | The path's category: a family like `'probe'`, or a slug like `'probe/creds'`. |
| `status` | The response status, such as `404`. |
| `method` | The request method, such as `GET` or `POST`. |
| `duration` | The response time, in milliseconds. |
| `action` | The edge's action: `'passed'`, `'observed'`, `'diverted'`, or `'blocked'`. |
| `tag` | The edge's tag, such as `'probe'`. |
| `asn` | The network's autonomous system number. |
| `network` | The network's name. |
| `network.kind` | The kind of network. `'hosting'` covers cloud and datacenter networks. |
| `country` | The country, as an ISO 3166 code such as `'NL'`. |
| `region` | The region. |
| `city` | The city. |

### Testing Rules

Every rule is tested against your real traffic before it runs. When you write or edit a rule, NiceTryBot backtests it against past requests and shows you exactly which clients it would have caught. If the list includes your own office, you'll know before anyone gets banned.

## Tags and Bans

When a rule matches a client, it reaches a **verdict**: a tag or a ban, along with when it first and last matched and when it expires. The **Tags & bans** page lists every client your rules have marked. You may lift a verdict at any time to let the client go immediately, although the rule may tag it again if it keeps matching.

Verdicts on their own don't block anyone. A ban is enforced on domains in **Enforce** mode, and a tag is something you can act on, most often by sending tagged clients to the bot gym. Ended verdicts are kept for 30 days.

## The Bot Gym

Instead of refusing a suspicious visitor outright, you may send it to the bot gym. The gym puts visitors through obstacles that cost a bot CPU time and patience, but only cost a person a moment. The gym is served in place, at the address the visitor asked for, so its address bar never changes.

The gym only holds visitors on domains in **Enforce** mode.

### Routines

A **routine** describes who goes to the gym and what they do there. Each routine has entry conditions, an ordered list of obstacles, an exit, and a pass time. Entry conditions may match a visitor's network, country, browser, user agent, IP address, TLS fingerprint, domain, path category, path, edge tag, or a rule's verdict.

When a visitor matches several routines, their obstacles stack into one pipeline and the strictest exit wins. Once a visitor completes the gym, they receive a pass that lasts for the routine's pass time, which may be anywhere from one minute to one week.

You may also send a rule's clients straight to the gym using the **Send its clients to the gym** option when editing a rule.

### Obstacles

NiceTryBot includes the following obstacles:

| Obstacle | Description |
| --- | --- |
| **Proof of work** | The browser solves a SHA-256 puzzle in the background. People just wait a moment. |
| **Waiting room** | A queue that keeps the visitor's CPU busy, one proof of work after another, for as long as you set. |
| **Type the code** | A short code drawn as a noisy image. The visitor reads it and types it back. |
| **Browser check** | A silent look for automation, such as webdriver flags, headless windows, and a missing Chrome runtime. |

[Add-ons](https://docs.nicetrybot.com/docs/0.x/addons.md) may add more obstacles. For example, the Captchas add-on adds a set of puzzles people solve in a second and bots get lost in.

### Exits

After the last obstacle, a routine's exit decides what happens next:

| Exit | Description |
| --- | --- |
| **Continue to their page** | The visitor gets a pass and lands where they were going. |
| **Show a not-found page** | The gym answers with a `404` itself. Your origin never sees the request. |
| **Turn them away** | The gym answers with a `403`. |

## Connecting an Agent

NiceTryBot's control panel includes an [MCP](https://modelcontextprotocol.io) server, so agents like Claude Code, Codex, and Cursor can read your traffic, add domains, write rules, and build gym routines on your behalf. The same tools are available from your terminal using the [`nicetrybot` command](https://docs.nicetrybot.com/docs/0.x/cli.md). The [MCP Server](https://docs.nicetrybot.com/docs/0.x/mcp.md) documentation covers API tokens, connecting an agent, and every tool the server offers.

### Searching in Plain English

If you add an Anthropic API key in **Settings → Artificial Intelligence**, you may search Activity in plain English, such as "phones from the Netherlands that hit the login page yesterday". This feature is off until you add a key. It is the only feature that sends anything outside of your servers: your question, the search or rule you are editing, and your team's hostnames are sent to Anthropic using your own key.
