padosoft/laravel-rebel-ai-guard

Anomaly detection + AI security copilot for Laravel Rebel: deterministic rules detect anomaly cases; the optional AI only explains/suggests (sanitized prompts, no PII/OTP, human review). Part of padosoft/laravel-rebel-*.

Maintainers

Package info

github.com/padosoft/laravel-rebel-ai-guard

pkg:composer/padosoft/laravel-rebel-ai-guard

Statistics

Installs: 191

Dependents: 2

Suggesters: 1

Stars: 0

Open Issues: 0

v0.1.2 2026-06-03 19:35 UTC

README

Deterministic anomaly detection, with an AI that explains — never decides. Fixed rules open anomaly cases (e.g. OTP bombing) from your audit log; an optional LLM can then describe a case in plain language. The AI only ever sees sanitized prompts (no PII, no OTPs, no tokens) and its output is advisory — humans review, destructive actions stay manual. Part of the padosoft/laravel-rebel-* suite.

Laravel Rebel

Laravel 12|13 PHP 8.3+ PHPStan max Pest 4 AI explains MIT

Table of contents

What it is

Two things, deliberately separated:

  1. Deterministic anomaly detectionAnomalyDetector scans rebel_auth_events and opens anomaly cases from fixed, auditable rules (v0.1.0: OTP bombing; more rules to come). No black box decides anything.
  2. An AI explainer (optional)AiExplainer can ask an LLM you provide to describe a case for an operator. It is advisory only, sees only sanitized input, and is absent unless you bind an AiClient.

Depends on padosoft/laravel-rebel-core.

The golden rule

The rules decide. The AI explains. Humans approve anything destructive.

The AI never opens, closes, or mitigates a case. It turns a case's signals into a sentence. Everything that acts is deterministic and auditable.

Why this package

What In short
★★★ Deterministic, auditable detection Cases come from fixed rules you can read and test — not a model's whim.
★★★ AI input is sanitized Emails, phones, OTP/digit runs (incl. Unicode) and Bearer/Basic/JWT/key tokens are scrubbed before any prompt leaves the app.
★★★ AI is advisory + injection-resistant The system prompt forbids deciding and treats case data as opaque (no prompt injection).
★★ Optional AI No AiClient bound? Detection still works; the explainer just returns null.
★★ Idempotent + tenant-aware Cases de-duplicate by a stable key; re-runs update in place; rows are tenant-scoped.
★★ Bring your own model Bind OpenAI, Anthropic, or a local model behind a one-method contract.

Rebel AI Guard vs the alternatives

Capability Rebel AI Guard Shopify "AI fraud" black boxes DIY log scripts
Deterministic, testable rules you own
AI explains, never decides n/a
Prompt sanitization (no PII/secret leak to LLM) n/a
Prompt-injection-resistant system prompt n/a
Works with NO AI configured
Idempotent, de-duplicated cases
Self-hosted over your own audit log
Tenant-aware + audit-native (your app)

Legend: ✅ built-in · ➖ partial / hosted-only / not exposed to you · ❌ not available.

Note: Shopify is a hosted, closed commerce platform — it runs its own opaque fraud scoring on its checkout, but never gives you a deterministic anomaly engine, a sanitized explain-not-decide AI, or rules you can read, test, and self-host over your own audit log.

Installation

composer require padosoft/laravel-rebel-ai-guard
php artisan vendor:publish --tag="rebel-ai-guard-migrations"
php artisan migrate

Usage

Detection runs automatically. Out of the box the package schedules the rebel:detect-anomalies command hourly, so anomaly cases appear in your admin panel on their own — you don't have to call the detector. The cadence is fully configurable (see Scheduling). Just make sure Laravel's scheduler is running (* * * * * php artisan schedule:run in cron, as usual).

You can also run it by hand at any time:

php artisan rebel:detect-anomalies               # scans the last 1440 min (config default)
php artisan rebel:detect-anomalies --lookback=60 # scan only the last hour
php artisan rebel:detect-anomalies \
  --from="2026-06-01T00:00:00" --to="2026-06-01T06:00:00" # explicit window

Or call the detector directly (e.g. from your own job):

use Padosoft\Rebel\AiGuard\Detection\AnomalyDetector;

$opened = app(AnomalyDetector::class)->detect(
    now()->subHour(),
    now(),
); // returns how many cases were opened/updated

Scheduling

