feather-reader 0.2.4

A minimalist, atproto-native RSS/Atom reader in Rust — your feed subscriptions live in your own PDS.
Documentation
[package]
name = "feather-reader"
version = "0.2.4"
edition = "2021"
rust-version = "1.94"
description = "A minimalist, atproto-native RSS/Atom reader in Rust — your feed subscriptions live in your own PDS."
authors = ["Justin Stanley"]
license = "AGPL-3.0-only"
repository = "https://github.com/justin-stanley/feather-reader"
homepage = "https://feather-reader.com"
readme = "README.md"
keywords = ["rss", "atom", "atproto", "feed", "reader"]
categories = ["web-programming"]

# The crate ships a small library (shared types + module seams) and the server binary.
[lib]
name = "feather_reader"
path = "src/lib.rs"

[[bin]]
name = "featherreader"
path = "src/main.rs"

[dependencies]
# --- async runtime -------------------------------------------------------
tokio = { version = "1.45", features = ["full"] }

# --- web framework + middleware -----------------------------------------
# `multipart` powers the OPML file-upload import (POST /opml).
axum = { version = "0.8", features = ["multipart"] }
# fs = ServeDir/ServeFile for embedded/static assets; trace = request tracing
# layer; set-header = the global security-header layer (CSP + friends).
tower-http = { version = "0.7", features = ["fs", "trace", "set-header"] }

# --- HTTP client (feed fetch) -------------------------------------------
# default-features off to drop the OpenSSL/native-tls path; rustls keeps the
# static-binary story clean. gzip for polite/compressed feed fetches.
reqwest = { version = "0.13", default-features = false, features = ["rustls", "gzip", "json"] }

# --- feed parsing --------------------------------------------------------
feed-rs = "2.4"

# --- HTML sanitization (feeds are hostile input) -------------------------
ammonia = "4"

# --- local per-DID SQLite cache -----------------------------------------
# RUNTIME queries only (no compile-time `query!` macros, so no DATABASE_URL at
# build). sqlx 0.9 splits the old `runtime-tokio-rustls` feature into
# `runtime-tokio` + `tls-rustls-ring`. `sqlite-bundled` links SQLite into the
# binary for the single-file deploy story; `migrate` runs embedded migrations.
sqlx = { version = "0.9", default-features = false, features = [
    "runtime-tokio",
    "tls-rustls-ring",
    "sqlite-bundled",
    "migrate",
    "chrono",
    # `derive` pulls in sqlx-macros so the store's row structs can
    # `#[derive(FromRow)]` — the derive macro, not just the `FromRow` trait.
    "derive",
] }

# --- HTML templating (compile-time, embedded in the binary) --------------
# askama 0.16 dropped the separate `askama_axum` integration crate (deprecated,
# pinned to askama <0.13). `askama_web` provides the axum IntoResponse glue.
askama = "0.16"
askama_web = { version = "0.16", features = ["axum-0.8"] }

# --- serde ---------------------------------------------------------------
serde = { version = "1", features = ["derive"] }
serde_json = "1"

# --- error handling ------------------------------------------------------
anyhow = "1"
thiserror = "2"

# --- tracing -------------------------------------------------------------
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }

# --- time / dates --------------------------------------------------------
chrono = { version = "0.4", features = ["serde"] }

# --- URL parsing (feed autodiscovery, canonicalization) ------------------
url = "2"

# --- CSPRNG (opaque, unguessable session ids) ----------------------------
# Already in the dependency tree transitively; pinned here as a direct dep so
# the session-id minting reads from the OS CSPRNG rather than a home-rolled RNG.
getrandom = "0.4"

# --- filesystem stat (startup disk-headroom check) -----------------------
# Already in the tree transitively; pinned as a direct dep so the startup
# watermark-vs-disk safety check can call `statvfs(3)` on unix to compare the
# configured DB-size watermark against the DB volume's actual free space. Unix
# only — the target deploys are Linux/Fly and the check no-ops elsewhere.
[target.'cfg(unix)'.dependencies]
libc = "0.2"

[dev-dependencies]
# `ServiceExt::oneshot` drives the axum router through a single request in the
# web-layer tests (cache-control headers, rate-limit 429, the pre-handshake
# login refusal, admin-mint authz). Already in the lock transitively; the `util`
# feature exposes `oneshot`.
tower = { version = "0.5", features = ["util"] }