solution-forest/laravel-boop

Send events to a self-hosted Boop server from Laravel.

Maintainers

Package info

github.com/solutionforest/laravel-boop

pkg:composer/solution-forest/laravel-boop

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.0.1 2026-08-29 14:34 UTC

This package is auto-updated.

Last update: 2026-08-30 09:44:45 UTC


README

Send events to a self-hosted Boop server from Laravel.

What is Boop

Boop is a tiny, self-hosted notification inbox for developers: one Go binary, one SQLite file, one Docker container. Your apps POST small JSON events with a project API key; the server stores them and pushes straight to Apple's APNs. There is no hosted relay, no account system, no telemetry, and it is MIT licensed.

Use it to replace hosted push relays like ntfy/Pushover, Slack webhooks, or hand-rolled APNs plumbing — you keep the delivery infrastructure on your own server, private and self-hosted, instead of sending your events through someone else's cloud.

This library does one thing — send events reliably without ever taking your application down.

  • PHP 8.1+, Laravel ^10.0|^11.0|^12.0|^13.0
  • Composer package: solution-forest/laravel-boop
  • Namespace: SolutionForest\Boop

Quick start

Spin up a local Boop instance, then point this package at it.

1. Start a Boop server (either way):

Option A — the upstream repo:

git clone https://github.com/chrisgreg/boop
cd boop
cp .env.example .env
mkdir -p data
docker compose up -d --build

Option B — this repo's ready-made compose file (builds the upstream server image):

mkdir -p data
docker compose -f docker-compose.boop.yml up -d --build

2. Create a project — open http://localhost:8080, follow the setup wizard, create a project, and copy the boop_proj_... API key it shows you.

3. Wire up Laravel:

composer require solution-forest/laravel-boop
# .env
BOOP_URL=http://localhost:8080
BOOP_API_KEY=boop_proj_...
use SolutionForest\Boop\Facades\Boop;

Boop::send('Backup complete');

See Usage for richer events and Async for fire-and-forget.

Installation

composer require solution-forest/laravel-boop

The package auto-registers its service provider and facade alias (Boop).

Optionally publish the config:

php artisan vendor:publish --tag=boop-config

Configuration

Set these in your .env (the API key must come from the environment, never from a committed file):

Env var Default Notes
BOOP_URL e.g. https://boop.example.com. Trailing slash is stripped.
BOOP_API_KEY Project key, boop_proj_.... Treated as a secret.
BOOP_SOURCE Default source tagged onto every event.
BOOP_ENABLED true Set false to no-op instead of hitting the server.
BOOP_TIMEOUT 10 Total per-request timeout, seconds.
BOOP_CONNECT_TIMEOUT 5 Connect timeout, seconds.
BOOP_MAX_RETRIES 2 Total attempts for network errors and 5xx (never 4xx); 2 = 1 initial + 1 retry.
BOOP_RETRY_DELAY 200 Base backoff in ms, jittered exponentially.

Extra keys to redact inside event data can be added via the redact_keys config entry.

Usage

The minimal call works with just a title:

use SolutionForest\Boop\Facades\Boop;

$result = Boop::send('Backup complete');

if ($result->ok) {
    echo $result->id; // "evt_..."
}

A richer event:

use SolutionForest\Boop\Enums\Level;

$result = Boop::send([
    'title' => 'Payment received',
    'body' => '£19.99 from customer #123',
    'level' => Level::Success,
    'source' => 'billing',
    'data' => [
        'customer_id' => 123,
        'amount' => 19.99,
        'currency' => 'GBP',
    ],
    'actions' => [
        ['label' => 'Open order', 'url' => 'https://shop.example/orders/123'],
    ],
]);

send() blocks until the server answers. It never throws: bad input, missing configuration, network failures and server errors all come back as a Result:

  • Result::ok$result->id, $result->createdAt
  • Result::disabled → sending is turned off
  • Result::failed$result->error is a BoopError (->code(), ->status, isRetryable(), ->errorCode)

Async

sendAsync() is fire-and-forget for hot paths. It validates and redacts immediately, then runs after the response has been sent (via dispatchAfterResponse()), falling back to running immediately when there is no HTTP context — it never silently drops. Failures are logged with only code / status / title / message; never the key or payload.

Boop::sendAsync('Cron finished');
Boop::sendAsync(['title' => 'Deploy failed', 'level' => 'error']);

Note on per-call overrides: for send(), the $overrides array (url, api_key, timeout, ...) is applied to that call's request. For sendAsync(), overrides are applied while validating/redacting the event, but the actual HTTP POST runs inside the dispatched SendEvent job, which resolves the configured singleton — so url / api_key / timeout overrides do not propagate to the async request. If you need per-request overrides on an async send, dispatch the SendEvent job yourself (below) with a configured client.

Queue-backed alternative

If you prefer a real queue worker, dispatch the SendEvent job yourself — this is the documented alternative and is not the default:

use SolutionForest\Boop\Jobs\SendEvent;

SendEvent::dispatch(['title' => 'Backup complete'])->onQueue('boop');

Behaviour guarantees

  • Never throws. send() and sendAsync() catch everything.
  • Retries only network errors and 5xx, up to BOOP_MAX_RETRIES total attempts with jittered exponential backoff; 4xx is never retried.
  • Redacts before transmit. The default sensitive keys (password, token, api_key, authorization, ... ) plus anything in redact_keys are replaced with [REDACTED] inside data, recursively and case-insensitively (-/_ equivalent).
  • Truncates rather than rejects. title (200), body (4000), short fields (200); character counts via mb_*.
  • Drops oversized data. If data cannot be JSON-encoded or is over 256 KB after redaction, it is dropped and [data omitted: over 256 KB or not JSON-serialisable] is appended to body.
  • Timeouts everywhere. Total and connect timeouts are configurable.
  • Health check. Boop::healthy() hits GET /health.

Testing

composer install
vendor/bin/phpunit

License

MIT — see LICENSE.