telehost / jarvis-client
Jarvis monitoring agent client for Laravel — expose health metrics to the Jarvis autonomous monitoring agent via a standardized /jarvis/health endpoint
Requires
- php: ^8.1
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0|^11.0
README
Expose your Laravel app to Jarvis — the autonomous monitoring agent that watches your infrastructure and alerts you via WhatsApp when things go wrong.
What it does
Jarvis is an AI agent that polls your app every 15 minutes and asks "is everything OK?". If anything is off — queue backlog, dropped users, rising error rate, failed jobs — it sends you a WhatsApp message with evidence-based suggestions.
This package exposes a standardized /jarvis/health endpoint that Jarvis polls. You define what metrics to expose, the package handles the rest (auth, formatting, error isolation).
Why you want it
| Without Jarvis | With Jarvis |
|---|---|
| You SSH in to check if things are OK | Jarvis tells you via WhatsApp when they're not |
| Errors pile up until users complain | Jarvis catches trends before customers notice |
| You learn Grafana/Prometheus | You define 5 closures and you're done |
| $50+/month for Datadog | Free tier, $19/mo Pro, with WhatsApp alerts in Spanish |
Installation
composer require telehost/jarvis-client
Publish the config:
php artisan vendor:publish --tag=jarvis-config
Quick Start
1. Get your token from https://jarvis.telehost.net/dashboard (register your app → copy token)
2. Set it in your .env:
JARVIS_TOKEN=jrv_app_abc123...
3. Edit config/jarvis.php:
return [ 'token' => env('JARVIS_TOKEN'), 'metrics' => [ 'users_active' => fn () => \App\Models\User::where('suspended', false)->count(), 'revenue_today' => fn () => \App\Models\Order::whereDate('created_at', today())->sum('total'), 'queue' => fn () => [ 'pending' => \DB::table('jobs')->count(), 'failed_1h' => \DB::table('failed_jobs') ->where('failed_at', '>=', now()->subHour()) ->count(), ], ], 'alerts' => [ 'disk_low' => fn () => disk_free_space('/') < 1_000_000_000 ? ['severity' => 'critical', 'message' => 'Less than 1GB disk free'] : null, ], ];
4. That's it. Jarvis will start monitoring within minutes.
Verify locally:
curl -H "Authorization: Bearer $JARVIS_TOKEN" http://localhost:8000/jarvis/health
You should see:
{
"app": "My App",
"version": "1.0",
"timestamp": "2026-04-19T14:30:00Z",
"status": "healthy",
"metrics": {
"users_active": 1247,
"revenue_today": 82340.50,
"queue": { "pending": 3, "failed_1h": 0 }
},
"alerts": [],
"custom": []
}
Advanced
Custom route path
JARVIS_PATH=/internal/jarvis
Or disable auto-registration and define it yourself:
// config/jarvis.php 'auto_register_route' => false, // routes/web.php Route::get('/custom/health/path', \TeleHost\Jarvis\JarvisHealthController::class) ->middleware([\TeleHost\Jarvis\JarvisAuth::class]);
Per-request middleware
// config/jarvis.php 'middleware' => [ 'throttle:30,1', // 30 requests per minute from Jarvis ],
Dynamic metric registration
From a service provider:
use TeleHost\Jarvis\JarvisManager; app(JarvisManager::class) ->metric('stripe_balance', fn () => \Stripe\Balance::retrieve()->available[0]->amount / 100) ->alert('subscription_renewal', function () { $expiring = Subscription::whereDate('expires_at', today()->addDays(7))->count(); return $expiring > 10 ? ['severity' => 'warning', 'message' => "{$expiring} subs expire this week"] : null; });
Error isolation
If any metric closure throws, only that metric fails — the rest continue:
{
"metrics": {
"users_active": 1247,
"external_api_count": { "error": "Connection timeout" },
"revenue_today": 82340.50
}
}
Jarvis handles the error gracefully and reports it to you.
What Jarvis expects
Your closures can return:
- Scalars:
int,float,string,bool - Arrays (for grouped metrics):
'whatsapp' => fn () => [ 'connected' => 15, 'disconnected' => 2, 'by_integration' => ['baileys' => 13, 'meta' => 4], ],
- null (metric skipped this cycle)
For alerts, return:
['severity' => 'info'|'warning'|'critical', 'message' => 'Human-readable text']- Or
null(no alert)
Security
- The endpoint is protected by a bearer token (NEVER commit it)
- Only Jarvis has the token — anyone else gets 401
- Use HTTPS in production (Laravel defaults to it behind most load balancers)
- Rotate the token anytime from the dashboard
Common metrics to expose
Tailor to your business, but here are patterns that work well:
'metrics' => [ // Business health 'users_active' => fn () => User::where('last_login_at', '>=', now()->subWeek())->count(), 'new_signups_24h' => fn () => User::where('created_at', '>=', now()->subDay())->count(), 'revenue_today' => fn () => Transaction::whereDate('created_at', today())->sum('amount'), // Infrastructure 'queue' => fn () => [ 'pending' => DB::table('jobs')->count(), 'failed_1h' => DB::table('failed_jobs')->where('failed_at', '>=', now()->subHour())->count(), ], // External dependencies 'db_latency_ms' => fn () => measureDbLatency(), 'stripe_reachable' => fn () => pingStripe(), // Error rates 'error_rate_pct' => fn () => calculateErrorRate(), ],
Testing
composer test
License
MIT. See LICENSE.md.
Who builds this
TeleHost C.A. — a Venezuelan infrastructure company that got tired of paying $50+/month for monitoring and built its own.
- Website: https://jarvis.telehost.net
- Issues: https://github.com/telehostca/jarvis-client-php/issues
- Related: @telehost/jarvis-client (Node.js version)