Instance configuration.
Every Limen instance is configured on its own. Options come from your
application's environment when started with {Limen, otp_app: app} (see
from_app/1), or are given directly with {Limen, name: name, config: opts}.
They are validated once, when the instance starts, and published with it,
so the request path never reads the application environment.
config :my_app, Limen,
mode: :enforce,
ipv6_prefix: 56Options
:secret_key- at least 32 random bytes, used to sign challenge tokens and pass cookies. Every node serving the same site needs the same key. Without one, a random key is generated at startup and a warning is logged. Set it fromconfig/runtime.exs, never in compiled config.:previous_secret_keys- earlier secret keys, still accepted when verifying tokens, for rotating the secret.:mode-:dry_run(default) or:enforce. In dry-run mode every decision is computed, recorded and emitted exactly as in enforce mode, but the request always continues. Routes and policies can override it.:trusted_proxies- CIDR ranges of the reverse proxies and TLS terminators in front of the application. Forwarding headers and the JA4 header are only read from these peers. Defaults to[].:client_ip_header- how trusted proxies report the client address:"x-forwarded-for","x-real-ip","forwarded"(RFC 7239) ornil(default) to always use the peer address. Forwarding chains are walked from the right, skipping trusted proxies, so clients cannot spoof their address by sending the header themselves.:ja4_header- request header the TLS terminator stores the JA4 fingerprint in. Defaults to"x-ja4".:ipv4_prefix- prefix length IPv4 clients are aggregated to. Defaults to32.:ipv6_prefix- prefix length IPv6 clients are aggregated to. One of48,56or64(default).:decision_log- sampled structured decision log, seeLimen.DecisionLog::sample_rate- fraction of:allowdecisions logged. Defaults to0.0.:non_allow_sample_rate- fraction of other decisions logged. Defaults to1.0.:size- ring buffer capacity. Defaults to1024.:flush_interval- milliseconds between flushes to the sink. Defaults to1000.:sink- where flushed decisions go: aLimen.DecisionLog.Sinkmodule, or{module, opts}. Defaults toLimen.DecisionLog.Logger, which logs them at its:level(:infoby default).:delivery-:batched(default): the flusher hands sampled decisions to the sink in batches, away from the request path.:inline, for tests only: each sampled decision goes to the sink at once, in the process that made it, so a sink writing through an Ecto sandbox sees the test's connection and a test can assert on what it wrote right after the request. A sink that raises raises in that process, failing the test. It puts the sink on the request path, so never use it in production. See "Decision log sinks" in the testing guide.
:asn- IP to ASN data, seeLimen.Signal.Asn::file- an iptoasn.comip2asn-combined.tsvfile (optionally gzipped) loaded at startup. With:url, where downloads are kept. Defaults tonil.:url- where to download the data from and check it for changes, such as"https://iptoasn.com/data/ip2asn-combined.tsv.gz". Needs a:file. Checks ask for data newer than the file, so when changing the:urlto another source, remove the file or change:filetoo. Defaults tonil: no downloads.:refresh- when to check the:url, seeLimen.Signal.Asn.Schedule::every- milliseconds between checks, at least a minute. Defaults to a day.:jitter- up to this share of:everyis added to each wait at random, so nodes do not check together. Defaults to0.1.:window- a{from, to}pair ofTimes (UTC), or a list of them, outside which scheduled checks do not run;tobeforefromruns over midnight. Defaults tonil: any time.:max_utilization- postpone a scheduled check while the BEAM's schedulers are busier than this share, between0and1. Defaults to0.9;nildoes not check.:max_memory- postpone a scheduled check while the BEAM uses more than this many bytes. Defaults tonil: no limit.:retry- milliseconds before trying a postponed check again, and a failed one, doubling with each failure up to:every. Defaults to ten minutes.:timeout- milliseconds a download may take. Defaults to five minutes.
:hosting- additional ASNs classified as hosting providers, on top of the built-in list.:source- theLimen.Signal.Asn.Sourceaddresses are looked up in. Defaults toLimen.Signal.Asn, the iptoasn.com data of:fileand:url, which must not be set with another source.
:fcrdns- crawler verification, seeLimen.Signal.Fcrdns::crawlers- map of crawler names (as classified byLimen.Signal.UserAgent) to the DNS suffixes their hosts must have, merged over the built-in map.:dns- module implementingLimen.Signal.Fcrdns.DNS. Defaults toLimen.Signal.Fcrdns.InetRes.:verified_ttl,:failed_ttl,:error_ttl- seconds results are cached. Default to86_400,3_600and60.:max_pending- addresses waiting for verification. Defaults to1_000; beyond that, claims stay unverified until the queue drains.:max_cache- cached results. Defaults to100_000.:interval- milliseconds between resolver runs. Defaults to50.:concurrencyand:timeout- lookups in flight and milliseconds each may take. Default to16and2_000.
:user_agents- tokensLimen.Signal.UserAgentclassifies, on top of its built-in lists, by family::crawler,:ai_crawler,:link_preview,:headlessor:tool, each a list of tokens (named after themselves) or{token, name}pairs, and:ignore, a list of built-in tokens to leave out. Listing a built-in token under a family moves it there. Tokens are matched as they appear in user agents, case included. Defaults to[].:shape- HTTP shape analysis, seeLimen.Signal.HttpShape::ignore_headers- headers left out of the header-order shape, such as those added by your proxies. Forwarding headers and the JA4 header are always left out.
:challenge- the proof-of-work challenge, seeLimen.Challenge::path- where Limen serves its challenge endpoints. Defaults to"/__limen".:ttl- seconds a challenge stays valid. Defaults to300.:pass_ttl- seconds a pass cookie stays valid. Defaults to3_600.:bind- what pass cookies and socket tokens are bound to: any of:prefix,:ja4and:user_agent. Defaults to all three. Leaving:prefixout keeps passes valid when a client changes networks, such as a phone moving from Wi-Fi to mobile data, at the cost of letting a pass be used from anywhere with the same JA4 and user agent. Changing it invalidates the passes already issued.:cookie- the pass cookie name. Defaults to"_limen_pass".:status- HTTP status of the challenge page. Defaults to403.:page- the module writing the challenge page, with texts in the visitor's language or markup of your own, seeLimen.Challenge.Page. Defaults toLimen.Challenge.Page, in English.:no_js- what clients without JavaScript get:{:meta_refresh, seconds}(default{:meta_refresh, 5}) lets them through after waiting,:denyshows a message asking to enable JavaScript.:secure_cookie-true,falseor:auto(default, secure over HTTPS).:replay_capacity- solved challenges remembered per:ttlto reject replays. Defaults to100_000.
:cluster- ban propagation across nodes, seeLimen.Cluster::enabled-false(default) ortrue. Read at startup.:scope- the:pgscope the instance starts and uses, which must be unique among the instances of a node. Defaults to one named after the instance, so instances of the same name on different nodes share their bans.:interval- milliseconds between broadcasts. Defaults to100.:max_outbox- bans waiting to be broadcast. Defaults to10_000.
:trap- honeypots, seeLimen.Trap::paths- trap path prefixes, such as["/archive/directory"]. A request under one of them is a confession: its prefix is flagged and sent to the maze. Defaults to[](no link traps).:ban- seconds a prefix that fell into a trap stays flagged. Defaults to86_400.:form_field- name of the decoy field ofLimen.Trap.form_fields/2. Defaults to"website".:min_fill_time- milliseconds a person needs at least to fill in a form; faster submissions are confessions. Defaults to3_000.
:maze- the slow, endless pages trapped clients get, seeLimen.Maze::corpus- text files the maze's language model also learns from, so its pages read like your site. Defaults to[].:bundled_corpus- whether to learn from Limen's own neutral text too. Defaults totrue; at least one corpus is needed.:drift-nil(default),:dailyor:weekly: how often every page changes. Without drift a page never changes.:max_concurrent- requests held in the maze at once; beyond that, they get an immediate429. Defaults to200.:admit-{module, function, args}, called withargsbefore a client is held; unless it returnstrue, the client gets an immediate429, as when the maze is full. For refusing the maze while the application is under load; it runs on the request path, so it must be cheap and must not call a process. Defaults tonil: every client is admitted up to:max_concurrent.:max_duration- milliseconds a maze response may take. Defaults to60_000.:delay-{min, max}milliseconds between chunks. Defaults to{1_000, 5_000}.:chunk-{min, max}bytes per chunk. Defaults to{64, 512}.:paragraphs-{min, max}paragraphs per page. Defaults to{4, 10}.:links-{min, max}links in a page's navigation and related lists. Defaults to{4, 10}.
:tarpit- limits on tarpitted requests, seeLimen.Tarpit::max_concurrent- requests held at once. Defaults to1_000.:max_delay- longest delay in milliseconds. Defaults to30_000.
:params- values of policy parameters, overriding the defaults policies declare, as a keyword list; see "Parameters" inLimen.Policy. Defaults to[].:lists- named lists loaded at startup, seeLimen.Lists: a list of values,{:cidr, ranges}or{:substrings, strings}per name.:state- sizing of the shared state, seeLimen.State::max_keys- exact keys per time-window epoch slot. Each window keeps up to three slots. Defaults to50_000.:sketch_widthand:sketch_depth- dimensions of the Count-Min Sketch counting keys beyond:max_keys, one per slot. Defaults to8_192and4(256 KiB per slot).:gcra_max_keys- keys tracked by hard limits. Defaults to100_000.:max_bans- concurrent bans. Defaults to100_000.:sweep_interval- milliseconds between sweeps of expired bans and idle limits. Defaults to5_000.:path_filter_capacity- (prefix, path) pairs remembered per minute to count distinct paths per client. Defaults to1_000_000(about 1 MiB per generation).:hll_precision- precision of the HyperLogLog counting distinct client prefixes. Defaults to12(4 KiB, 1.6% error).
Sketch dimensions are read once at startup.
Changing options at runtime
Limen.update_config/3 changes an option of a running instance. Options
given as keyword lists are merged into their current values, as if they
had been given together at startup, so changing one leaves the others as
they are:
Limen.update_config(:my_app, :trap, min_fill_time: 1_000)Some options are only read when the instance starts, and changing them at
runtime raises: :lists (use Limen.Lists instead), :secret_key and
:previous_secret_keys, and in their groups :state :max_keys,
:sketch_width, :sketch_depth, :path_filter_capacity and
:hll_precision; :cluster :enabled and :scope; :decision_log
:size; :challenge :replay_capacity; :maze :corpus and
:bundled_corpus; :asn :file, :url and :refresh. Restart the
instance to change them.
Summary
Functions
Builds a validated configuration from options.
The instances configured for app, with their options.
Validates and changes one option of a built configuration.
Types
Functions
Builds a validated configuration from options.
Raises ArgumentError on an unknown or invalid option.
The instances configured for app, with their options.
With no :instances option there is a single instance named after the
application. Otherwise each entry of :instances is an instance, whose
options are merged over the top-level ones.
config :my_app, Limen,
mode: :dry_run,
instances: [public: [], admin: [mode: :enforce]]
Validates and changes one option of a built configuration.
A keyword list is merged into the option's current value, recursively,
so update(config, :trap, min_fill_time: 0) keeps the trap paths. Any
other value replaces the current one. Raises ArgumentError on an invalid
value, and on a change to an option only read at startup (see "Changing
options at runtime" above).