Skip to content

bokelleher/rust-pois

Repository files navigation

POIS ESAM Server

A lightweight, self-contained Rust server for Program Opportunity Information Service (POIS) and ESAM (Event Signaling and Management) workflows.
It provides a REST/JSON API, simple web UI, and SQLite persistence for defining channels, rules, and ESAM event logs.

It is also the reference implementation of SCTE 130-9 (SESAME)Secure ESAM Authentication and Message Encryption — adding HMAC-SHA256 authentication, channel-scoped authorization, and AES-256-GCM payload encryption to the ESAM exchange with measured sub-millisecond per-request overhead (docs/benchmarks.md). The wire format is specified in docs/SESAME.md.


🚀 Features

  • ESAM / SCTE-35 XML processing via REST endpoints
  • SESAME (SCTE 130-9) security — three additive, independently-enableable tiers, bidirectional (verifies inbound requests and signs outbound POIS responses), with no ESAM XML schema changes
  • SCTE-35 tools (build / decode / validate) — the decoder accepts base64, hex, or binary input
  • Front-end web UI served directly from /static
  • Channel and Rule management API with bearer authentication
  • Automatic SQLite migrations on first start
  • Built-in default default channel + Default noop rule (seeded automatically)
  • Event logging and monitoring
  • TLS optional (via POIS_TLS_CERT / POIS_TLS_KEY)
  • Zero external dependencies beyond SQLite and Rust
  • One-shot install and uninstall scripts for Ubuntu 24.04

🧩 Environment Variables

Variable Description Default
POIS_PORT Listening port for the web server 8080
POIS_DB SQLite database URL (absolute path recommended) sqlite://pois.db
POIS_JWT_SECRET Secret key for JWT token signing (auto-generated by installer) required
POIS_TLS_CERT, POIS_TLS_KEY Optional TLS PEM paths unset
POIS_SESAME_KEYS SESAME signing/encryption key material (JSON, see below). Unset ⇒ SESAME inactive unset
POIS_SESAME_MIN_TIER Default minimum SESAME tier (0–3) when a channel has no explicit policy 0
POIS_SESAME_REPLAY_WINDOW SESAME replay/freshness window, seconds 300
POIS_SESAME_RESPONSE_KEYID Signing key-id used to sign POIS responses. Unset ⇒ responses unsigned unset
POIS_SESAME_RESPONSE_ENCID Encryption key-id for Tier 3 responses unset

These are injected automatically by the installer into the systemd unit. POIS_JWT_SECRET is generated fresh on each install using openssl rand.


🔐 SESAME (SCTE 130-9) Security

SESAME secures the ESAM HTTP exchange in three additive tiers, conveyed entirely in HTTP headers (and, for Tier 3, by encrypting the body). The ESAM XML is never modified.

Tier Protects against Mechanism
1 Authentication + Integrity injection, tampering, replay, DoS HMAC-SHA256 signing, timestamp, nonce
2 Authorization cross-channel interference channel-scoped credential binding (X-SESAME-Scope)
3 Encryption signal reconnaissance AES-256-GCM payload encryption

When no POIS_SESAME_* variables are set, the ESAM path behaves exactly as before (Tier 0 / unauthenticated) — adoption is incremental and backward compatible. Per-channel minimum tier is stored in channels.sesame_min_tier.

Key configuration (POIS_SESAME_KEYS, distributed out of band per §8.2.5):

{
  "signing": [
    {"key_id": "sas-east-01",  "secret_hex": "", "channels": ["SportsFeed-East"]},
    {"key_id": "pois-primary", "secret_hex": "", "channels": ["*"]}
  ],
  "encryption": [
    {"enc_key_id": "enc-sportsfeed-2026q1", "key_hex": "<64 hex chars = 32 bytes>"}
  ]
}

Measured overhead on AWS c6i.xlarge (Intel Ice Lake, AES-NI + SHA; the hardware the spec cites — full table and methodology in docs/benchmarks.md):

Combined per-request path p50 (1 KB) p99 (16 KB)
Inbound — verify + authz + decrypt 3.31 µs 0.027 ms
Outbound — encrypt + sign 4.23 µs 0.028 ms

Worst-case combined p99 is 0.028 ms — ~35× inside the 1 ms budget. Pure-Rust RustCrypto stack (hmac, sha2, aes-gcm), builds everywhere.


🛠️ Quick Start (Production)

sudo apt update
sudo apt install git -y
git clone https://github.com/bokelleher/rust-pois.git
cd rust-pois
sudo ./install.sh

The installer will automatically install Rust and build the binary from source. Allow 2-3 minutes for the build to complete.

Follow prompts for:

  • service user (default pois)
  • port (default 8080)

When complete:

sudo systemctl status pois

Visit the UI:

http://<server-ip>:<port>/

🧑‍💻 Developer Shortcuts

make install      # wraps: sudo ./install.sh
make uninstall    # wraps: sudo ./uninstall.sh
make run-dev      # runs a dev server on localhost:18080 using pois.dev.db
make build        # cargo build --release

Override defaults if needed:

make run-dev DEV_PORT=19000

🧹 Factory Reset

To wipe the SQLite database and reseed the default channel/rule:

sudo ./factory_reset.sh

The script will:

  1. Stop the pois service
  2. Delete /opt/pois/pois.db
  3. Restart the service — migrations + seed will run automatically

⚠️ Warning: This erases all channels, rules, and events.


🔍 Health & Logs

Purpose Command
View logs sudo journalctl -u pois -f
Health check curl http://localhost:<port>/healthz
Database inspect sqlite3 /opt/pois/pois.db ".tables"

📦 Uninstall

chmod +x uninstall.sh
sudo ./uninstall.sh

Removes the binary, systemd unit, and optional user/data directory.


🗺️ Roadmap

Planned/proposed enhancements are tracked in ROADMAP.md — including SMPTE‑timecode/frame‑rate rendering and SCTE‑104 conversion for the SCTE‑35 tools, human‑readable UPIDs in the Event Monitor, logging of SESAME rejections, and a distributed (Redis) replay cache for multi‑node SESAME.


🧾 License

MIT License © 2025 Bo Kelleher

About

A POIS Server written in Rust

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors