{"id":13821764,"url":"https://github.com/bpolaszek/freddie","last_synced_at":"2025-10-26T11:12:04.797Z","repository":{"id":37268720,"uuid":"432684314","full_name":"bpolaszek/freddie","owner":"bpolaszek","description":"A Mercure Hub, written in PHP (8.1 + ReactPHP)","archived":false,"fork":false,"pushed_at":"2024-11-05T14:40:46.000Z","size":192,"stargazers_count":98,"open_issues_count":9,"forks_count":14,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-28T15:07:47.938Z","etag":null,"topics":["hub","mercure","php","sse"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bpolaszek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-28T10:44:39.000Z","updated_at":"2025-03-15T11:01:39.000Z","dependencies_parsed_at":"2023-01-29T22:01:21.483Z","dependency_job_id":"d53c5dc1-5d01-4b38-9d8c-1828ba49d244","html_url":"https://github.com/bpolaszek/freddie","commit_stats":{"total_commits":19,"total_committers":3,"mean_commits":6.333333333333333,"dds":"0.26315789473684215","last_synced_commit":"483266289c8dbe41ac348f0313906964c6eb79bd"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bpolaszek%2Ffreddie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bpolaszek%2Ffreddie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bpolaszek%2Ffreddie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bpolaszek%2Ffreddie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bpolaszek","download_url":"https://codeload.github.com/bpolaszek/freddie/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247208142,"owners_count":20901570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hub","mercure","php","sse"],"created_at":"2024-08-04T08:01:27.667Z","updated_at":"2025-10-26T11:12:04.736Z","avatar_url":"https://github.com/bpolaszek.png","language":"PHP","readme":"[![Application](https://github.com/bpolaszek/mercure-x/actions/workflows/app.yml/badge.svg)](https://github.com/bpolaszek/mercure-x/actions/workflows/app.yml)\n[![Coverage](https://codecov.io/gh/bpolaszek/freddie/branch/main/graph/badge.svg?token=uB4gRHyS6r)](https://codecov.io/gh/bpolaszek/freddie)\n\n# Freddie\n\nFreddie is a PHP implementation of the [Mercure Hub Specification](https://mercure.rocks/spec).\n\nIt is blazing fast, built on the shoulders of giants:\n- [PHP](https://www.php.net/releases/8.1/en.php) 8.1\n- [Framework X](https://framework-x.org/) and [ReactPHP](https://reactphp.org/)\n- [Symfony](https://symfony.com/) 6\n- [Redis](https://redis.io/) (optionally).\n\nSee what features are covered and what aren't (yet) [here](#feature-coverage).\n\n## Installation\n\nPHP 8.1+ is required to run the hub.\n\n### As a standalone Mercure hub\n\n```bash\ncomposer create-project freddie/mercure-x freddie \u0026\u0026 cd freddie\nbin/freddie\n```\n\nThis will start a Freddie instance on `127.0.0.1:8080`, with anonymous subscriptions enabled.\n\nYou can publish updates to the hub by generating a valid JWT signed with the `!ChangeMe!` key with `HMAC SHA256` algorithm.\n\nTo change these values, see [Security](#security).\n\n### As a bundle of your existing Symfony application\n\n```bash\ncomposer req freddie/mercure-x\n```\n\nYou can then start the hub by doing:\n\n```bash\nbin/console freddie:serve\n```\n\nYou can override relevant env vars in your `.env.local` \nand services in your `config/services.yaml` as usual.\n\nThen, you can inject `Freddie\\Hub\\HubInterface` in your services so that you can call `$hub-\u003epublish($update)`,\nor listening to dispatched updates in a CLI context 👍\n\nKeep in mind this only works when using the Redis transport.\n\n⚠️ **Freddie** uses its own routing/authentication system (because of async / event loop). \n\nThe controllers it exposes cannot be imported in your `routes.yaml`, and get out of your  `security.yaml` scope.\n\n## Usage\n\n```bash\n./bin/freddie\n```\n\nIt will start a new Mercure hub on `127.0.0.1:8080`. \nTo change this address, use the `X_LISTEN` environment variable:\n\n```bash\nX_LISTEN=\"0.0.0.0:8000\" ./bin/freddie\n```\n\n### Security \n\nThe default JWT key is `!ChangeMe!` with a `HS256` signature. \n\nYou can set different values by changing the environment variables (in `.env.local` or at the OS level): \n`X_LISTEN`, `JWT_SECRET_KEY`, `JWT_ALGORITHM`, `JWT_PUBLIC_KEY` and `JWT_PASSPHRASE` (when using RS512 or ECDSA)\n\nPlease refer to the [authorization](https://mercure.rocks/spec#authorization) section of the Mercure specification to authenticate as a publisher and/or a subscriber.\n\n### PHP Transport (default)\n\nBy default, the hub will run as a simple event-dispatcher, in a single PHP process.\n\nIt can fit common needs for a basic usage, but using this transport prevents scalability,\nas opening another process won't share the same event emitter.\n\nIt's still prefectly usable as soon as :\n- You don't expect more than a few hundreds updates per second\n- Your application is served from a single server.\n\n### Redis transport\n\nOn the other hand, you can launch the hub on **multiple ports** and/or **multiple servers** with a Redis transport\n(as soon as they share the same Redis instance), and optionally use a load-balancer to distribute the traffic.\n\nThe [official open-source version](https://mercure.rocks/docs/hub/install) of the hub doesn't allow scaling \nbecause of concurrency restrictions on the _bolt_ transport.\n\nTo launch the hub with the Redis transport, change the `TRANSPORT_DSN` environment variable:\n\n```bash\nTRANSPORT_DSN=\"redis://127.0.0.1:6379\" ./bin/freddie\n```\n\nOptional parameters you can pass in the DSN's query string:\n- `pingInterval` - regularly ping Redis connection, which will help detect outages (default `2.0`)\n- `readTimeout` - max duration in seconds of a ping or publish request (default `0.0`: considered disabled)\n\n_Alternatively, you can set this variable into `.env.local`._\n\n## Advantages and limitations\n\nThis implementation does not provide SSL nor HTTP2 termination, so you'd better put a reverse proxy in front of it. \n\n### Example Nginx configuration\n\n```nginx\nupstream freddie {\n    # Example with a single node\n    server 127.0.0.1:8080;\n\n    # Example with several nodes (they must share the same Redis instance)\n    # 2 instances on 10.1.2.3\n    server 10.1.2.3:8080;\n    server 10.1.2.3:8081;\n\n    # 2 instances on 10.1.2.4\n    server 10.1.2.4:8080;\n    server 10.1.2.4:8081;\n}\n\nserver {\n    \n    listen 443 ssl http2;\n    listen [::]:443 ssl http2;\n    server_name example.com;\n\n    ssl_certificate /etc/ssl/certs/example.com/example.com.cert;\n    ssl_certificate_key /etc/ssl/certs/example.com/example.com.key;\n    ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;\n\n    location /.well-known/mercure {\n        proxy_pass http://freddie;\n        proxy_read_timeout 24h;\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Host $host;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n```\n\n### Example Caddy configuration\n\n#### Single node\n\n```caddy\nexample.com\n\nreverse_proxy 127.0.0.1:8080\n```\n\n#### With multiple nodes\n\n```caddy\nexample.com\n\nreverse_proxy 10.1.2.3:8080 10.1.2.3:8081 10.1.2.4:8080 10.1.2.4:8081\n```\n\n### Payload limitations\n⚠ There's a known limit in [Framework-X](https://framework-x.org/docs/api/request/) which prevents request bodies to weigh more than [64 KB](https://github.com/reactphp/http/issues/412). \nAt the time of writing, this limit cannot be raised due to Framework-X encapsulating HTTP Server instantiation.\n\nPublishing bigger updates to Freddie (through HTTP, at least) could result in 400 errors.\n\n## Feature coverage\n\n| Feature                                     | Covered                               |\n|---------------------------------------------|---------------------------------------|\n| JWT through `Authorization` header          | ✅                                     |\n| JWT through `mercureAuthorization` Cookie   | ✅                                     |\n| Allow anonymous subscribers                 | ✅                                     |\n| Alternate topics                            | ✅️                                    |\n| Private updates                             | ✅                                     |\n| URI Templates for topics                    | ✅                                     |\n| HMAC SHA256 JWT signatures                  | ✅                                     |\n| RS512 JWT signatures                        | ✅                                     |\n| Environment variables configuration         | ✅                                     |\n| Custom message IDs                          | ✅                                     |\n| Last event ID (including `earliest`)        | ✅️                                    |\n| Customizable event type                     | ✅️                                    |\n| Customizable `retry` directive              | ✅️                                    |\n| CORS                                        | ❌ (configure them on your web server) |\n| Health check endpoint                       | ❌ (PR welcome)                        |\n| Logging                                     | ❌ (PR welcome))️                      |\n| Metrics                                     | ❌ (PR welcome)️                       |\n| Different JWTs for subscribers / publishers | ❌ (PR welcome)                        |\n| Subscription API                            | ❌️ (TODO)                             |\n\n\n## Tests\n\nThis project is 100% covered with [Pest](https://pestphp.com/) tests. \n\n```bash\ncomposer tests:run\n```\n\n## Contribute\n\nIf you want to improve this project, feel free to submit PRs:\n\n- CI will yell if you don't follow [PSR-12 coding standards](https://www.php-fig.org/psr/psr-12/)\n- In the case of a new feature, it must come along with tests\n- [PHPStan](https://phpstan.org/) analysis must pass at level 8\n\nYou can run the following command before committing to ensure all CI requirements are successfully met:\n\n```bash\ncomposer ci:check\n```\n\n## License\n\nGNU General Public License v3.0.\n","funding_links":[],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbpolaszek%2Ffreddie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbpolaszek%2Ffreddie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbpolaszek%2Ffreddie/lists"}