PHPackages                             padosoft/laravel-rebel-channels - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. padosoft/laravel-rebel-channels

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

padosoft/laravel-rebel-channels
===============================

Channel/provider abstraction (SMS/WhatsApp/voice) for Laravel Rebel: verification routing with fallback, cooldown, multi-dimensional rate limiting, and anti toll-fraud/IRSF defences. Part of padosoft/laravel-rebel-\*.

v0.1.2(1mo ago)0228↓90%6MITPHPPHP ^8.3CI passing

Since Jun 3Pushed 1mo agoCompare

[ Source](https://github.com/padosoft/laravel-rebel-channels)[ Packagist](https://packagist.org/packages/padosoft/laravel-rebel-channels)[ Docs](https://github.com/padosoft/laravel-rebel-channels)[ RSS](/packages/padosoft-laravel-rebel-channels/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (18)Versions (6)Used By (6)

Laravel Rebel — Channels
========================

[](#laravel-rebel--channels)

> Official documentation:

> **One safe, fault-tolerant pipe for phone verifications (SMS / WhatsApp / voice).** You ask "verify this number"; Rebel Channels runs it through a bot gate, anti toll-fraud/IRSF defences, a per-number rate limit, and **provider fallback** — then audits every decision (number always HMAC'd). It is provider-agnostic: plug in `laravel-rebel-channel-twilio` (or your own). Part of the `padosoft/laravel-rebel-*` suite.

 [![Laravel Rebel](resources/screenshoots/Laravel-Rebel-banner.png)](resources/screenshoots/Laravel-Rebel-banner.png)

 [![Laravel 12|13](https://camo.githubusercontent.com/9e9b743bcbf97a29fe735334a4a8e906d05d60310969905af6607cef8da30138/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d313225323025374325323031332d4646324432303f7374796c653d666c61742d737175617265266c6f676f3d6c61726176656c266c6f676f436f6c6f723d7768697465)](https://camo.githubusercontent.com/9e9b743bcbf97a29fe735334a4a8e906d05d60310969905af6607cef8da30138/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d313225323025374325323031332d4646324432303f7374796c653d666c61742d737175617265266c6f676f3d6c61726176656c266c6f676f436f6c6f723d7768697465) [![PHP 8.3+](https://camo.githubusercontent.com/6aa777dd33ef43fbef727d8187b578003a61e5dc41bbc958b0938c996cdc92f2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e33253230253743253230382e34253230253743253230382e352d3737374242343f7374796c653d666c61742d737175617265266c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://camo.githubusercontent.com/6aa777dd33ef43fbef727d8187b578003a61e5dc41bbc958b0938c996cdc92f2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e33253230253743253230382e34253230253743253230382e352d3737374242343f7374796c653d666c61742d737175617265266c6f676f3d706870266c6f676f436f6c6f723d7768697465) [![PHPStan max](https://camo.githubusercontent.com/4b9a3c97d76534abb905e64bd9e5bb9f13fe68e962071e0ccbbe7b629112f11c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6d61782d3241364644423f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/4b9a3c97d76534abb905e64bd9e5bb9f13fe68e962071e0ccbbe7b629112f11c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6d61782d3241364644423f7374796c653d666c61742d737175617265) [![Pest 4](https://camo.githubusercontent.com/9b9da1d7d243a7465ab338e9374e47300a7fe2e26b5c291e7e3c95b53153789a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d50657374253230342d3232433535453f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/9b9da1d7d243a7465ab338e9374e47300a7fe2e26b5c291e7e3c95b53153789a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d50657374253230342d3232433535453f7374796c653d666c61742d737175617265) [![anti IRSF](https://camo.githubusercontent.com/c5fce1d90fd781b6e38082c4b77768aded88fc60526759ace76889f9fc23f0ec/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f616e74692d2d495253462d6275696c742d2d696e2d3842354346363f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/c5fce1d90fd781b6e38082c4b77768aded88fc60526759ace76889f9fc23f0ec/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f616e74692d2d495253462d6275696c742d2d696e2d3842354346363f7374796c653d666c61742d737175617265) [![MIT](https://camo.githubusercontent.com/ac049ef4e7a0b7196b09add6ac2d4f180e544c0ac779c2b2ac2fd2723a209579/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/ac049ef4e7a0b7196b09add6ac2d4f180e544c0ac779c2b2ac2fd2723a209579/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666c61742d737175617265)

---

Table of contents
-----------------

[](#table-of-contents)

- [What it is (and what it is not)](#what-it-is-and-what-it-is-not)
- [Quick glossary (one minute)](#quick-glossary-one-minute)
- [Why Rebel Channels — the moats](#why-rebel-channels--the-moats)
- [Rebel Channels vs the alternatives](#rebel-channels-vs-the-alternatives)
- [How it works (step by step)](#how-it-works-step-by-step)
- [Installation (junior-proof)](#installation-junior-proof)
- [Configuration (every option)](#configuration-every-option)
- [Usage examples](#usage-examples)
- [`.env.example`](#envexample)
- [Security notes](#security-notes)
- [Testing &amp; License](#testing--license)

---

What it is (and what it is not)
-------------------------------

[](#what-it-is-and-what-it-is-not)

**It is** the *routing + defence* layer for sending one-time phone verifications. It does not talk to Twilio (or any vendor) itself — it defines the contracts and the guarded flow, and delegates the actual send to a **provider** package such as [`laravel-rebel-channel-twilio`](https://github.com/padosoft/laravel-rebel-channel-twilio).

**It is not** an OTP generator (that's the provider's job, e.g. Twilio Verify), and it is not tied to one vendor — register several providers and it will **fall back** between them.

Depends on [`padosoft/laravel-rebel-core`](https://github.com/padosoft/laravel-rebel-core).

---

Quick glossary (one minute)
---------------------------

[](#quick-glossary-one-minute)

TermIn plain words**Verification**"Send a code to this number and let me check what the user typed."**Provider**The vendor that actually sends/checks the code (e.g. Twilio Verify).**Channel**The medium: `sms`, `whatsapp`, `voice`.**Fallback**If provider A is down, automatically try provider B.**IRSF / toll-fraud**International Revenue Share Fraud: attackers pump OTP traffic toward premium-rate numbers to cash in. Expensive if undefended.**Geo allowlist**"Only send to these country prefixes" — the single most effective IRSF defence.**Per-prefix cap**A velocity circuit breaker per number prefix, so a sudden spike toward one range trips.**Bot gate**A check (reCAPTCHA/Turnstile…) that a human, not a script, triggered the send.---

Why Rebel Channels — the moats
------------------------------

[](#why-rebel-channels--the-moats)

★WhatIn short★★★**IRSF / toll-fraud defences built in**Geo allowlist, prefix blocklist, and a per-prefix velocity circuit breaker — the stuff that saves real money.★★★**Provider fallback**Register Twilio + a backup; an outage on one silently rolls over to the next.★★★**Tamper-evident references**The `check()` handle is HMAC-signed and **bound to the phone** — no provider/channel injection, no cross-user replay.★★**Bot gate + per-number rate limit**Two more layers before a single euro is spent on a send.★★**Audited, privacy-first**Every decision is recorded with the number **HMAC'd** (never in clear).★★**Vendor-agnostic**Swap or combine providers without touching your app code.★**Safe defaults**Ships a cache-backed rate limiter and a no-op bot gate so it just works, then hardens as you configure it.---

Rebel Channels vs the alternatives
----------------------------------

[](#rebel-channels-vs-the-alternatives)

Sending a verification SMS, compared:

Capability**Rebel Channels**ShopifyTwilio Verify SDK (direct)Hand-rolled SMS + OTPSend/check a code✅✅✅✅**Provider fallback** on outage✅❌❌❌Geo allowlist (IRSF)✅❌➖ (manual in console)❌Per-prefix velocity circuit breaker✅❌❌❌Per-number rate limit / cooldown✅➖➖❌Bot gate before spending✅➖❌❌Reference signed + phone-bound (anti replay/injection)✅❌❌❌Unified audit trail, number HMAC'd✅❌❌❌Vendor-agnostic / swappable✅❌❌➖> Legend: ✅ built-in · ➖ partial / manual / hosted-only · ❌ not available. Twilio Verify is a great provider — Rebel Channels wraps it (and others) with the routing, fraud and audit layer your app would otherwise have to build and maintain itself. Shopify is a closed, hosted commerce platform: it sends its own customer OTPs but exposes none of these low-level primitives to you — you can't self-host it, swap providers, or configure its fraud controls, so it's a black box for this use case.

---

How it works (step by step)
---------------------------

[](#how-it-works-step-by-step)

```
$router->start($phone, Channel::Sms, $context, $botToken)
        |
        v
[1] Bot gate        -> BotProtection::passes()?      no -> blocked('bot_denied')
[2] Fraud guard     -> blocklist / geo allowlist / per-prefix cap?  no -> blocked(reason)
[3] Rate limit      -> too many sends to this number?  yes -> blocked('rate_limited')
        |  (the attempt is counted here, BEFORE trying providers, so outages can't bypass it)
        v
[4] Provider fallback -> try providers in order; first that accepts wins
        |
        v
returns a PENDING result with a SIGNED, phone-bound reference

...later...

$router->check($phone, $code, $reference, $context)
        |
        v
verify the reference signature + phone binding -> delegate to the provider -> approved / failed

```

---

Installation (junior-proof)
---------------------------

[](#installation-junior-proof)

> Prerequisites: Laravel **12 or 13**, PHP **8.3+**, and `padosoft/laravel-rebel-core`. You also want at least one provider, e.g. `padosoft/laravel-rebel-channel-twilio`.

```
composer require padosoft/laravel-rebel-channels
php artisan vendor:publish --tag="rebel-channels-config"
```

Then register a provider (the Twilio package does this for you) and you're ready:

```
use Padosoft\Rebel\Channels\Enums\Channel;
use Padosoft\Rebel\Channels\Routing\VerificationRouter;
use Padosoft\Rebel\Core\Context\SecurityContext;
use Padosoft\Rebel\Core\Identifiers\PhoneIdentifier;

$router = app(VerificationRouter::class);
$start  = $router->start(PhoneIdentifier::from('+39 333 1234567'), Channel::Sms, SecurityContext::fromRequest($request));
```

---

Configuration (every option)
----------------------------

[](#configuration-every-option)

File `config/rebel-channels.php`:

KeyDefaultWhat it does`providers``[]`Provider keys to try, in fallback order. Empty = every registered provider that supports the channel.`rate_limit.max_per_window``5`Max verification sends per phone+channel within the window.`rate_limit.window_seconds``3600`The rate-limit window length.`fraud.allowed_prefixes``[]`If non-empty, ONLY numbers starting with one of these E.164 prefixes are allowed (geo allowlist).`fraud.blocked_prefixes``[]`Numbers starting with one of these are always blocked.`fraud.per_prefix.length``3`How many leading chars of the E.164 number form the velocity bucket (`3` → `+39`).`fraud.per_prefix.max_per_window``0`Per-prefix send cap (`0` disables the circuit breaker).`fraud.per_prefix.window_seconds``3600`The per-prefix window length.To actually gate bots, bind your own implementation of the core `BotProtection` contract (reCAPTCHA/Turnstile…); otherwise a permissive no-op default is used.

---

Usage examples
--------------

[](#usage-examples)

**1. Start + check a verification**

```
$start = $router->start($phone, Channel::Sms, $ctx);
// store $start->reference (already signed) for the check step

$result = $router->check($phone, $request->string('code'), $reference, $ctx);
if ($result->approved()) {
    // the number is verified
}
```

**2. WhatsApp with SMS fallback** — list both providers; the router rolls over:

```
// config/rebel-channels.php
'providers' => ['twilio', 'vonage'],
```

**3. Lock down to your markets (kills most IRSF)**

```
'fraud' => [
    'allowed_prefixes' => ['+39', '+1', '+44'], // only Italy, US, UK
    'per_prefix' => ['length' => 3, 'max_per_window' => 50, 'window_seconds' => 3600],
],
```

**4. Pass a bot token** (verified by your bound `BotProtection`):

```
$router->start($phone, Channel::Sms, $ctx, $request->string('captcha_token'));
```

**5. Inspect the audit trail**

```
use Padosoft\Rebel\Core\Models\RebelAuthEvent;

RebelAuthEvent::query()->where('event_type', 'channel.verification.blocked')->get(); // see WHY sends were stopped
```

---

`.env.example`
--------------

[](#envexample)

```
REBEL_CHANNELS_RL_MAX=5
REBEL_CHANNELS_RL_WINDOW=3600
REBEL_CHANNELS_PREFIX_LEN=3
REBEL_CHANNELS_PREFIX_MAX=0
REBEL_CHANNELS_PREFIX_WINDOW=3600
```

---

Security notes
--------------

[](#security-notes)

- **IRSF defence in depth**: geo allowlist + prefix blocklist + per-prefix velocity cap.
- **Rate limit can't be bypassed**: the attempt is counted *before* providers are tried, so forcing provider failures does not grant unlimited sends.
- **Tamper-evident, phone-bound references**: the `check()` handle is HMAC-signed and tied to the number; forged or cross-user references are rejected.
- **Privacy-first audit**: every routing decision is recorded with the phone number HMAC'd; reasons are generic machine codes, never the OTP.
- **Atomic rate limiting**: the default limiter uses the cache's atomic increment with a TTL set once per window (no slide, no under-count).

---

🔋 Vibe coding with batteries included
-------------------------------------

[](#-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 &amp; License
---------------------

[](#testing--license)

```
composer test      # Pest (router fallback, rate limit, bot gate, fraud guard, signed references, audit)
composer phpstan   # static analysis, level max
composer pint      # code style
```

**License:** MIT — see [LICENSE](LICENSE). Part of the [`padosoft/laravel-rebel`](https://github.com/padosoft) suite.

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance91

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

51d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/10467699?v=4)[Lorenzo](/maintainers/lopadova)[@lopadova](https://github.com/lopadova)

---

Top Contributors

[![lopadova](https://avatars.githubusercontent.com/u/10467699?v=4)](https://github.com/lopadova "lopadova (8 commits)")

---

Tags

laravelsecurityAuthenticationpadosoftRebel

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/padosoft-laravel-rebel-channels/health.svg)

```
[![Health](https://phpackages.com/badges/padosoft-laravel-rebel-channels/health.svg)](https://phpackages.com/packages/padosoft-laravel-rebel-channels)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k102.4M1.5k](/packages/spatie-laravel-permission)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

45955.7k](/packages/harris21-laravel-fuse)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[masterix21/laravel-licensing

Laravel licensing package with polymorphic assignment to any model, activation keys, expirations/renewals, and seat control via LicenseUsage. Supports offline verification with public-key–signed tokens, a CLI to generate/rotate/revoke keys, and an extensible architecture via config and contracts.

1613.3k4](/packages/masterix21-laravel-licensing)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
