phprise / grogu-gateway
A generic gateway for Symfony/ApiPlatform applications based on The OTAKU Manifesto.
Package info
github.com/phprise-foundation/grogu-gateway
Type:project
pkg:composer/phprise/grogu-gateway
Requires
- php: ^8.4
- guzzlehttp/psr7: ^2.8
- phprise/common: ^2.0
- phprise/http: ^2.0
Requires (Dev)
- phpunit/phpunit: ^12.5
- vimeo/psalm: ^6.14
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A generic gateway for Symfony/ApiPlatform applications based on the OTAKU Manifesto.
What it does
Grogu is a single entry point in front of many ApiPlatform services. It discovers each service's OpenAPI document at runtime, aggregates every route into one map, and forwards incoming requests to the service that owns the route.
Given enma=https://enma.io and nami=https://nami.io, a request to
POST grogu.io/api/order is forwarded to POST nami.io/api/order.
How it works
- Bootstrap: reads the
GROGU_SERVICESenvironment variable and builds aServiceRegistry. - Discovery: for every service, fetches
{host}/api/docs.jsonand extracts its operations. - Aggregation: builds a
RouteMap. If two services expose the same method and path, anAmbiguousRouteExceptionis thrown and the gateway refuses to start. - Routing: resolves the incoming request against the map and forwards it to the owner.
Discovery happens on every request, so any change in a service's documentation is reflected immediately without restarting the gateway.
Configuration
Services are configured through the GROGU_SERVICES environment variable as a
comma-separated list of name=host pairs:
export GROGU_SERVICES="enma=https://enma.io,nami=https://nami.io,yagami=https://yagami.io"
The host is the origin only (scheme + domain). The path to the OpenAPI
document is configured separately through GROGU_SPEC_PATHS, a comma-separated
list of candidate paths tried in order until one returns a valid JSON
document:
export GROGU_SPEC_PATHS="/api/docs.json,/docs.json,/openapi.json"
This makes the gateway flexible across frameworks: an API Platform service may
expose /api/docs.json, while another may expose /docs.json. If
GROGU_SPEC_PATHS is not set, it defaults to /api/docs.json.
Different environments can expose different hosts, or omit services entirely (for example, authentication in a development environment).
Authentication
Authentication is optional and pluggable. By default it is disabled
(GROGU_AUTH_MODE=none), and the gateway forwards every request untouched.
When enabled, the gateway authenticates the request before forwarding it. The authenticator is a generic HTTP adapter: it extracts a token from a request header, calls a verification URL, and interprets the JSON response. This means the same adapter works with any verification service (KoenmaID, Keycloak, a custom one).
Environment variables
| Variable | Default | Description |
|---|---|---|
GROGU_AUTH_MODE |
none |
none (disabled) or http (generic HTTP adapter) |
GROGU_AUTH_URL |
— | The verification URL the gateway calls |
GROGU_AUTH_HEADER |
Authorization |
The request header that carries the token |
GROGU_AUTH_PUBLIC_ROUTES |
— | Comma-separated routes that skip authentication |
The verification contract
The gateway sends a POST to GROGU_AUTH_URL with a JSON body:
{ "token": "<the token, without the scheme>" }
The verification service must answer with:
{
"valid": true,
"subject": "user-123",
"claims": { "role": "admin" },
"metadata": { "tenant": "acme" }
}
valid(required) — whether the token is accepted.subject— who was authenticated.usernameis accepted as a fallback.claims— key/value data about the identity.metadata— free-form key/value data for the consumer's own rules.
Any other shape (or valid: false) results in a 401.
Example: KoenmaID
export GROGU_AUTH_MODE=http export GROGU_AUTH_URL="https://koenma.example.com/token/verify" export GROGU_AUTH_HEADER="Authorization" export GROGU_AUTH_PUBLIC_ROUTES="GET /docs,GET /health"
Example: Keycloak (same adapter)
export GROGU_AUTH_MODE=http export GROGU_AUTH_URL="https://keycloak.example.com/realms/acme/protocol/openid-connect/userinfo" export GROGU_AUTH_HEADER="Authorization"
Custom authenticators
For cases the HTTP adapter does not cover, implement
Phprise\GroguGateway\Domain\Contract\AuthenticatorInterface and wire it in
public/index.php through an AuthenticationGate.
Running
composer install
GROGU_SERVICES="nami=https://nami.splitpaybrasil.dev.br" php -S localhost:8000 -t public
Testing
vendor/bin/phpunit vendor/bin/psalm
Philosophy
We follow The OTAKU Manifesto: Fluid Structure Design. Please read more about it in OTAKU Manifesto. See our security policy in SECURITY.md.