Photon
Feature flags, wiring, and verify commands for adding Photon to a Rust service. Project overview: README.
Install
crates.io package uf-photon (Rust crate name remains photon):
= { = "uf-photon", = "0.1.1", = ["runtime", "mem"] }
Features
| Feature | Purpose |
|---|---|
runtime |
Full stack — backends, Photon, executor. |
mem |
Default in-process storage (InProcStoragePort) for tests and dev |
sqlite |
Embedded SQLite storage (photon-backend-sqlite) |
nats |
NATS JetStream storage adapter (photon-backend-nats) |
fluvio |
Fluvio storage adapter (photon-backend-fluvio) |
kafka |
Kafka storage adapter (photon-backend-kafka) |
Configuration reference: docs.rs photon::config. Primary tutorial: Getting started (Embedded vs Brokered publisher/worker).
Ships with no default features (default = []). Enable runtime + mem for the standard evaluation path.
How to run examples
Navigational index: examples/README.md (when-to-use ladder + links to each .rs file).
Canonical teaching path (start here). Topology docs: Embedded / Brokered.
All examples need PHOTON_TRANSPORT_KEY (base64 of 32 bytes). Dev/smoke key:
1. Embedded — embedded_mem (standalone)
One process, in-memory storage. No external services.
Success: stderr/tracing shows a published event id and a handler log line.
2. Embedded durable — embedded_sqlite (standalone)
Same API as (1), file-backed SQLite (write-through + in-memory live fanout).
# optional: PHOTON_SQLITE_PATH=/tmp/photon-example.db
3. Brokered — NATS publisher + worker (multi-process — run as a set)
Publisher and worker share one JetStream cluster. They are not useful alone. Teach the broker axis once with NATS; swap the builder for Kafka/Fluvio in production (see adapter rustdoc).
| Rule | Detail |
|---|---|
| Shared env | Same PHOTON_TRANSPORT_KEY, PHOTON_NATS_URL, PHOTON_NATS_STREAM on every process |
| Start order | Broker → worker(s) first → publisher |
| Local plaintext | PHOTON_ALLOW_INSECURE_BROKER=1 (dev/CI only; never in production) |
| Workers | Each process embeds Photon; call start_executor on workers only |
| Stop | Ctrl-C on the worker |
Local NATS (single node):
# Terminal 1 — worker (leave running)
# Terminal 2 — publisher (exits after one event)
Optional: start a second nats_worker in another terminal (same env) before publishing.
Production: use TLS (tls://…) + .credentials_file / PHOTON_NATS_CREDS; never set PHOTON_ALLOW_INSECURE_BROKER or PHOTON_ALLOW_DEV_TRANSPORT_KEY. Cluster labs: infra/broker/README.md.
4. Secure brokered — NATS TLS + credentials (nats_secure_worker / nats_secure_publisher)
Same pair topology as (3), but wired the production way: .require_tls() + .credentials_file / PHOTON_NATS_CREDS — never PHOTON_ALLOW_INSECURE_BROKER. Requires a real TLS-terminated NATS endpoint (e.g. via a sidecar proxy or managed JetStream); without one, both binaries print the runbook and exit cleanly instead of falling back to plaintext.
# cargo run -p uf-photon --example nats_secure_publisher --features runtime,nats
Success (with a TLS broker): worker worker received greeting; publisher published over TLS. Without one: both print a PHOTON_NATS_URL must be tls://… warning and return Ok.
5. Brokered — Kafka publisher + worker (kafka_worker / kafka_publisher)
Same publisher/worker contract as (3), backed by KafkaStoragePortBuilder. Local single-node lab: infra/broker/scripts/kafka-single.sh (KRaft, 127.0.0.1:9092).
&& && &&
# cargo run -p uf-photon --example kafka_publisher --features runtime,kafka
Success: worker worker received greeting; publisher kafka_publisher: published.
6. Brokered — Fluvio publisher + worker (fluvio_worker / fluvio_publisher)
Same publisher/worker contract as (3), backed by FluvioStoragePortBuilder. Local single-node lab: infra/broker/scripts/fluvio-single.sh (SC + SPU on 127.0.0.1:9103).
&& && &&
# cargo run -p uf-photon --example fluvio_publisher --features runtime,fluvio
Success: worker worker received greeting; publisher fluvio_publisher: published.
7. Durable consumer recovery — durable_consumer_recovery (standalone)
Single process, two phases: handles a batch with a durable = "…" subscription, force-flushes the checkpoint, "crashes" (drops the Photon handle), then opens a fresh SqliteStoragePort on the same file and resumes — proving the checkpoint-driven restart contract that nats_worker / kafka_worker / fluvio_worker rely on against their own brokers.
Success: phase 1: checkpoint committed … simulating a process crash followed by phase 2: resumed from checkpoint with no redelivery.
Other examples
| Example | Topology | Features | Notes |
|---|---|---|---|
subscribe_v2 |
Embedded | runtime,mem |
Arc<dyn Actor> + HandlerCtx + configure |
keyed_topic |
Embedded | runtime,mem |
keyed_by + typed subscribe_on filter |
manual_subscribe |
Embedded | runtime,mem |
Raw topic-name subscribe stream |
consumer_group |
Embedded | runtime,mem |
Group delivery / shards (single member) |
telemetry_ops_log |
Embedded | runtime,mem |
PhotonBuilder::ops_log |
Wiring
Follow the rustdoc Getting started for topology choice. Checklist:
- Build with
Photon::builder()— default installsInProcStoragePort(mem). RequiresPHOTON_TRANSPORT_KEY(TransportCrypto::from_env). - Optionally pass
.storage_port(Arc<dyn StoragePort>)forsqliteor broker adapters (Brokered: same port config on every binary). - Call
.auto_registry()when using#[photon::topic]/#[photon::subscribe]. - Keep the
Photonhandle and callpublish_on(&photon)/subscribe_on(&photon, opts)(preferred). - Optional:
photon::configure(photon)for process-wide.publish()/.subscribe()sugar. - Call
photon.start_executor(identity)on Embedded hosts and Brokered workers (publisher-only binaries can skip).
Default bootstrap (mem)
use Photon;
// Loads PHOTON_TRANSPORT_KEY via from_env().
let photon = builder.auto_registry.build?;
// EventType { ... }.publish_on(&photon).await?;
// Optional: configure(photon) for .publish() without a handle.
Custom storage port
use Arc;
use Photon;
use InProcStoragePort;
use TransportCrypto;
let port = new;
let photon = builder
.storage_port
.auto_registry
.build?;
Broker env vars and builder options: each adapter's *StoragePortBuilder rustdoc (linked from photon::config).
SQLite — durable single-process
Write-through persistence with in-memory live fanout (no external broker):
use Arc;
use ;
let port = new;
// Or: SqliteStoragePort::from_env().await? // reads PHOTON_SQLITE_PATH
let photon = builder
.storage_port
.auto_registry
.build?;
NATS JetStream — production (durable)
Durable subscriptions, checkpoint replay, and stream-sharded fleet ingress:
use Arc;
use ;
let port = new;
let photon = builder
.storage_port
.auto_registry
.build?;
NATS JetStream — high ingress (ephemeral)
Maximum publish throughput when durable replay and checkpoints are not required:
use Arc;
use ;
let port = new;
let photon = builder
.storage_port
.auto_registry
.build?;
Builder fields and env fallbacks: NatsStoragePortBuilder rustdoc.
Verify
Prefer AWS when local cargo is unavailable (remote check on an EC2 smoke host).
Full matrix: root README § Verify. Macro expansion: docs/macro-expansion.md.