Skip to main content
algovoi-receipt-sentinel sits on top of the receipt verifier and webhook verifier. Feed it every verification result and it fires typed alerts when it detects attack patterns — replay bursts, tamper attempts, or scanning probes.
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

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)

Default rules

Four rules ship by default. All are configurable.
Alert codeWatchesThresholdWindow
TAMPER_DETECTEDINVALID_SIGNATURE, TAMPERED_SIGNATURE, INVALID_JWS_FORMAT560 s
REPLAY_DETECTEDSTALE_SIGNATURE3120 s
SCAN_DETECTEDMISSING_SIGNATURE, MISSING_ENVELOPE, MALFORMED_SIGNATURE1030 s
BURST_FAILUREAll error codes (catch-all)1060 s

Alert object

@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

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

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

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

ImplementationTestsResult
Python unit2323/23
Python vectors1313/13
TypeScript unit2222/22
TypeScript vectors1313/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.
LanguageResult
Python13/13
TypeScript13/13
Go13/13
Rust13/13
Java13/13
PHP13/13
.NET13/13
Ruby13/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:
python vectors/generate_vectors.py

See also