# SG/Sentinel Rules Of The Game: The Behavioural Specification In English

**version** v0.27.58
**date** 18 May 2026
**from** Human (project lead)
**to** Architect, Developer (lead), Security, @Dev
**type** Arch brief (behavioural specification)

---

## What This Is

The behavioural specification for SG/Sentinel, written in plain English, following the packet through the execution flow. This is the next evolution of the architecture diagrams: where those show the components, this follows a request through them, stating exactly what should happen at each step. It is the reference the tabletop simulations run against, and together with them it forms the acceptance criteria.

The voice memo named the purpose: **a document that writes the rules and the behaviour in English, like acceptance criteria, that follows the packet, follows the execution flow.** When the code is implemented, the question is: does it behave like this document describes? If yes, the code is correct. This document is the answer key.

This is deliberately precise. The simulations (companion documents) will run requests against these rules and surface gaps and contradictions. This document is what they test against, so it must state the behaviour unambiguously.

## The Rules Of The Game: First Principles

Before the step-by-step, the governing principles that every behaviour must honour (drawn from the series):

1. **No invalid request reaches the application.** Allowlist, not denylist. We know what good looks like.
2. **Log every hit.** Every request is logged at Layer 1, regardless of verdict.
3. **Fast-track the known-good.** Fingerprinted, validated users are accelerated.
4. **Detect before damage, not instantly.** Catch attackers over a window, before they do harm.
5. **Slow analysis, fast enforcement.** LLM and heavy analysis are async; inline is fast and deterministic.
6. **Every block has a reason.** Logged, inspectable.
7. **Sentinel's logging ends at S3.** Existing code consumes from there.
8. **The same code runs everywhere.** Local equals production (deploy parity).

Every behaviour below must be consistent with these. Where the simulations find a behaviour that violates one, that is a gap.

## The Execution Flow: Following The Packet

The canonical path of a request through SG/Sentinel:

```
STEP 0:  Request arrives at CloudFront
STEP 1:  Layer 1 (CloudFront Function) assigns an ID and logs
STEP 2:  Layer 1 checks the fast deterministic rules
STEP 3:  Layer 1 decides: block, fast-track, or pass-on
STEP 4:  (if passed) Layer 2 (Lambda@Edge) validates against known-good
STEP 5:  Layer 2 decides: block, pass to application, or flag
STEP 6:  (if passed) the request reaches the application
STEP 7:  (async, always) Layer 3 analyses, builds evidence, updates rules
STEP 8:  (always) the log lands in S3
```

Now each step in detail.

### Step 0: Request Arrives

A request arrives at CloudFront. CloudFront may serve from cache (cache hit) or forward to the origin (cache miss). Layer 1 (CloudFront Function) runs on the request regardless.

**Behaviour:** every request enters Layer 1. There is no path around it.

### Step 1: Layer 1 Assigns An ID And Logs

Layer 1 immediately assigns a request ID (our own, alongside any AWS-provided ID). It captures the log data: method, path, headers, source IP, timing, cache status.

**Behaviour:**
- A request ID is assigned before any rule runs.
- The log data is captured.
- Privacy rules apply: if IP-logging is off for this config, the IP is not logged (or is hashed); if a threat is suspected, the IP is captured.
- The log is queued for S3 delivery (it lands in S3 at Step 8, regardless of verdict).

**Open behaviour question (for simulation):** when exactly is the log written to S3? At Layer 1 immediately, or after the verdict is known (so the log includes the verdict)? The behaviour should be: the log includes the verdict, so it is finalised after the decision but the ID and capture happen at Step 1.

### Step 2: Layer 1 Checks Fast Deterministic Rules

Layer 1 runs the entry rules of the fractal rule graph. These are deterministic, fast, no-I/O rules using embedded data:

- Is the source IP in the banned-IP list? (embedded)
- Is the source region banned? (embedded)
- Is the request obviously malformed?
- Is the path an obvious-bad pattern? (`/etc/passwd`, `/wp-login.php` on a static site, `/.env`, known exploit probes)
- Does the request match a known scanner signature?

The entry rules may direct traversal to deeper rules (the next-rule mechanism), but at Layer 1 everything is fast and deterministic.

**Behaviour:**
- Each rule returns a verdict with a confidence (deterministic-certain for these).
- A deterministic-certain block (e.g. `/etc/passwd`) blocks immediately.
- Rules may accumulate an anomaly score (for opinion rules, though most Layer 1 rules are certain).

