Skip to content

Mercure Real-Time Push

Doggy implements real-time server push via the Mercure protocol. The Mercure Hub is built into FrankenPHP and enabled through the Caddyfile — no separate container or service needed.

Architecture

Client (EventSource)
    ↑ SSE
    └── http://localhost:8000/.well-known/mercure

    Caddyfile (FrankenPHP)
        ↑ mercure directive
    FrankenPHP (built-in Hub)

Caddyfile Configuration

The FrankenPHP Caddyfile embeds the Mercure Hub:

caddy
http://localhost:8000 {
    route /.well-known/mercure {
        mercure {
            transport bolt {
                path /var/www/var/mercure.db
            }
            publisher_jwt !ChangeThisMercureHubJWTSecretKey!
            subscriber_jwt !ChangeThisMercureHubJWTSecretKey!
            cors_origins http://localhost:8000
            publish_origins *
            anonymous
        }
    }
}

JWT Tokens

MercureTokenFactory generates subscription JWT tokens:

php
// src/Security/MercureTokenFactory.php
$token = $factory->createSubscribeToken(
    topics: ['/user/123/*', '/entity/*'],
    roles: ['ROLE_USER'],
    expiresIn: 3600
);

MercureCookieSubscriber injects the mercureAuthorization cookie into every response:

php
// Auto-generated subscription topics
$topics = [
    'https://enterprise.local/user/' . $userId . '/export',
    '/user/' . $userId . '/*',
    '/entity/*',
];

Frontend Subscription

javascript
const url = new URL('/.well-known/mercure', window.location.origin);
url.searchParams.append('topic', '/user/' + userId + '/*');
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    // Handle push message
};

Key Applications

  • Export progress: Real-time progress for large file exports
  • Presence detection: PresenceService + PresenceApiController
  • Real-time notifications: System messages, approval reminders
  • Data sync: Real-time updates in multi-user collaboration

Open Source under MIT | Copyright © 2026 Doggy