> ## Documentation Index
> Fetch the complete documentation index at: https://docs.algovoi.co.uk/llms.txt
> Use this file to discover all available pages before exploring further.

# Receipt sentinel

> Tenant side sliding window monitor for AlgoVoi verification failures. Detects tamper attempts, replay patterns and scanning bursts. Zero dependencies.

`algovoi-receipt-sentinel` sits on top of the [receipt verifier](/receipt-verifier) and [webhook verifier](/webhook-verifier). Feed it every verification result and it fires typed alerts when it detects attack patterns — replay bursts, tamper attempts, or scanning probes.

```bash theme={null}
pip install algovoi-receipt-sentinel
npm install @algovoi/receipt-sentinel
```

***

## How it works

The sentinel maintains a per-source sliding window for each configured rule. When the number of matching failures inside the window reaches the threshold, a `SentinelAlert` is emitted and the counter resets.

```
verification event → record(source, error_code)
                         │
                    match rules
                         │
              ┌──────────┴──────────┐
              │ evict old entries   │
              │ append timestamp    │
              │ count >= threshold? │
              └──────────┬──────────┘
                         │ yes
                    SentinelAlert
```

***

## Quick start

<CodeGroup>
  ```python Python theme={null}
  from algovoi_receipt_sentinel import Sentinel
  from algovoi_receipt_verifier import verify_receipt, WebhookVerificationError

  sentinel = Sentinel()   # uses default rules

  # In your webhook/receipt handler:
  try:
      event = verify_receipt(...)
      sentinel.record(source=request.remote_addr, error_code=None)  # success clears counter
  except WebhookVerificationError as exc:
      alert = sentinel.record(source=request.remote_addr, error_code=exc.code)
      if alert:
          notify_security_team(alert)
  ```

  ```typescript TypeScript theme={null}
  import { Sentinel } from "@algovoi/receipt-sentinel";
  import { verifyWebhook, WebhookVerificationError } from "@algovoi/webhook-verifier";

  const sentinel = new Sentinel();

  try {
    const event = verifyWebhook({ payload, secret, signatureHeader });
    sentinel.record({ source: req.ip, errorCode: null });
  } catch (err) {
    if (err instanceof WebhookVerificationError) {
      const alert = sentinel.record({ source: req.ip, errorCode: err.code });
      if (alert) notifySecurityTeam(alert);
    }
  }
  ```
</CodeGroup>

***

## Default rules

Four rules ship by default. All are configurable.

| Alert code        | Watches                                                         | Threshold | Window |
| ----------------- | --------------------------------------------------------------- | --------- | ------ |
| `TAMPER_DETECTED` | `INVALID_SIGNATURE`, `TAMPERED_SIGNATURE`, `INVALID_JWS_FORMAT` | 5         | 60 s   |
| `REPLAY_DETECTED` | `STALE_SIGNATURE`                                               | 3         | 120 s  |
| `SCAN_DETECTED`   | `MISSING_SIGNATURE`, `MISSING_ENVELOPE`, `MALFORMED_SIGNATURE`  | 10        | 30 s   |
| `BURST_FAILURE`   | All error codes (catch-all)                                     | 10        | 60 s   |

***

## Alert object

```python theme={null}
@dataclass(frozen=True)
class SentinelAlert:
    code: str              # "TAMPER_DETECTED" | "REPLAY_DETECTED" | "SCAN_DETECTED" | "BURST_FAILURE"
    source: str            # source identifier passed to record()
    count: int             # number of failures that triggered the alert
    window_seconds: int    # rule window
    triggered_at: int      # unix timestamp
    error_codes_seen: frozenset[str]
```

***

## API reference

### Python

```python theme={null}
from algovoi_receipt_sentinel import Sentinel, SentinelRule, SentinelAlert

# Default rules
sentinel = Sentinel()

# Custom rules
sentinel = Sentinel(rules=[
    SentinelRule(
        alert_code="TAMPER_DETECTED",
        error_codes=frozenset({"INVALID_SIGNATURE"}),
        threshold=3,
        window_seconds=60,
    ),
])

# Record an event
alert: SentinelAlert | None = sentinel.record(
    source="10.0.0.1",        # any string identifier — IP, tenant ID, etc.
    error_code="INVALID_SIGNATURE",  # or None for success
    timestamp=int(time.time()),      # optional; defaults to now
)

# Reset counters
sentinel.reset("10.0.0.1")   # reset one source
sentinel.reset()              # reset all
```

### TypeScript

```typescript theme={null}
import { Sentinel, SentinelRule, SentinelAlert } from "@algovoi/receipt-sentinel";

const sentinel = new Sentinel();  // default rules

const alert: SentinelAlert | null = sentinel.record({
  source: "10.0.0.1",
  errorCode: "INVALID_SIGNATURE",  // or null for success
  timestamp: Math.floor(Date.now() / 1000),  // optional
});

sentinel.reset("10.0.0.1");  // reset one source
sentinel.reset();             // reset all
```

***

## Custom rules

```python theme={null}
from algovoi_receipt_sentinel import Sentinel, SentinelRule

sentinel = Sentinel(rules=[
    # Tighter tamper window for high-value endpoints
    SentinelRule(
        alert_code="TAMPER_DETECTED",
        error_codes=frozenset({"INVALID_SIGNATURE", "TAMPERED_SIGNATURE"}),
        threshold=3,
        window_seconds=30,
    ),
    # Alert on any 20 failures in 5 minutes
    SentinelRule(
        alert_code="BURST_FAILURE",
        error_codes=frozenset({
            "INVALID_SIGNATURE", "TAMPERED_SIGNATURE", "STALE_SIGNATURE",
            "MISSING_SIGNATURE", "MALFORMED_SIGNATURE", "INVALID_PAYLOAD",
        }),
        threshold=20,
        window_seconds=300,
    ),
])
```

***

## Test results

| Implementation     | Tests | Result |
| ------------------ | ----- | ------ |
| Python unit        | 23    | 23/23  |
| Python vectors     | 13    | 13/13  |
| TypeScript unit    | 22    | 22/22  |
| TypeScript vectors | 13    | 13/13  |

**Python 36/36 · TypeScript 35/35**

### 8-language cross-validation

**104/104** agreements — all 8 implementations produce identical alert decisions from the same event sequences.

| Language   | Result |
| ---------- | ------ |
| Python     | 13/13  |
| TypeScript | 13/13  |
| Go         | 13/13  |
| Rust       | 13/13  |
| Java       | 13/13  |
| PHP        | 13/13  |
| .NET       | 13/13  |
| Ruby       | 13/13  |

***

## Vectors

13 fixtures in `vectors/` — 8 that fire alerts and 5 that stay quiet. Each is a self-contained JSON sequence of events with expected outcomes. Regenerate:

```bash theme={null}
python vectors/generate_vectors.py
```

***

## See also

* [Webhook verifier](/webhook-verifier) — validates `X-AlgoVoi-Signature` headers
* [Receipt verifier](/receipt-verifier) — validates JWS compliance receipts
* [Notifications](/concepts/notifications) — webhook delivery and retry schedule
* [Package suite](/package-suite) — full open-source package listing
