devuni / notifier-agent
Laravel agent for the Devuni Notifier platform - encrypted backups and announcements.
Requires
- php: ^8.4
- guzzlehttp/guzzle: ^7.11.1
- illuminate/console: ^12.55.0 || ^13.14
- illuminate/contracts: ^12.55.0 || ^13.14
- illuminate/http: ^12.55.0 || ^13.14
- illuminate/queue: ^12.55.0 || ^13.14
- illuminate/routing: ^12.55.0 || ^13.14
- illuminate/support: ^12.55.0 || ^13.14
- laravel/prompts: ^0.3.18
Requires (Dev)
- filament/support: ^5.6
- larastan/larastan: ^3.10.0
- laravel/pint: ^1.29.1
- mockery/mockery: ^1.6.12
- nunomaduro/collision: ^8.9.4
- orchestra/testbench: ^11.0.0 || ^12.0
- pestphp/pest: ^4.7.2
- pestphp/pest-plugin-laravel: ^4.1.0
- rector/rector: ^2.4.5
Suggests
- filament/support: Auto-injects announcement banners into FilamentPHP dashboards; not required when using a custom (SPA) dashboard.
Conflicts
This package is auto-updated.
Last update: 2026-06-22 22:24:55 UTC
README
Encrypted database & storage backups for Laravel apps, shipped to the Devuni Notifier central server. AES-256 ZIPs, chunked HTTPS upload, token auth, queue support. Supports MySQL, MariaDB, PostgreSQL, and YugabyteDB (via YSQL).
How it works
┌─────────────────────┐ encrypted ZIP ┌─────────────────────┐
│ Your Laravel app │ ───── chunked upload ─────▶ │ notifier.devuni.cz │
│ (this package) │ (X-Notifier-Token) │ (central server) │
└─────────────────────┘ └─────────────────────┘
│ │
│ mysqldump + storage/app/public │ stores + monitors
│ → AES-256 ZIP │ → sends alerts
▼ ▼
local temp file long-term backup archive
(cleaned up after upload)
Heads up: This is the client side of the Devuni Notifier platform. Without a central server configured via
NOTIFIER_URL, there's nowhere to send backups. If you don't have it, try spatie/laravel-backup instead.
Install
composer require devuni/notifier-agent php artisan vendor:publish --tag="notifier-config" php artisan notifier:install # interactive .env wizard php artisan notifier:check # verify setup (env, DB, 7z, mysqldump, URL)
Requirements: PHP 8.4+, Laravel ^12.55 or ^13.14, the right dump tool for your DB (mysqldump / mariadb-dump for MySQL & MariaDB, pg_dump or ysql_dump for PostgreSQL & YugabyteDB), and p7zip-full (recommended) or PHP zip extension.
Migrating from devuni/notifier-package
This package is the successor of devuni/notifier-package - same namespace (Devuni\Notifier\), same env vars (NOTIFIER_*), same artisan commands, same config/notifier.php, same route prefix. The swap is one step:
composer remove devuni/notifier-package composer require devuni/notifier-agent
Nothing else changes - your published config, .env, and scheduled notifier:* commands keep working as-is. Don't be confused by the version number: notifier-agent restarts at 1.0.0, but it contains everything notifier-package 2.8.0 had (and newer).
Never install both packages side by side - they share the
Devuni\Notifier\namespace. Composer refuses the combination (aconflictrule guards it), so if you see a conflict error duringcomposer require, run thecomposer removestep first.
Heads-up when coming from notifier-package ≤ 2.7.x: version 2.8.0+ introduced announcements, which are enabled by default (see below). If you don't want the banner, set NOTIFIER_ANNOUNCEMENTS_ENABLED=false.
Usage
Scheduled backups (recommended)
Add to routes/console.php:
use Illuminate\Support\Facades\Schedule; Schedule::command('notifier:database-backup')->dailyAt('02:00')->onOneServer(); Schedule::command('notifier:storage-backup')->weeklyOn(0, '03:00')->onOneServer();
On demand
php artisan notifier:database-backup php artisan notifier:storage-backup
HTTP API
Trigger backups from an external scheduler. Rate-limited to 10 req/hour.
curl -X POST https://your-app.com/api/notifier/backup \ -H "X-Notifier-Token: your-token" \ -d "type=backup_database" # or backup_storage
On failure the response returns an opaque error_id (UUID) - the full detail (stack trace, mysqldump/7z stderr) stays in your backup log channel. Grep logs for the UUID to correlate.
Announcements
Pull this site's maintenance/announcement notices from the central server and show them in your dashboard. On by default - it costs nothing until NOTIFIER_URL is set (the service no-ops without a target). Disable with NOTIFIER_ANNOUNCEMENTS_ENABLED=false.
Filament hosts (the common case): when Filament is installed, the notices are auto-injected as a banner into every panel page via a render hook - nothing to place. Move the default spot with NOTIFIER_ANNOUNCEMENTS_FILAMENT_HOOK (default panels::content.start; also panels::body.start, panels::topbar.end), or turn the auto-injection off with NOTIFIER_ANNOUNCEMENTS_FILAMENT=false. Works across Filament v3/v4/v5.
Per-announcement placement: each announcement carries a target from the control plane. A Filament announcement is routed to the render hook named in its target; with no target it falls to the default hook above. The package only injects at the hooks you opt into, so to support more than one position list them all (comma-separated) in NOTIFIER_ANNOUNCEMENTS_RENDER_HOOKS - the default hook is always included:
NOTIFIER_ANNOUNCEMENTS_RENDER_HOOKS="panels::content.start,panels::topbar.end"
Existing setups need no change: with no target (or older servers) every announcement keeps rendering at the default hook exactly as before.
Banner cap: each render location shows the top NOTIFIER_ANNOUNCEMENTS_MAX_VISIBLE announcements (default 5, priority-ordered) plus a muted + N dalších oznámení overflow line; set it to 0 for unlimited. Custom (SPA) hosts via customAnnouncements() always get the full list.
Blade hosts: drop the component anywhere:
<x-notifier-announcements-notice />
Inertia / Vue / React (custom SPA) hosts: the service is framework-agnostic - render it yourself. Announcements aimed at a custom dashboard arrive with dashboard_type custom and a target DOM element id; fetch just those and mount each at its target:
// All active announcements (any dashboard_type): app(\Devuni\Notifier\Services\AnnouncementsService::class)->activeAnnouncements(); // Only the custom (SPA) ones, optionally for a single element id: app(\Devuni\Notifier\Services\AnnouncementsService::class)->customAnnouncements(); app(\Devuni\Notifier\Services\AnnouncementsService::class)->customAnnouncements('spa-banner'); // e.g. share as an Inertia prop, then render each item into the element named by its 'target'.
Requests are per-repository and reuse your existing NOTIFIER_URL + X-Notifier-Token (GET {NOTIFIER_URL}/announcements), so the server returns only this site's announcements - no other repositories are disclosed. Responses are cached (NOTIFIER_ANNOUNCEMENTS_CACHE_TTL, default 900 s) so the dashboard never blocks on a live request, and any fetch failure renders nothing rather than breaking your dashboard. Customize the Blade markup with vendor:publish --tag="notifier-views".
Heartbeat
Tell the control plane this site is alive and report its identity. On by default - schedule the command and the server knows the agent is running; the server marks an agent stale after 6 h of silence, so the operator sees when a site stops checking in. Disable with NOTIFIER_HEARTBEAT_ENABLED=false (the push becomes a no-op).
The host app schedules it - hourly is the recommended cadence (comfortably inside the 6 h stale window):
use Illuminate\Support\Facades\Schedule; Schedule::command('notifier:heartbeat')->hourly()->onOneServer();
Or run it on demand:
php artisan notifier:heartbeat
Each heartbeat POST {NOTIFIER_URL}/heartbeat (per-repository, same X-Notifier-Token) reports an identity + liveness manifest: the agent / PHP / Laravel versions, the queue connection, which features are enabled (announcements, backups, heartbeat), free + total disk bytes on the storage volume, the last database & storage backup times (recorded automatically when notifier:database-backup / notifier:storage-backup succeed), and the agent's own clock (reported_at). The server stamps its own receipt time - the agent never sends that. Unlike announcements, the heartbeat is a push: a rejected or unreachable server makes the command exit non-zero (and log to the backup channel), so your scheduler's failure handling can react.
Configure
Minimum .env:
NOTIFIER_BACKUP_CODE=... # auth token NOTIFIER_URL=https://notifier.devuni.cz/api/v1/repositories/123 # your endpoint NOTIFIER_BACKUP_PASSWORD=... # ZIP password
Optional: NOTIFIER_LOGGING_CHANNEL, NOTIFIER_ROUTES_ENABLED, NOTIFIER_ROUTE_PREFIX, NOTIFIER_ZIP_STRATEGY (auto/cli/php), NOTIFIER_CHUNK_SIZE, NOTIFIER_QUEUE_CONNECTION, NOTIFIER_DATABASE_CONNECTION, NOTIFIER_POSTGRES_DUMP_BINARY, NOTIFIER_POSTGRES_SCHEMA, NOTIFIER_HEARTBEAT_ENABLED. See config/notifier.php for defaults and descriptions.
Database engine
The package auto-detects which dump tool to use from your Laravel connection driver:
| Driver | Tool used | Install |
|---|---|---|
mysql, mariadb |
mysqldump |
apt install mysql-client or mariadb-client |
pgsql (PostgreSQL) |
pg_dump |
apt install postgresql-client |
pgsql (YugabyteDB) |
ysql_dump if installed, else pg_dump |
YugabyteDB tools |
By default the package backs up your app's default Laravel connection (config('database.default')). Override with NOTIFIER_DATABASE_CONNECTION=pgsql if you want a different one.
For PostgreSQL/Yugabyte, force a specific binary via NOTIFIER_POSTGRES_DUMP_BINARY=ysql_dump (or pg_dump). Non-public schemas: set NOTIFIER_POSTGRES_SCHEMA=myschema.
Exclusions
Arrays - edit config/notifier.php:
'excluded_tables' => ['telescope_entries', 'sessions', 'cache', 'jobs', 'failed_jobs'], 'excluded_files' => ['.gitignore', 'temp', 'logs/debug.log'],
Queue offloading
API-triggered backups can be offloaded to avoid PHP timeouts:
NOTIFIER_QUEUE_CONNECTION=redis # or database, sqs, beanstalkd
Artisan commands always run synchronously regardless of this setting.
Security
- At rest: AES-256 encrypted archives with
0600permissions, cleaned up after upload - In transit: HTTPS-only,
hash_equalstoken comparison, per-chunk + full-file SHA-256 verification - No leaks: ZIP password passed via stdin (not argv - invisible to
ps//proc/*/cmdline); API errors return opaque UUIDs, not raw exception messages - Report vulnerabilities: see security policy - don't open public issues
Links
- Changelog - see what's new in each release
- Contributing
- Full config reference
Credits
License
MIT - see LICENSE.md.