LLAR Configuration
Runtime configuration is written in .llar files. These files define sources, schedules, processing hooks, highlights, and related behavior.
This page is generated from metadata attached to LLAR's config and source vars. The same content is rendered in the dashboard and exported for docs.llar.dev.
Quick Start
A minimal config defines one or more sources and a schedule that updates them.
(fetch github-llar-releases (src/feed "https://github.com/irq0/llar/releases.atom") :tags #{:my-first-feed :github})
(fetch hn-frontpage (src/hn :front_page) :tags #{:my-first-feed :hackernews})
(sched-fetch my-first-feeds :now-and-hourly (some #{:my-first-feed} $TAGS))Runtime Constructs
Top-level forms accepted in .llar files.
fetch
Define a source, its source tags, UI options, and optional item processing hooks.
(fetch src-key src & body)Options and notes
- :tags tags applied to the source. a set of keywords
- :options reader behavior changes. a set of keywords. supported: :mark-read-on-view, :main-list-use-description,
- :pre pre-processing function body
- :pre-fns a list of functions to pre-process items
- :rm a filter function body
- :rm-fn a list of filter functions
- :post a post-processing function body
- :post-fns a list of post-processing functions
Example
(fetch github-llar-releases
(src/feed "https://github.com/irq0/llar/releases.atom")
:tags #{:github :release})fetch-reddit
Define a Reddit source with a generated source key and the source tag :reddit.
(fetch-reddit src & body)Options and notes
- :min-score score below will be filtered out
- :dynamic? enable dynamic score cut-off feature. keep top 5% rated entries
- :tags see fetch
- :options see fetch
Example
(fetch-reddit (src/reddit "clojure" :top :week)
:min-score 50
:tags #{:programming})sched-fetch
Schedule updates for all fetchable sources matching a predicate.
(sched-fetch SCHED-NAME CHIME-TIMES PREDICATE)Options and notes
- PREDICATE can use the source predicate bindings
- CHIME-TIMES can be a canned schedule keyword or a chime time sequence
Example
(sched-fetch my-feeds :now-and-hourly
(some #{:my-feed-group} $TAGS))autoread
Automatically remove :unread from old items fetched from matching sources.
(autoread SCHED-NAME PERIOD PREDICATE)Options and notes
- PERIOD is a java-time period such as (time/weeks 4)
- PREDICATE can use $KEY, $SRC, and $TAGS
Example
(autoread reddit-ages-fast (time/weeks 4)
(some #{:reddit} $TAGS))highlight
Add :highlight to items matching configured words or authors.
(highlight words|authors VALUE...)Options and notes
- words are matched against extracted item terms
- authors are matched case-insensitively
Example
(highlight words "llar" "rss")
(highlight authors "Douglas Engelbart")sort-default
Set the default sort order for source or item tag views.
(sort-default TAG-KEY SORT-ORDER)Options and notes
- SORT-ORDER is one of :newest, :ranked, or :oldest
Example
(sort-default :blog :ranked)rc
Read or set dynamic runtime behavior config. Reads resolve runtime overrides first, system config values that differ from shipped defaults second, and shipped defaults last.
(rc PATH VALUE)Options and notes
- PATH is a vector under a supported runtime config root
- VALUE is validated with the path's runtime config spec
- Supported roots include [:reader ...], [:podcast ...], [:digest], and [:update]
Example
(rc [:reader :ranking :highlight-boost-hours] 48)
(rc [:reader :default-list-view :storage] :headlines)reader-favorites
Set the complete ordered reader favorite navigation list.
(reader-favorites FAVORITES)Options and notes
- FAVORITES is the complete ordered list of [KEY GROUP] pairs
- KEY is a source tag, item tag, source key, or view key
- GROUP is one of :default, :item-tags, :source-tag, or :type
- Omit entries from FAVORITES to remove them from the reader navigation
Example
(reader-favorites
[[:all :default]
[:saved :item-tags]
[:highlight :item-tags]
[:bookmark :type]])reader-default-list-view
Set the default reader list style for one group item.
(reader-default-list-view KEY STYLE)Options and notes
- KEY is a source tag, item tag, source key, or view key
- STYLE is one of :headlines or :gallery
Example
(reader-default-list-view :storage :headlines)
(reader-default-list-view :tweet :gallery)reader-ranking
Set reader ranking tuning values.
(reader-ranking KEY VALUE ...)Options and notes
- KEY is :highlight-boost-hours or :rarity-boost-cap-hours
- VALUE is numeric
- Multiple key/value pairs can be set in one form
Example
(reader-ranking :highlight-boost-hours 48
:rarity-boost-cap-hours 168)reader-url-handler
Configure reader annotation export through an external URL handler.
(reader-url-handler CONFIG)Options and notes
- CONFIG is nil, a map, or key/value pairs
- Required key: :template
- Optional keys: :name, :icon
- The template supports {title}, {url}, {id}, {source}, and {body}
Example
(reader-url-handler :name "Org-roam"
:icon "fas fa-brain"
:template "org-protocol://roam-ref?ref={url}&title={title}&body={body}")podcast-retention
Override podcast retention for a source.
(podcast-retention SOURCE-KEY LIMIT)Options and notes
- SOURCE-KEY is the configured source key
- LIMIT is the episode count to retain
Example
(podcast-retention my-video-feed 10)digest
Configure and enable digest delivery.
(digest KEY VALUE ...)Options and notes
- Required key: :to
- Optional keys: :from, :limit, :inline-images?, :keep-unread-issues
- Defining digest enables digest delivery.
Example
(digest :to "you_abc123@kindle.com"
:from "llar@example.org"
:limit 200
:inline-images? true
:keep-unread-issues 1)podcast-download
Configure and enable podcast media downloads.
(podcast-download KEY VALUE ...)Options and notes
- Optional keys: :video-format, :extra-args, :max-attempts, :retry-cooldown-minutes
- Defining podcast-download enables podcast media downloading and retention jobs.
Example
(podcast-download :video-format "bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080]"
:extra-args ["--embed-metadata" "--embed-chapters"]
:max-attempts 3
:retry-cooldown-minutes 30)Source Constructors
Source constructors live in the src namespace inside .llar files.
src/website
Generic website fetch.
(src/website url & {:as args})Schemas
:irq0/url-str(and string? (try (as-url %) (catch java.lang.Exception _ false))):irq0-src-args/user-agent(or :default keyword? :custom string?)
Example
(src/website "https://example.org/article.html")src/custom
Custom function source returning LLAR item data.
(src/custom id fn)Schemas
keyword?keyword?fn?fn?
Example
(src/custom :my-source (fn [] []))src/website+paywall
Website fetch using a cookie store function.
(src/website+paywall url cookie-getter & {:as args})Schemas
:irq0/url-str(and string? (try (as-url %) (catch java.lang.Exception _ false)))fn?fn?:irq0-src-args/user-agent(or :default keyword? :custom string?)
Example
(src/website+paywall "https://example.org" cookie-store)src/feed
RSS, Atom, and similar feed formats.
(src/feed url & {:as args})Schemas
:irq0/url-str(and string? (try (as-url %) (catch java.lang.Exception _ false))):irq0-src-args/user-agent(or :default keyword? :custom string?):irq0-src-args/force-update?boolean?
Defaults
{:user-agent :default, :force-update? false}Example
(src/feed "https://github.com/irq0/llar/releases.atom")src/selector-feed
Build a feed from an HTML page using Hickory selectors.
(src/selector-feed url selectors extractors args)Schemas
:irq0-src-selectors/selectors(keys :req-un [:irq0-src-selectors/urls] :opt-un [:irq0-src-selectors/ts :irq0-src-selectors/title :irq0-src-selectors/author :irq0-src-selectors/content :irq0-src-selectors/description]):irq0-src-selectors/extractors(keys :opt-un [:irq0-src-selectors/urls :irq0-src-selectors/ts :irq0-src-selectors/title :irq0-src-extractors/author :irq0-src-selectors/content :irq0-src-selectors/description])
Example
(src/selector-feed "https://example.org" {:urls (S/tag :a)} {} {})src/wp-json
WordPress REST API posts.
(src/wp-json url & {:as args})Schemas
:irq0/url-str(and string? (try (as-url %) (catch java.lang.Exception _ false))):irq0-src-args/user-agent(or :default keyword? :custom string?):irq0-src-args/force-update?boolean?
Defaults
{:user-agent :default, :force-update? false}Example
(src/wp-json "https://example.org/wp-json/")src/twitter-search
Twitter search source.
(src/twitter-search query oauth-creds)Schemas
string?string?:irq0-src-twitter/credentials(keys :req-un [:irq0-src-twitter/app-key :irq0-src-twitter/app-secret :irq0-src-twitter/user-token :irq0-src-twitter/user-token-secret])
Example
(src/twitter-search "clojure" ($credentials :twitter-api))src/twitter-timeline
Twitter home timeline source.
(src/twitter-timeline oauth-creds)Schemas
:irq0-src-twitter/credentials(keys :req-un [:irq0-src-twitter/app-key :irq0-src-twitter/app-secret :irq0-src-twitter/user-token :irq0-src-twitter/user-token-secret])
Example
(src/twitter-timeline ($credentials :twitter-api))src/readability
Single web page processed through article extraction.
(src/readability url & {:as args})Schemas
:irq0/url-str(and string? (try (as-url %) (catch java.lang.Exception _ false))):irq0-src-args/user-agent(or :default keyword? :custom string?)
Example
(src/readability "https://example.org/article.html")src/reddit
Reddit listing source.
(src/reddit subreddit listing)
(src/reddit subreddit listing timeframe)Schemas
string?string?#{:best :controversial :hot :new :random :rising :top}#{:best :controversial :hot :new :random :rising :top}#{:all :day :hour :month :week :year}#{:all :day :hour :month :week :year}
Example
(src/reddit "clojure" :top :week)src/imap
IMAP or IMAPS mailbox source using credentials from credentials.edn.
(src/imap url-str creds)Schemas
string?string?:irq0-src-mailbox/credentials(keys :req-un [:irq0-src-mailbox/username :irq0-src-mailbox/password])
Example
(src/imap "imaps://imap.example.org/INBOX" ($credentials :imap))src/hn
Hacker News via Algolia.
(src/hn tag & {:as args})Schemas
:irq0-hn/tag(and keyword? (#{:show_hn :comment :front_page :ask_hn :job} %)):irq0-hn/args(keys :opt-un [:irq0-hn-filter/query :irq0-hn-filter/count :irq0-hn-filter/filters :irq0-hn-filter/count :irq0-hn-filter/min-score :irq0-hn-filter/min-comments :irq0-hn-filter/created-after])
Defaults
{:count 1000}Example
(src/hn :front_page :query "clojure" :min-score 20)src/streaming-channel
Streaming channel source such as supported video/audio channels.
(src/streaming-channel url & {:as args})Schemas
:irq0/url-str(and string? (try (as-url %) (catch java.lang.Exception _ false)))
Defaults
{:max-results 30}Example
(src/streaming-channel "https://www.youtube.com/@veritasium")src/github-issues
Search GitHub issues/PRs. Query uses GitHub search syntax. Date tokens like {{last-week}} are expanded at fetch time.
(src/github-issues query & {:as args})Schemas
:irq0-gh/querystring?:irq0-gh/args(keys :opt-un [:irq0-gh/per-page :irq0-gh/sort :irq0-gh/order])
Defaults
{:per-page 30, :order :desc}Example
(src/github-issues "repo:ceph/ceph is:pr created:>{{last-week}}")src/github-repos
Search GitHub repositories. Query uses GitHub search syntax. Date tokens like {{last-week}} are expanded at fetch time.
(src/github-repos query & {:as args})Schemas
:irq0-gh/querystring?:irq0-gh/args(keys :opt-un [:irq0-gh/per-page :irq0-gh/sort :irq0-gh/order])
Defaults
{:per-page 30, :order :desc}Example
(src/github-repos "language:clojure stars:>20" :sort :stars)Canned Schedules
These keywords can be passed as the schedule argument to sched-fetch and internal LLAR schedulers.
:during-daytime | Daily at 10:00, 12:00, 13:00, 14:00, 16:00, and 18:00. |
|---|---|
:sundays | Weekly on Sunday at 05:00. |
:early-morning | Daily at 07:00. |
:now-and-early-morning | Once within the next 0-120 seconds, then daily at 07:00. |
:now-and-hourly | Once within the next 0-120 seconds, then every hour. |
:hourly | Every hour. |
:now-and-every-5-minutes | Once within the next 0-120 seconds, then every 5 minutes. |
Processing Hooks
:pre, :rm, and :post forms are evaluated with these bindings.
$item | The full item. |
|---|---|
$key | Source key as keyword. |
$title | Item title, or empty string. |
$authors | Item authors, or empty string. |
$tags | Source tags configured on the fetch definition. |
$raw | Raw fetched data when supported by the source. |
$url | Item URL, or empty string. |
$html | HTML content, or empty string. |
$text | Plain text content, or empty string. |
$score | Score for sources such as Reddit or Hacker News, or -1. |
$options | Source options configured on the fetch definition. |
$entry | The item entry map. |
Source Predicate Bindings
sched-fetch and autoread predicates are evaluated with these bindings.
$KEY | Source key as keyword. |
|---|---|
$SRC | Source constructor value. |
$TAGS | Source tags configured on the fetch definition. |
Helper Bindings
$add-tag | Return a processor that adds an item tag. |
|---|---|
$add-tag-filter | Return a processor that adds a tag when a predicate matches. |
$category-rm | Build a filter that removes items by feed category. |
$credentials | Read an entry from credentials.edn. |
$ellipsify | Truncate text with an ellipsis. |
$exchange | Swap two item paths. |
$extract | Run article extraction on HTML content. |
$fetch | Run LLAR HTTP fetch. |
$hickory-sanitize-blobify | Sanitize and blobify Hickory content. |
$hickory-to-html | Render Hickory as HTML. |
$html-to-hickory | Parse HTML into Hickory. |
$html2text | Convert HTML to plain text. |
$http-cookie-store | Create a clj-http cookie store. |
$http-get | Call clj-http.client/get. |
$http-post | Call clj-http.client/post. |
$make-item-hash | Create a stable LLAR item hash. |
$parse-ts | Parse a timestamp into a zoned date time. |
$parse-url | Parse or absolutify URLs. |
$uri-path | Read the path from a URI. |
Available Namespaces
src | llar.src |
|---|---|
string | clojure.string |
log | clojure.tools.logging |
S | hickory.select |
time | java-time.api |
Feature Examples
Feature-specific configuration examples that are not top-level .llar constructs.
zotero-export-links
credentials.ednOptions and notes
- Enables the reader's Zotero annotation export action
- The Zotero API key must allow item creation
- The exported item is stored in a Zotero collection named llar
Example
{:zotero {:api-key "ZOTERO_API_KEY"
:user-id "ZOTERO_USER_ID"}}Examples
fetch
(fetch github-llar-releases
(src/feed "https://github.com/irq0/llar/releases.atom")
:tags #{:github :release})fetch-reddit
(fetch-reddit (src/reddit "clojure" :top :week)
:min-score 50
:tags #{:programming})sched-fetch
(sched-fetch my-feeds :now-and-hourly
(some #{:my-feed-group} $TAGS))autoread
(autoread reddit-ages-fast (time/weeks 4)
(some #{:reddit} $TAGS))highlight
(highlight words "llar" "rss")
(highlight authors "Douglas Engelbart")sort-default
(sort-default :blog :ranked)rc
(rc [:reader :ranking :highlight-boost-hours] 48)
(rc [:reader :default-list-view :storage] :headlines)reader-favorites
(reader-favorites
[[:all :default]
[:saved :item-tags]
[:highlight :item-tags]
[:bookmark :type]])reader-default-list-view
(reader-default-list-view :storage :headlines)
(reader-default-list-view :tweet :gallery)reader-ranking
(reader-ranking :highlight-boost-hours 48
:rarity-boost-cap-hours 168)reader-url-handler
(reader-url-handler :name "Org-roam"
:icon "fas fa-brain"
:template "org-protocol://roam-ref?ref={url}&title={title}&body={body}")podcast-retention
(podcast-retention my-video-feed 10)digest
(digest :to "you_abc123@kindle.com"
:from "llar@example.org"
:limit 200
:inline-images? true
:keep-unread-issues 1)podcast-download
(podcast-download :video-format "bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080]"
:extra-args ["--embed-metadata" "--embed-chapters"]
:max-attempts 3
:retry-cooldown-minutes 30)src/website
(src/website "https://example.org/article.html")src/custom
(src/custom :my-source (fn [] []))src/website+paywall
(src/website+paywall "https://example.org" cookie-store)src/feed
(src/feed "https://github.com/irq0/llar/releases.atom")src/selector-feed
(src/selector-feed "https://example.org" {:urls (S/tag :a)} {} {})src/wp-json
(src/wp-json "https://example.org/wp-json/")src/twitter-search
(src/twitter-search "clojure" ($credentials :twitter-api))src/twitter-timeline
(src/twitter-timeline ($credentials :twitter-api))src/readability
(src/readability "https://example.org/article.html")src/reddit
(src/reddit "clojure" :top :week)src/imap
(src/imap "imaps://imap.example.org/INBOX" ($credentials :imap))src/hn
(src/hn :front_page :query "clojure" :min-score 20)src/streaming-channel
(src/streaming-channel "https://www.youtube.com/@veritasium")src/github-issues
(src/github-issues "repo:ceph/ceph is:pr created:>{{last-week}}")src/github-repos
(src/github-repos "language:clojure stars:>20" :sort :stars)zotero-export-links
{:zotero {:api-key "ZOTERO_API_KEY"
:user-id "ZOTERO_USER_ID"}}Runtime Config Settings
rc controls dynamic runtime behavior settings. It reads runtime overrides first, system config values that differ from shipped defaults second, and shipped defaults from resources/config.edn last.
| Path | Description | Spec | System config path | Example |
|---|---|---|---|---|
[:reader :favorites] | Favorite reader navigation entries. | :irq0-appconfig/favorites(coll-of (tuple keyword? :irq0-appconfig/view-group)) | [:ui :favorites] | (rc [:reader :favorites] VALUE) |
[:reader :default-list-view] | Default reader list style by group item. | :irq0-appconfig/default-list-view(map-of keyword? :irq0-appconfig/list-view) | [:ui :default-list-view] | (rc [:reader :default-list-view] VALUE) |
[:reader :ranking] | Ranking query tuning. | :irq0-appconfig/ranking(keys :opt-un [:irq0-appconfig/highlight-boost-hours :irq0-appconfig/rarity-boost-cap-hours]) | [:ranking] | (rc [:reader :ranking] VALUE) |
[:reader :export :url-handler] | External URL handler used for reader annotation export. | :irq0-appconfig/url-handler(nilable (keys :req-un [:irq0-appconfig/template] :opt-un [:irq0-appconfig/name :irq0-appconfig/icon])) | [:export :url-handler] | (rc [:reader :export :url-handler] VALUE) |
[:podcast :retention] | Podcast episode retention policy. | :irq0-appconfig/podcast-retention(keys :req-un [:irq0-appconfig/default-episode-limit] :opt-un [:irq0-appconfig/sources]) | [:api :podcast :retention] | (rc [:podcast :retention] VALUE) |
[:digest] | Digest delivery and rendering policy. | :irq0-appconfig/runtime-digest(and (keys :opt-un [:irq0-appconfig/enabled? :irq0-appconfig/to :irq0-appconfig/from :irq0-appconfig/limit :irq0-appconfig/inline-images? :irq0-appconfig/keep-unread-issues]) (or (not (:enabled? %)) (string? (:to %)))) | [:api :digest] | (rc [:digest] VALUE) |
[:podcast :enabled?] | Enable podcast media downloading and retention jobs. | :irq0-appconfig/podcast-enabledboolean? | (rc [:podcast :enabled?] VALUE) | |
[:podcast :download] | Podcast media downloader policy. | :irq0-appconfig/podcast-download(keys :opt-un [:irq0-appconfig/video-format :irq0-appconfig/extra-args :irq0-appconfig/max-attempts :irq0-appconfig/retry-cooldown-minutes]) | [:api :podcast] | (rc [:podcast :download] VALUE) |
[:podcast :scan] | Podcast scanner policy. | :irq0-appconfig/podcast-scan(keys :req-un [:irq0-appconfig/limit]) | (rc [:podcast :scan] VALUE) | |
[:update] | Source update retry policy. | :irq0-appconfig/update(keys :req-un [:irq0-appconfig/max-retry]) | [:update-max-retry] | (rc [:update] VALUE) |
System Config
System config is EDN, loaded at startup before runtime .llar files. It configures paths, commands, API ports, PostgreSQL pools, mail transport, credentials location, and other service-level settings.
Runtime behavior settings such as reader favorites, default list views, ranking tuning, and podcast retention are available through rc. Existing system config keys for those settings remain supported through their system config paths.
Use resources/config.edn as the complete default example and docker/docker-config.edn for Docker Compose deployments. Secrets belong in credentials.edn.