Photon

Realtime product state

Photon is a Rust pub/sub runtime with typed topics and durable subscriptions. Storage is pluggable through StoragePort, with memory, SQLite, NATS, Fluvio, and Kafka adapters. The same publish and subscribe surface runs embedded or brokered.

Why this family exists

Photon is typed topics and subscriptions. Canonical data stays in your datastore. The transport log carries encrypted, transient events, and Leptos clients refetch synced server functions when those events arrive.

Where it lands

Photon topics stream progress into the browser in the counter demo and anywhere Orbital binds subscriptions. The UI stays current without treating the transport log as the system of record.
Declare #[photon::topic] payloads and #[photon::subscribe] workers, boot Photon::builder().auto_registry(), and publish with publish_on. Durable subscriptions checkpoint the last delivered sequence per partition; keyed topics partition by field for filtered consume. The capacity campaign measures encrypted publish-to-checkpoint delivery with one and four durable subscribers. Resources keeps the qualified Fluvio and embedded SQLite ingress component measurements. Use memory for local development, SQLite for a durable single process, or broker adapters for fleet scale. Primary guide: docs.rs/uf-photon getting started (embedded versus brokered).

In the workspace

Photon topics stream progress into the browser in the counter demo and anywhere Orbital binds subscriptions. The UI stays current without treating the transport log as the system of record.

Code sample

From the upstream getting-started docs — see also all code recipes on Resources.
Photon topicPhoton
Typed topic + durable subscribe
use std::sync::Arc;
use photon::{subscribe, topic, JsonIdentityFactory, Photon};
use photon_core::Actor;

#[topic(name = "orders.created", keyed_by = "order_id")]
pub struct OrderCreated {
    pub order_id: String,
    pub amount_cents: u64,
}

#[subscribe(topic = "orders.created", durable = "billing")]
async fn on_order_created(actor: Box<dyn Actor>, event: OrderCreated) -> photon::Result<()> {
    tracing::info!(actor = actor.label(), order = %event.order_id, cents = event.amount_cents);
    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let photon = Photon::builder().auto_registry().build()?;
    photon.start_executor(Arc::new(JsonIdentityFactory))?;

    OrderCreated {
        order_id: "ord-1".into(),
        amount_cents: 9900,
    }
    .publish_on(&photon)
    .await?;
    Ok(())
}