pub struct Config {Show 19 fields
pub bind: SocketAddr,
pub db_path: PathBuf,
pub public_url: String,
pub allowed_dids: Vec<String>,
pub poll_interval: Duration,
pub retention_days: u32,
pub proxy_images: bool,
pub beta_cap: i64,
pub trusted_ip_header: Option<String>,
pub max_subs_per_did: i64,
pub max_feeds_global: i64,
pub max_entries_per_feed: i64,
pub db_size_watermark_bytes: i64,
pub sidecar: SidecarConfig,
pub cookie_secret: String,
pub dev_did: Option<String>,
pub resolver_base: String,
pub bot_secret: Option<String>,
pub claim_ttl_secs: i64,
}Expand description
Fully-resolved server configuration, materialized once at startup.
Fields§
§bind: SocketAddrThe socket address the HTTP server binds to.
db_path: PathBufFilesystem path to the SQLite cache/database file.
public_url: StringThe externally-reachable base URL (used to build the atproto OAuth callback and client-metadata URLs). No trailing slash.
allowed_dids: Vec<String>Optional login allow-list of atproto DIDs. Empty means the instance is open to any atproto identity that can log in.
poll_interval: DurationThe default per-feed poll interval.
retention_days: u32Retention window: read, unstarred entries older than this are pruned.
proxy_images: boolWhether to proxy feed images through the server (privacy vs. bandwidth).
beta_cap: i64Closed-beta seat cap: the maximum number of DIDs that may hold beta
access at once (redeeming an invite fails with CapacityFull past this).
From FEATHERREADER_BETA_CAP, default 100.
trusted_ip_header: Option<String>The reverse-proxy header the rate limiter TRUSTS for the real client IP,
e.g. Fly-Client-IP (bare Fly) or CF-Connecting-IP (Cloudflare). When
set, ONLY this header is consulted — never the spoofable multi-hop
X-Forwarded-For chain — and it falls back to the socket peer if the
header is absent/unparseable. Unset (the default) trusts the socket peer
only, which is correct for a direct bind with no proxy in front.
From FEATHERREADER_TRUSTED_IP_HEADER.
max_subs_per_did: i64Per-DID subscription cap. A DID may hold at most this many subscriptions;
add_subscription rejects over it and import_opml trims to it. Bounds
the storage/poller blast radius of one account on a small box.
From FEATHERREADER_MAX_SUBS_PER_DID, default 500.
max_feeds_global: i64Global ceiling on distinct feeds in the shared cache. A new feed is
refused once the feeds table holds this many rows (existing feeds still
poll). From FEATHERREADER_MAX_FEEDS, default 10_000.
max_entries_per_feed: i64Cap on how many entries are retained per feed on insert — the newest N by
published date; older rows are pruned in the same transaction so one
firehose feed can’t fill the disk. From FEATHERREADER_MAX_ENTRIES_PER_FEED,
default 2_000.
db_size_watermark_bytes: i64DB-size watermark, in bytes. Above it the background poller stops fetching
new content (and logs an alert) so the $3.50 box can’t be filled to a
crash. 0 disables the watermark. From FEATHERREADER_DB_SIZE_WATERMARK_BYTES,
default 2 GiB.
sidecar: SidecarConfigThe atproto OAuth sidecar wiring (base URL + shared internal secret).
HMAC key used to sign the session cookie. In production this MUST be set
(FEATHERREADER_COOKIE_SECRET); a stable dev fallback is used otherwise
so local runs work without configuration.
dev_did: Option<String>Optional dev-only DID: when set, a request with no valid session cookie is served as this DID (local runs without the OAuth sidecar). Unset in a real deployment — no session then means “logged out”.
resolver_base: StringBase URL of the atproto handle resolver (com.atproto.identity.resolveHandle),
no trailing slash. Used by the pre-handshake beta gate to turn a submitted
handle into a DID so an existing seat can be honored on a cookie-less first
login. Defaults to crate::atproto::DEFAULT_RESOLVER_HOST. From
FEATHERREADER_RESOLVER_HOST.
bot_secret: Option<String>Shared bearer secret gating the headless bot mint endpoint (POST /bot/claims), sent by the follow→invite bot as X-Bot-Secret. When empty
the endpoint is DISABLED (503) — a bot can’t mint. Like the cookie/sidecar
secrets it MUST be set on a production-like instance (fail-loud at boot);
on a loopback/dev instance it stays unset so /bot/claims is simply off
until an operator opts in. From FEATHERREADER_BOT_SECRET.
claim_ttl_secs: i64TTL (seconds) for a claim invite code minted by POST /bot/claims. The
bot delivers the claim link asynchronously (a public skeet), so this is a
generous window — the admin-mint browser flow’s 30-minute TTL would expire
before the follower ever taps the link. From FEATHERREADER_CLAIM_TTL_SECS,
default 14 days.
Implementations§
Source§impl Config
impl Config
Sourcepub fn from_env() -> Result<Self>
pub fn from_env() -> Result<Self>
Build a Config from the process environment, falling back to the
defaults above for anything unset. Returns an error only when a present
variable fails to parse — an unset variable is never an error.
Sourcepub fn did_allowed(&self, did: &str) -> bool
pub fn did_allowed(&self, did: &str) -> bool
Whether the given atproto DID is permitted to log in. When no allow-list is configured the instance is open, so every DID is allowed.
Sourcepub fn admin_seed_dids(&self) -> &[String]
pub fn admin_seed_dids(&self) -> &[String]
The admin-bootstrap seed for the closed-beta gate: the DIDs that get a
beta_access seat automatically (via crate::store::ensure_seed) so a
fresh instance always has at least the operator(s) inside the gate and
able to mint invite codes.
Reuses ALLOWED_DIDS as the seed source — the same “these are the people
I trust on this instance” concept — so operators don’t configure the list
twice. Returns a borrowed slice (empty when the instance is open / no
allow-list is set, in which case there is nothing to seed).
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Config
impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnsafeUnpin for Config
impl UnwindSafe for Config
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more