Config key Env Default Effect
detect.schedule REBEL_AIGUARD_SCHEDULE true Auto-register the schedule. Set false to opt out and wire your own.
detect.frequency REBEL_AIGUARD_FREQUENCY hourly How often the scheduled command runs. A whitelisted cadence name or a raw cron expression.
detect.lookback_minutes REBEL_AIGUARD_LOOKBACK 1440 Default scan window (minutes, ending "now"). The scheduled run passes this explicitly; --lookback/--from/--to override per manual run.

The schedule is only registered in console context, so it never affects HTTP requests.

Frequency accepts either a whitelisted cadence name or a raw 5-field cron expression:

  • Cadence names: everyMinute, everyTwoMinutes, everyThreeMinutes, everyFourMinutes, everyFiveMinutes, everyTenMinutes, everyFifteenMinutes, everyThirtyMinutes, hourly, daily, weekly, monthly, quarterly, yearly (case-insensitive).
  • Cron expression: anything that looks like */15 * * * * is applied via the scheduler's ->cron(). Only whitelisted names are ever called as methods — an unrecognised value that is not a valid cron expression falls back to hourly (it never calls an arbitrary method).
REBEL_AIGUARD_FREQUENCY=everyFifteenMinutes   # cadence name
# REBEL_AIGUARD_FREQUENCY="*/15 9-17 * * 1-5" # or a raw cron (every 15 min, 9-17, Mon-Fri)

Running over an explicit window (--from / --to) and simulating cron

--lookback=<minutes> scans a window ending "now". For a precise window, pass ISO-8601 --from and --to (when both are given they override --lookback; if you pass only --from, --to defaults to "now"). Invalid datetimes — or a --to that is not after --from — print an error and exit non-zero.

# Simulate the hourly cron run for a specific past hour:
php artisan rebel:detect-anomalies \
  --from="2026-06-01T09:00:00" --to="2026-06-01T10:00:00"

# Backfill a whole day in one shot:
php artisan rebel:detect-anomalies \
  --from="2026-06-01T00:00:00" --to="2026-06-02T00:00:00"

# From a point in time until "now":
php artisan rebel:detect-anomalies --from="2026-06-01T00:00:00"

Because the scheduled invocation simply runs rebel:detect-anomalies --lookback=<configured>, a manual run with the same window behaves identically to the cron run.

Explain a case (optional AI):

use Padosoft\Rebel\AiGuard\AiExplainer;
use Padosoft\Rebel\AiGuard\Models\AnomalyCase;

$explainer = app(AiExplainer::class);
$case = AnomalyCase::query()->findOrFail($id);

$text = $explainer->explain($case); // null if no AiClient is bound

Bring your own model — implement and bind the contract:

use Padosoft\Rebel\AiGuard\Contracts\AiClient;

$this->app->singleton(AiClient::class, MyOpenAiClient::class);

Security notes

  • No PII/secret to the LLM: PromptSanitizer scrubs emails, phone numbers, 4+ digit runs (incl. Unicode), and Bearer/Basic/JWT/sk-/ghp_/xox* tokens before sending.
  • No decisions by AI: the system prompt forbids deciding/recommending destructive actions and instructs the model to treat case data as opaque (prompt-injection resistant).
  • Deterministic core: cases come from fixed rules; the audit log already stores only HMAC'd identifiers, so cases carry hashes, not raw PII.
  • Tenant-scoped, idempotent: re-running the detector updates open cases instead of duplicating them.

.env.example

REBEL_AIGUARD_OTP_BOMBING_THRESHOLD=10
REBEL_AIGUARD_SCHEDULE=true
REBEL_AIGUARD_FREQUENCY=hourly
REBEL_AIGUARD_LOOKBACK=1440

🔋 Vibe coding with batteries included

This package ships AI batteries — so you (and your AI agent) can extend it correctly on the first try:

  • CLAUDE.md — a concise AI working guide (purpose, conventions, architecture, how to extend, Definition of Done). Plain Markdown, so Claude Code, Cursor, Copilot and Codex all read it.
  • AGENTS.md — the agent/workflow contract (branch → PR → CI → tag/release, the gates).
  • .claude/skills/ — invocable skills (at least rebel-package-dev) encoding the suite's TDD loop, the PHPStan-level-max recipes, the security/telemetry rules, and the release discipline.

Open the repo in your AI editor and just start — the rules, guardrails and extension recipes come with it. PRs that follow the shipped CLAUDE.md pass CI (PHPStan max + Pest + Pint) and review the first time around.

Testing & License

composer test      # Pest (detection, idempotency, severity, sanitizer, AI explainer)
composer phpstan   # static analysis, level max
composer pint      # code style

License: MIT — see LICENSE. Part of the padosoft/laravel-rebel suite.