Hannes Braun


Why scfg is pretty neat

January 4, 2024

A few months ago, I found out about scfg, a simple configuration file format initially developed by Simon Ser. I think it’s pretty neat, and I’d like to give you a quick overview here. Keep in mind that the format is not final as of writing this (January 2024), however.

First of all, what is a configuration file format? What does it need to support? A configuration file typically holds static data. It’s not a program or executable in any way. Of course, exceptions apply. Spark, for example, a framework that I’m currently helping to maintain, uses Ruby scripts as configuration files. But that’s another story. Let’s focus on the typical use cases.

In order for a configuration file format to be easy to use, it needs to support storing data in a hierarchical way. While the INI file format is extremely simple, it lacks this ability. Of course, you can “solve” this with various workarounds, like using a dot notation for the section names. But that’s rather confusing and not quite intuitive to read.

Therefore, a proper solution would have to support lists of values and dictionaries in addition to primitive values. And of course, there are popular configuration file formats such as JSON, YAML, or TOML that support this. Why not just choose one of them?

JSON is not convenient to write. For an array of dictionaries, you need two levels of indentation, a lot of brackets, and usually a lot of lines with only one brace when the file is formatted properly. Don’t get me wrong: for tasks where you don’t need to write JSON by hand (like transmitting data structures over HTTP), this is absolutely fine. You can read it if you need to without a substantial effort and maybe even modify it. And most of the time, you don’t need to worry about it, and everything’s fine. But it’s not what I would want to edit by hand on a regular basis.

Next option: YAML. This is certainly easier to write and read. But have you read the spec (currently, that is version 1.2.0)? You can even use JSON syntax inside a YAML file. So although YAML syntax is quite good in most cases, the format as a whole is too complex for my taste.

And then there’s TOML. I feel like it’s a mixture of the formats I mentioned previously. Using key-value pairs is pretty straight-forward. But then storing something like an array of dictionaries (or tables, as it is called in TOML) feels pretty much like writing an INI file again. The only difference is that this is part of the spec. I don’t really know what I should think about this format. As of now, I don’t hate it, but I don’t really like it either. Meh.

So back to the topic of this post: scfg. Instead of trying to enforce a certain syntax for common data structures, I feel like you’re quite free in how you would like to map your internal data structures onto the configuration file. But it’s usually quite obvious how you’d map something.

Let’s take a look at how it works. The only kind of primitive data type that you have is a string. This makes sense, as that is what you’re working with in the actual configuration file, no matter what you’re trying to represent. If you’d like to store an integer, a date, or something else, feel free to do so. Either the scfg library of your choice or you will take care of converting the string. This is usually not a big deal and keeps the specification small. There are also just two other kinds of data structures (not counting the lists that are part of them). One of them is a directive. A directive has a name (string), a list of parameters (strings) that may as well be empty, and an optional block. A block contains a list of directives, which may be empty as well. If the block is empty, its parentheses can also be omitted.

Let’s see a quick example:

server_url "https://example.com"

users {
    john 123456
    mark doNOTstorepasswordsinplaintext
}
permitted_http_versions HTTP/1.1 HTTP/2 HTTP/3

modules {
    basic_auth
    # tls <version> <private key> <cert>
    tls 1.3 /certs/privkey.pem /certs/fullchain.pem
    health
}

This is a fictional example of the configuration for some kind of web service. Storing simple key-value pairs is easy, as you can see with server_url. A dictionary is also quite trivial. Omit the parameters and use a block as I did with users. If you want to have a list, one option is to use the parameters of a directive (see permitted_http_versions). Another option is to use a block, with its directives being the list entries. This is what I did with modules. This adds the possibility of adding more parameters to a list entry if required. While the module basic_auth does not need any more configuration, tls receives three parameters here. The line starting with a “#” is a comment.

I think this should give you a good overview of what scfg can do. It usually solves all my needs for a simple configuration file, and I’m quite happy with it.

PS: I even wrote a scfg library for Java called scfg4j (what a creative name). The logic of the parser is pretty much ported from the Go implementation. I have no plans to use the library so far. As such, I probably won’t spend time on this library for now. But if some of you want to have a tagged release and want to have it available on something like Maven Central, feel free to send me a message. For now, it’s just available for… no reason/educational purposes?