Expand description
Runtime configuration for the FeatherReader server.
Everything is env-driven with a sane default for every knob, so a bare
./featherreader boots and works — no config file required (the
“trivial to self-host” promise). The environment variables
all share the FEATHERREADER_* prefix:
| Variable | Default | Meaning |
|---|---|---|
FEATHERREADER_BIND | 127.0.0.1:8080 | host:port the HTTP server binds. |
FEATHERREADER_DB | featherreader.db | Path to the SQLite cache file. |
FEATHERREADER_PUBLIC_URL | http://localhost:8080 | Externally-reachable base URL (OAuth callback + client metadata). |
FEATHERREADER_ALLOWED_DIDS | (empty = open) | Comma-separated login allow-list of atproto DIDs. |
FEATHERREADER_POLL_INTERVAL | 3600 (1h) | Default per-feed poll interval, in seconds. |
FEATHERREADER_RETENTION_DAYS | 90 | Prune read, unstarred entries older than this. |
FEATHERREADER_PROXY_IMAGES | false | Proxy feed images so reader IPs aren’t leaked to feed hosts. |
FEATHERREADER_TRUSTED_IP_HEADER | (unset) | Trusted reverse-proxy header for the real client IP (e.g. Fly-Client-IP, CF-Connecting-IP). Unset trusts the socket peer only. |
FEATHERREADER_MAX_SUBS_PER_DID | 500 | Per-DID subscription cap. |
FEATHERREADER_MAX_FEEDS | 10000 | Global distinct-feed ceiling. |
FEATHERREADER_MAX_ENTRIES_PER_FEED | 2000 | Per-feed retained-entry cap (newest N). |
FEATHERREADER_DB_SIZE_WATERMARK_BYTES | 2 GiB | Above this the poller stops fetching new content (0 disables). |
FEATHERREADER_RESOLVER_HOST | https://bsky.social | atproto handle-resolver base (com.atproto.identity.resolveHandle) the pre-handshake beta gate uses to honor an existing seat on a cookie-less login. |
FEATHERREADER_BOT_SECRET | (unset = /bot/claims disabled) | Shared bearer secret (X-Bot-Secret) gating the headless follow→invite bot’s mint endpoint POST /bot/claims. Unset ⇒ endpoint returns 503. MUST be set (strong) on a production-like instance if the bot is used. |
FEATHERREADER_CLAIM_TTL_SECS | 1209600 (14 days) | TTL for a bot-minted claim invite code — long, since the claim link is delivered asynchronously (a public skeet). |
The atproto OAuth sidecar (@atproto/oauth-client-node) is configured with a
second small block — the base URL the Rust server reaches it on and the shared
secret gating its internal API (see SidecarConfig):
| Variable | Default | Meaning |
|---|---|---|
SIDECAR_PUBLIC_URL | http://127.0.0.1:8081 | Public base URL of the OAuth sidecar (its browser-facing /login, plus the OAuth client_id/redirect_uri). |
SIDECAR_INTERNAL_URL | (= SIDECAR_PUBLIC_URL) | Loopback base URL the Rust server reaches the sidecar’s /internal/* API on. Defaults to the public URL for single-URL local dev. |
SIDECAR_INTERNAL_SECRET | (dev fallback) | Shared X-Internal-Secret for the sidecar’s /internal/* API. |
FEATHERREADER_COOKIE_SECRET | (dev fallback) | HMAC key used to sign the session cookie. |
FEATHERREADER_DEV_DID | (unset) | When set, a request with no session cookie acts as this DID (local runs without the sidecar). |
FEATHERREADER_BIND also accepts the design’s FEATHERREADER_ADDR spelling
as a fallback for compatibility.
Structs§
- Config
- Fully-resolved server configuration, materialized once at startup.
- Sidecar
Config - Configuration for the atproto OAuth sidecar (
@atproto/oauth-client-node).