### Step 3: Layer 1 Decides

Layer 1 reaches a decision:

- **Block:** if a deterministic-certain rule fired (banned IP, obvious-bad path), block now. Log the block with reason. Apply the block action (drop silently, deflect with 404, or wild-goose-chase, per the rule).
- **Fast-track:** if the request matches a known-good fingerprint on the allowlist, fast-track it (skip deeper checks, pass toward the application quickly).
- **Pass-on:** if neither blocked nor fast-tracked, pass to Layer 2 for deeper validation.

**Behaviour:**
- A block at Layer 1 ends the request (it does not reach Layer 2 or the application).
- A fast-track requires a fingerprint match (see the fingerprint behaviour below).
- A pass-on sends the request to Layer 2.

**Open behaviour question (for simulation):** where is the fingerprint allowlist stored, such that Layer 1 (no I/O, embedded data only) can check it? This is a known gap to resolve: either the fingerprint allowlist is embedded data (refreshed by redeploy, so it lags) or the fast-track decision happens at Layer 2 (which has I/O). The simulations must resolve this.

### Step 4: Layer 2 Validates Against Known-Good

If passed on, Layer 2 (Lambda@Edge, with network and file access) validates the request against the known-good profile (the app-coupling). It runs on the request or on cache miss, per configuration.

- Does the request match a known, valid endpoint? (the OpenAPI / known-good profile)
- Is the request shape valid (headers, method, parameters, body)?
- Does the request carry valid authentication if required?
- Cached threat-intel: is the IP known-bad from a cached lookup?
- Opinion rules: accumulate an anomaly score.

**Behaviour:**
- A request to an endpoint not in the known-good profile is invalid (no-invalid-request principle).
- The known-good profile is the source of truth for what is valid.
- Opinion rules accumulate; the anomaly score is computed.

### Step 5: Layer 2 Decides

Layer 2 reaches a decision:

- **Block:** if the request is invalid (not in the known-good profile) or the anomaly score exceeds the threshold, block. Log with reason.
- **Flag:** if suspicious but not over threshold, flag it (log, mark for async analysis), but pass it on.
- **Pass:** if valid, pass to the application.

**Behaviour:**
- An invalid request (not in the profile) is blocked.
- An over-threshold anomaly score blocks.
- A flag does not block but triggers async analysis (Step 7).

### Step 6: The Request Reaches The Application

If passed by Layer 2, the request reaches the protected application (vault server, static site, API).

**Behaviour:**
- Only valid requests reach the application (the no-invalid-request principle holds).
- If the application returns an error that should have been caught earlier (e.g. a 404 for a path that should have been blocked), that is a signal of a Sentinel gap (the no-404s-at-the-API-layer principle).

### Step 7: Layer 3 Analyses (Async, Always)

Regardless of the verdict, Layer 3 (async, out-of-band) may analyse the request as part of its ongoing work:

- Build the evidence graph for the source IP/user.
- Run pattern analysis over a window (detect-before-damage).
- Run LLM interpretation on flagged or ambiguous requests.
- Enrich with threat intelligence (async lookups).
- Generate or update rules; update Layer 1 embedded lists / Layer 2 rules.

**Behaviour:**
- Layer 3 never blocks inline (it is async).
- Layer 3 feeds the fast layers: an attacker detected over a window results in an updated banned list or rule.
- The evidence graph grows.

### Step 8: The Log Lands In S3

Regardless of verdict, the finalised log (with the verdict and reason) lands in S3.

**Behaviour:**
- Every request produces a log in S3.
- The log includes the request ID, verdict, reason, layer, timing.
- Sentinel's job ends here; existing code consumes from S3.

## The Fingerprint And Fast-Track Behaviour

Because fast-tracking is central and raises gaps, its behaviour in detail:

- A user is fingerprinted from stable signals (IP, auth state, session, stable headers).
- Once validated (authenticated, behaving in-profile), the fingerprint goes on the allowlist.
- Subsequent requests matching the fingerprint are fast-tracked (skip deeper checks).
- The fingerprint is time-bounded (valid for a session or a stable-signal window).
- When signals change, the fingerprint is re-validated.

