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/query string?
  • :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/query string?
  • :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-daytimeDaily at 10:00, 12:00, 13:00, 14:00, 16:00, and 18:00.
:sundaysWeekly on Sunday at 05:00.
:early-morningDaily at 07:00.
:now-and-early-morningOnce within the next 0-120 seconds, then daily at 07:00.
:now-and-hourlyOnce within the next 0-120 seconds, then every hour.
:hourlyEvery hour.
:now-and-every-5-minutesOnce within the next 0-120 seconds, then every 5 minutes.

Processing Hooks

:pre, :rm, and :post forms are evaluated with these bindings.

$itemThe full item.
$keySource key as keyword.
$titleItem title, or empty string.
$authorsItem authors, or empty string.
$tagsSource tags configured on the fetch definition.
$rawRaw fetched data when supported by the source.
$urlItem URL, or empty string.
$htmlHTML content, or empty string.
$textPlain text content, or empty string.
$scoreScore for sources such as Reddit or Hacker News, or -1.
$optionsSource options configured on the fetch definition.
$entryThe item entry map.

Source Predicate Bindings

sched-fetch and autoread predicates are evaluated with these bindings.

$KEYSource key as keyword.
$SRCSource constructor value.
$TAGSSource tags configured on the fetch definition.

Helper Bindings

$add-tagReturn a processor that adds an item tag.
$add-tag-filterReturn a processor that adds a tag when a predicate matches.
$category-rmBuild a filter that removes items by feed category.
$credentialsRead an entry from credentials.edn.
$ellipsifyTruncate text with an ellipsis.
$exchangeSwap two item paths.
$extractRun article extraction on HTML content.
$fetchRun LLAR HTTP fetch.
$hickory-sanitize-blobifySanitize and blobify Hickory content.
$hickory-to-htmlRender Hickory as HTML.
$html-to-hickoryParse HTML into Hickory.
$html2textConvert HTML to plain text.
$http-cookie-storeCreate a clj-http cookie store.
$http-getCall clj-http.client/get.
$http-postCall clj-http.client/post.
$make-item-hashCreate a stable LLAR item hash.
$parse-tsParse a timestamp into a zoned date time.
$parse-urlParse or absolutify URLs.
$uri-pathRead the path from a URI.

Available Namespaces

srcllar.src
stringclojure.string
logclojure.tools.logging
Shickory.select
timejava-time.api

Feature Examples

Feature-specific configuration examples that are not top-level .llar constructs.

zotero-export-links

credentials.edn

Options 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.

PathDescriptionSpecSystem config pathExample
[: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-enabled
boolean?
(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.