ograf-core
An independent Rust implementation of the OGraf graphics-control HTTP + WebSocket API.
Note: This is an unofficial implementation, not affiliated with or endorsed by the EBU. It aims for full spec compliance with the official OGraf v1 specification.
Design
Every access decision — who may connect a renderer, who may see it, who may
target it — is delegated to an AccessControl trait that a
consuming binary implements and wires in. ograf-core ships one trivial
implementation, AllowAllAccessControl, that imposes no
restriction at all.
This is a deliberate Dependency Inversion seam: ograf-core depends on
nothing outside this crate and knows nothing about tenants, or
credentials. A consumer that wants real access control (zones, per-vendor
API keys, encrypted renderer tokens, ...) implements AccessControl in its
own crate and links ograf-core as a library — it never needs to fork or
patch this code to do it.
Quick start
use ;
use ;
async
build_router returns a plain axum::Router — nest it under your own
top-level router alongside whatever admin/auth routes your binary adds.
Configuration
Config::from_env() reads:
| Variable | Default | Meaning |
|---|---|---|
OGRAF_HOST |
0.0.0.0 |
Bind address |
OGRAF_PORT |
8080 |
Bind port |
OGRAF_STORAGE |
./graphics |
Where graphics live on disk (relative to the process's working directory) |
RUST_LOG |
info |
Log level |
OGRAF_ACTION_TIMEOUT_MS |
5000 |
How long an HTTP action call waits for the renderer's confirmation before failing |
OGRAF_GRAPHICS_CACHE_TTL_SECS |
30 |
Graphics list cache TTL in seconds (0 = disabled, always fetch fresh) |
OGRAF_RENDERER_MAX_PENDING |
100 |
Maximum pending requests per renderer (DoS protection) |
Where graphics come from
GraphicStore (src/store/graphics.rs) scans OGRAF_STORAGE on disk and
caches the result for OGRAF_GRAPHICS_CACHE_TTL_SECS (default: 30 seconds).
Set TTL to 0 to disable caching and always fetch fresh from disk.
Any immediate subdirectory containing a *.ograf.json manifest is treated
as one graphic; the subdirectory name becomes its graphicId. Writing (or
deleting) that directory is entirely the consumer's job — ograf-core only
ever reads.
Note: With caching enabled, new/deleted graphics may not appear immediately
(up to TTL delay). For development environments where graphics change frequently,
consider OGRAF_GRAPHICS_CACHE_TTL_SECS=0.
API surface
GET /ograf/v1/— server info (name, description, author, version from Core'sCargo.toml)GET /ograf/v1/health— health check (always returns 200 OK, no auth required)GET /ograf/v1/graphics,GET /ograf/v1/graphics/:id— list/inspect graphicsGET /ograf/v1/graphics/:id/assets/*path,GET /ograf/v1/graphics/:id/thumbnail— serve graphic assetsGET /ograf/v1/renderers,GET /ograf/v1/renderers/:id,GET /ograf/v1/renderers/:id/target— list/inspect renderersPUT /ograf/v1/renderers/:id/target/graphicInstance/{load,clear}POST /ograf/v1/renderers/:id/target/graphicInstance/{playAction,stopAction,updateAction}POST /ograf/v1/renderers/:id/target/graphicInstance/customActions/:actionIdPOST /ograf/v1/renderers/:id/customActions/:actionId— renderer-scoped custom actionGET /rendererApi/v1/connect— renderer WebSocket upgrade (RENDERER_CONNECT_PATH; outside/ograf/v1, since the renderer protocol isn't part of the Server API)
OGraf v1 Spec Compliance
ograf-core implements the complete OGraf v1 Server API specification. All endpoints, WebSocket messages, and behaviors match the official spec (see SPEC_COMPLIANCE.md for verification details).
Non-breaking Extensions
These additions enhance observability and functionality without breaking compatibility with spec-compliant clients or renderers:
1. Instance State Tracking
The GET /renderers/:id/target response includes extra fields for each GraphicInstance:
state— Current instance state:loaded,playing, orstoppedcurrentStep— Last reported step fromplayAction(persisted between actions)data— Last confirmed data from the renderer
Rationale: Helps dashboards and UIs display more than "a graphic is loaded here" without requiring clients to track state themselves.
2. Inline Renderer Metrics (v0.3.0+)
The GET /renderers/:id response includes an optional metrics field with:
pendingRequests— Number of in-flight renderer requestsmessagesSent/messagesReceived— Cumulative WebSocket message countsuptimeSeconds— Renderer connection uptime
Rationale: Observability without requiring a separate metrics endpoint.
3. Renderer Reconnect State Resync (v0.3.0+)
Renderers can include an optional instances array in their Hello message to resync Core's view of loaded instances after reconnect. Each instance snapshot includes instanceId, graphicId, data, and currentStep.
Rationale: Enables seamless renderer reconnect after network blips or Core restarts without losing instance state.
4. AccessControl Extensions (v0.3.0+)
The AccessControl trait includes optional methods for fine-grained access control:
filter_graphics()— Graphics-level visibility control forGET /graphicscan_load_graphic()— Per-graphic load authorization checked beforeload()
Both have default implementations that allow all access (maintaining backward compatibility). Custom implementations can enforce zone/role-based restrictions.
Rationale: Enables multi-tenant deployments with graphics scoped to specific zones or renderers.
5. Lenient currentStep Parsing
If a renderer's playActionResult contains a non-numeric currentStep, it defaults to 0.0 instead of rejecting the entire message.
Rationale: Prevents timeout/failure when a template returns unexpected values. The action still succeeds; only this one field degrades gracefully.
6. Health Check Endpoint (v0.3.0+)
GET /ograf/v1/health always returns {"status": "ok"} with 200 OK. No authentication required.
Rationale: Standard endpoint for load balancers and orchestrators to check service health.
Compatibility: Clients can safely ignore all extra fields. Renderers that don't send instances in Hello behave identically to v0.2.x. See SPEC_COMPLIANCE.md for full details.
Status
Early-stage (0.4.0) — Spec-compliant but not yet battle-tested in production.
This is a library implementation of the OGraf v1 spec. Consumers implement their own access control via the AccessControl trait.
Use in production: Possible, but be aware this is a new implementation without significant production usage. Test thoroughly in your environment before deploying.
Learn More About OGraf
- OGraf Specification — Official EBU specification
Questions or feedback? Open an issue on GitHub or write a pm.
License
Dual-licensed under either of
at your option.