**Open behaviour questions (for simulation):**
- Where is the allowlist stored, and which layer checks it? (the Step 3 gap)
- What exactly constitutes a fingerprint? (which signals, how combined)
- How is a fingerprint validated initially (what makes a user "known-good")?
- What happens when a fast-tracked fingerprint starts behaving out-of-profile? (anomaly in an allowlisted user)

These are the gaps the simulations will probe.

## The Anomaly-Scoring Behaviour

For opinion rules (not deterministic-certain):

- Each opinion rule that matches adds points to an anomaly score.
- The score accumulates across rules for a request.
- If the score exceeds the threshold, the request is blocked.
- This lets multiple low-confidence signals combine into a high-confidence block.

**Open behaviour question (for simulation):** is the anomaly score computed at Layer 1, Layer 2, or both? Given Layer 1 is no-I/O and fast, probably the score accumulates at Layer 1 (cheap checks) and Layer 2 (richer checks), with the threshold checked at Layer 2. The simulations must confirm.

## The Detect-Before-Damage Behaviour

For attackers that are not caught by deterministic rules:

- The attacker makes many requests (probing).
- Their out-of-profile requests are flagged (Step 5) and logged.
- Layer 3 (async) analyses the pattern over a window.
- When the pattern is confirmed as an attack (an out-of-profile request no legitimate client makes), the attacker is blocked: their IP/fingerprint is added to the banned list.
- The block takes effect for subsequent requests (the window before damage).

**Behaviour:**
- Detection happens over a window (minutes), not instantly.
- The success condition is: the attacker is blocked before doing damage.
- The mechanism is: out-of-profile requests reveal the attacker; async analysis confirms; the fast layers are updated to block.

**Open behaviour question (for simulation):** what is the window, and what damage is possible within it? If an attacker can do damage on a single in-profile request, detect-before-damage fails. The simulations must test this against the known-good profile (can a damaging request be in-profile?).

## Why This Document Is The Acceptance Criteria

The voice memo: **this document becomes the acceptance criteria, because when we implement the code, the question is: will the code behave like we described here?**

Every behaviour above is a testable assertion. The implementation is correct if and only if it behaves as described. The simulations (companion documents) run requests against these behaviours and surface where the behaviours are unclear, contradictory, or incomplete. Those gaps get resolved here before code is written, reducing the decisions made during implementation.

## What This Does Not Try To Be

- **Not the code.** It is the behavioural spec the code implements.
- **Not exhaustive of every rule.** It is the execution flow and the key behaviours; specific rules are in the rule graph.
- **Not final.** The simulations will surface gaps that refine this.

## Relationship To Previous Briefs

| Date | Document | Relationship |
|---|---|---|
| 18 May | `v0.27.58__arch-brief__sg-sentinel-architecture-and-data-flows.md` | The architecture this follows the packet through |
| 18 May | `v0.27.58__arch-brief__sg-sentinel-rule-architecture-strategy.md` | The rule graph the behaviour traverses |
| 18 May | `v0.27.58__arch-brief__sg-sentinel-time-as-first-class-dimension.md` | The fingerprint and detect-before-damage behaviours |
| 18 May | `v0.27.58__arch-brief__edge-layer-execution-model-layered-responders.md` | The layers the packet flows through |
| 18 May | `v0.27.58__dev-brief__edge-layer-mvp-visibility-blocking-deployment.md` | The logging and blocking use cases |

---

## Acceptance Criteria

| # | Criterion | Verification |
|---|-----------|-------------|
| 1 | The execution flow (Steps 0-8) is unambiguous | Each step has clear behaviour |
| 2 | Every request is logged at Layer 1 and lands in S3 | Logging behaviour holds |
| 3 | No invalid request reaches the application | The allowlist behaviour holds |
| 4 | Every block has a logged reason | Block behaviour holds |
| 5 | The fingerprint and fast-track behaviour is specified | Fast-track behaviour clear (gaps flagged) |
| 6 | The anomaly-scoring behaviour is specified | Scoring behaviour clear (gaps flagged) |
| 7 | The detect-before-damage behaviour is specified | Detection behaviour clear (gaps flagged) |
| 8 | The open behaviour questions are flagged for simulation | Gaps identified for the tabletop |
| 9 | The implementation can be tested against this spec | It is acceptance criteria |

---

This document is released under the Creative Commons Attribution 4.0 International licence (CC BY 4.0).
