Configuring Iocaine
Last updated: 2025-07-11

`iocaine` can be configured via a TOML-format configuration file, or via the environment. Almost everything has sane defaults, but providing a wordlist, and at least one source for the markov generator is required. Providing a request handler - while not required - is strongly recommended.

The configuration file is split into five main sections: `[server]`, `[sources]`, `[generator]`, `[templates]`, and `[metrics]`.

When configured through environment variables, the naming follows the `IOCAINE__<section>__<property>` pattern. If subsections are involved, those are also separated by double underscores. For example, the `bind` option under the `[server]` section can be set via `IOCAINE__SERVER__BIND`, and the `path` option under `[server.request-handler]` translates to `IOCAINE__SERVER__REQUEST_HANDLER__PATH`. As you see, dashes are translated to underscores. Options where the value is a list, or a map, such as `IOCAINE__SOURCES__MARKOV`, the value of the environment variable must be a TOML-formatted array or map.

# `[server]`

The `[server]` section is used to configure the address iocaine will bind to, the optional request handler, and the bind address of the optional control socket. The default bind address is shown below:

    [server]
    bind = "127.0.0.1:42069"

> Important

> Binding to anything but a TCP port is only available in iocaine 2.2.0 and later, as are any other parameters than `bind`.

The bind address can be set to not only a TCP address and port pair, but to a Unix Domain Socket path, or - on Linux - to an Abstract Unix Domain Socket too (if the value begins with `@`).

When using a Unix Domain Socket, permissions on the created socket file can be controlled by the `[server].unix_listen_access` setting. By default, the permissions will not be touched, and will inherit `umask`. Valid values for this setting are: `owner`, `group`, and `everybody`.

    [server]
    bind = "/run/iocaine/iocaine.socket"
    unix_listen_access = "group"

## `[server.control]`

> Introduced in: iocaine 2.2.0

> Built-in reload introduced in: iocaine 2.3.0

Starting with iocaine 2.2.0, iocaine supports limited configuration reloading. This requires a control socket, and is not enabled by default. Otherwise it follows the same pattern as the `[server]` bind.

When enabled, it is possible to send a reload request to iocaine through that socket, to deploy a new configuration, without restarting the service. This will only succeed if the configurations are compatible: no bind addresses are changed, and the `[metrics]` section is unchanged. This limitation may be lifted in the future.

    [server.control]
    bind = "/run/iocaine/control.socket"
    unix_listen_access = "owner"

> Important

> It is highly recommended to use a Unix Domain Socket here, and restrict the permissions to “owner” or “group”.

Starting with iocaine 2.3.0, there is a built in `reload` command, which supports every kind of binding out of the box.

    iocaine --config /etc/iocaine/config.toml reload

Prior to that version, a reload request can be sent with - for example - `curl`:

    curl -s --fail --unix-socket /run/iocaine/control.socket \
         --json '{"path": "/etc/iocaine/config.toml"}' \
         http://iocaine/config/load

## `[server.request-handler]`

> Introduced in: iocaine 2.2.0

> Lua and Fennel introduced in: iocaine 2.4.0

    [server.request-handler]
    path = "/path/to/iocaine-modules"
    language = "roto" # or lua, or fennel
    # options = { compiler = "/path/to/fennel.lua" }

If one wishes to use `iocaine` for more than just garbage generation, that’s where the scriptable request handler support comes into play. With it, it is possible to write short programs that decide the fate of any incoming requests - see the request handler documentation for more information.

Within the configuration file, a `path` must be specified to use this feature, along with a `language` (which defaults to “roto”). This `path` must point to a directory that contains the appropriate entry point for the selected language. For Roto, this is `pkg.roto`, for Lua and Fennel, this is `main.lua` and `main.fnl`, respectively.

# `[sources]`

The `[sources]` section is the only section without defaults, specifying both options here is mandatory.

    [sources]
    words = "/usr/share/dict/wamerican.txt"
    markov = [
      "/var/lib/iocaine/markov/bee-movie.txt",
      "/var/lib/iocaine/markov/moby-dick.txt"
    ]

The first option, `words`, refers to a word list file, with one word per line. When generating links, the path of the link will be a word chosen from this word list.

The second option, `markov`, is a list of files used to train the markov chain generator. These will be used to generate the main content. The files are read as-is, and, and the software works best if there’s a large amount of text to train on, text without any kind of markup. Giving it the complete works of Shakespeare, the script of the Bee Movie, or other larger bodies of text will yield good results. The text to train on need not be related in any way to the content `iocaine` is supposed to shadow.

# `[generator]`

The `[generator]` section is used to set an initial seed for the random number generator used when rendering garbage.

    [generator]
    initial_seed = ""

# `[templates]`

    [templates]
    # directory =

The `[templates].directory` property can be set to a directory containing custom templates. If not set (the default), `iocaine` will use its own default template. If configured, the directory must contain `main.jinja` and `challenge.jinja` files. See the templating documentation for more information about changing the templates.

## `[templates.minify]`

> Introduced in: iocaine 2.0.0

Starting with the iocaine 2.0.0, iocaine supports minifying its output. This is most useful with custom templates with CSS, as the savings can be substantial there.

    [templates.minify]
    enable = false
    minify_css = false

By default, minification is turned off. When enabled, only the HTML is minified, to minify any CSS, that minifications need to be opted into.

# `[metrics]`

`iocaine` can be configured to emit Prometheus-compatible metrics, on a separate port (configurable via the `[metrics].bind` setting). By default, metrics are turned off, they can be enabled with `[metrics].enable`. By default, the metrics do not include any labels, those can be configured via `[metrics].labels`, which is an array of string values. Valid values within the array are: `"Host"`, `"UserAgent"`, `"UserAgentGroup"`, and `"Verdict"`.

When enabled, metrics can be scraped from the `/metrics` endpoint of the configured bind address. For a detailed guide, and examples, please see the monitoring howto.

    [metrics]
    enable = false
    bind = "127.0.0.1:42042"
    #labels = [ "Host", "UserAgent", "UserAgentGroup", "Verdict" ]
    #persist-path = "/var/lib/iocaine/metrics.json"

> `persist-path` introduced in: iocaine 2.4.0

When the `"UserAgentGroup"` label is enabled, `iocaine` will try to map user agents to specific groups, configurable via `[[metrics.agent_group]]`, as detailed below. Each member of the array has an `agent` and a `group` field, where `agent` is a regexp, and the `group` is the string that will be added as the `user_agent_group` label, if the `agent` regexp matched. Matching is done in the same order as these groups are listed in the configuration, and will stop at the first match.

To keep the number of metrics balooning, it is advised to use either a single `"Host"` label, or a combination of `"Host"` and `"UserAgentGroup"`, with a catch-all entry at the end of `[[metrics.agent_group]]`, to limit the number of combinations.

    [[metrics.agent_group]]
    agent = "(?i:ClaudeBot)"
    group = "ClaudeBot"

    [[metrics.agent_group]]
    agent = "."
    group = "Other"

The `"Verdict"` label is only available in iocaine 2.2.0 or later.

When a `persist-path` is set, `iocaine` will save its metrics to the file specified therein when shutting down, and will attempt to reload them on startup, preserving all compatible counters. This is disabled by default.