Freddie is a PHP implementation of the Mercure Hub Specification.
It is blazing fast, built on the shoulders of giants:
- PHP 8.1
- Framework X and ReactPHP
- Symfony 6
- Redis (optionally).
See what features are covered and what aren't (yet) here.
PHP 8.1 is required to run the hub.
composer create-project freddie/mercure-x freddie && cd freddie
bin/freddie
This will start a Freddie instance on 127.0.0.1:8080
, with anonymous subscriptions enabled.
You can publish updates to the hub by generating a valid JWT signed with the !ChangeMe!
key with HMAC SHA256
algorithm.
To change these values, see Security.
composer req freddie/mercure-x
You can then start the hub by doing:
bin/console freddie:serve
You can override relevant env vars in your .env.local
and services in your config/services.yaml
as usual.
Then, you can inject Freddie\Hub\HubInterface
in your services so that you can call $hub->publish($update)
,
or listening to dispatched updates in a CLI context 👍
Keep in mind this only works when using the Redis transport.
The controllers it exposes cannot be imported in your routes.yaml
, and get out of your security.yaml
scope.
./bin/freddie
It will start a new Mercure hub on 127.0.0.1:8080
.
To change this address, use the X_LISTEN
environment variable:
X_LISTEN="0.0.0.0:8000" ./bin/freddie
The default JWT key is !ChangeMe!
with a HS256
signature.
You can set different values by changing the environment variables (in .env.local
or at the OS level):
X_LISTEN
, JWT_SECRET_KEY
, JWT_ALGORITHM
, JWT_PUBLIC_KEY
and JWT_PASSPHRASE
(when using RS512 or ECDSA)
Please refer to the authorization section of the Mercure specification to authenticate as a publisher and/or a subscriber.
By default, the hub will run as a simple event-dispatcher, in a single PHP process.
It can fit common needs for a basic usage, but using this transport prevents scalability, as opening another process won't share the same event emitter.
It's still prefectly usable as soon as :
- You don't expect more than a few hundreds updates per second
- Your application is served from a single server.
On the other hand, you can launch the hub on multiple ports and/or multiple servers with a Redis transport (as soon as they share the same Redis instance), and optionally use a load-balancer to distribute the traffic.
The official open-source version of the hub doesn't allow scaling because of concurrency restrictions on the bolt transport.
To launch the hub with the Redis transport, change the TRANSPORT_DSN
environment variable:
TRANSPORT_DSN="redis://127.0.0.1:6379" ./bin/freddie
Alternatively, you can set this variable into .env.local
.
This implementation does not provide SSL nor HTTP2 termination, so you'd better put a reverse proxy in front of it.
upstream freddie {
# Example with a single node
server 127.0.0.1:8080;
# Example with several nodes (they must share the same Redis instance)
# 2 instances on 10.1.2.3
server 10.1.2.3:8080;
server 10.1.2.3:8081;
# 2 instances on 10.1.2.4
server 10.1.2.4:8080;
server 10.1.2.4:8081;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com/example.com.cert;
ssl_certificate_key /etc/ssl/certs/example.com/example.com.key;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
location /.well-known/mercure {
proxy_pass http://freddie;
proxy_read_timeout 24h;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
example.com
reverse_proxy 127.0.0.1:8080
example.com
reverse_proxy 10.1.2.3:8080 10.1.2.3:8081 10.1.2.4:8080 10.1.2.4:8081
⚠ There's a known limit in Framework-X which prevents request bodies to weigh more than 64 KB. At the time of writing, this limit cannot be raised due to Framework-X encapsulating HTTP Server instantiation.
Publishing bigger updates to Freddie (through HTTP, at least) could result in 400 errors.
Feature | Covered |
---|---|
JWT through Authorization header |
✅ |
JWT through mercureAuthorization Cookie |
✅ |
Allow anonymous subscribers | ✅ |
Alternate topics | ✅️ |
Private updates | ✅ |
URI Templates for topics | ✅ |
HMAC SHA256 JWT signatures | ✅ |
RS512 JWT signatures | ✅ |
Environment variables configuration | ✅ |
Custom message IDs | ✅ |
Last event ID (including earliest ) |
✅️ |
Customizable event type | ✅️ |
Customizable retry directive |
✅️ |
CORS | ❌ (configure them on your web server) |
Health check endpoint | ❌ (PR welcome) |
Logging | ❌ (PR welcome))️ |
Metrics | ❌ (PR welcome)️ |
Different JWTs for subscribers / publishers | ❌ (PR welcome) |
Subscription API | ❌️ (TODO) |
This project is 100% covered with Pest tests.
composer tests:run
If you want to improve this project, feel free to submit PRs:
- CI will yell if you don't follow PSR-12 coding standards
- In the case of a new feature, it must come along with tests
- PHPStan analysis must pass at level 8
You can run the following command before committing to ensure all CI requirements are successfully met:
composer ci:check
GNU General Public License v3.0.