JSON configuration and adapt command

This page covers the ferron adapt command, the JSON configuration format, and how to work with JSON-based configurations. Configuration file parsing is handled by the config-ferronconf module (for .conf files) or the config-json module (for .json files).

The adapt command

The adapt command converts .conf configuration files into their JSON representation. This is useful for debugging, programmatic configuration generation, or understanding how Ferron parses your configuration.

ferron adapt -c ferron.conf

This reads your .conf file and outputs the complete parsed configuration as JSON to standard output.

Configuration example:

Given a ferron.conf file:

{
  runtime {
    io_uring true
  }
}

*:8080 {
  root /var/www/html
}

Running ferron adapt -c ferron.conf outputs:

{
  "global_config": {
    "directives": {
      "runtime": [
        {
          "args": [],
          "children": {
            "directives": {
              "io_uring": [
                {
                  "args": [
                    {
                      "Boolean": [
                        true,
                        {
                          "line": 3,
                          "column": 14,
                          "file": "/path/to/ferron.conf"
                        }
                      ]
                    }
                  ],
                  "children": null,
                  "span": {
                    "line": 3,
                    "column": 5,
                    "file": "/path/to/ferron.conf"
                  }
                }
              ]
            },
            "matchers": {},
            "span": {
              "line": 2,
              "column": 11,
              "file": "/path/to/ferron.conf"
            }
          },
          "span": {
            "line": 2,
            "column": 3,
            "file": "/path/to/ferron.conf"
          }
        }
      ]
    },
    "matchers": {},
    "span": {
      "line": 1,
      "column": 1,
      "file": "/path/to/ferron.conf"
    }
  },
  "ports": {
    "http": [
      {
        "port": 8080,
        "hosts": [
          [
            {
              "ip": null,
              "host": null
            },
            {
              "directives": {
                "root": [
                  {
                    "args": [
                      {
                        "String": [
                          "/var/www/html",
                          {
                            "line": 8,
                            "column": 8,
                            "file": "/path/to/ferron.conf"
                          }
                        ]
                      }
                    ],
                    "children": null,
                    "span": {
                      "line": 8,
                      "column": 3,
                      "file": "/path/to/ferron.conf"
                    }
                  }
                ]
              },
              "matchers": {},
              "span": {
                "line": 7,
                "column": 8,
                "file": "/path/to/ferron.conf"
              }
            }
          ]
        ]
      }
    ]
  }
}

JSON configuration structure

The JSON configuration follows a hierarchical structure that mirrors Ferron’s internal configuration model.

Root object

The top-level configuration contains two main sections:

FieldTypeDescription
global_configServerConfigurationBlockGlobal configuration applying to all protocols
portsBTreeMap<String, Vec<ServerConfigurationPort>>Per-protocol port configurations, keyed by protocol name (e.g., “http”, “https”, “tcp”)

Configuration blocks

A ServerConfigurationBlock represents a scope of directives:

FieldTypeDescription
directivesHashMap<String, Vec<ServerConfigurationDirectiveEntry>>All directives in this block, indexed by name
matchersHashMap<String, ServerConfigurationMatcher>Named matcher expressions for conditional directives
spanServerConfigurationSpan | nullSource location of this block

Blocks appear at multiple levels:

  • Global configuration — server-wide settings
  • Port/host configuration — protocol and host-specific settings
  • Nested directives — child blocks within directive entries (e.g., runtime { io_uring true })

Directive entries

Each directive entry represents one occurrence of a directive:

FieldTypeDescription
argsVec<ServerConfigurationValue>Arguments provided to this directive
childrenServerConfigurationBlock | nullOptional nested configuration block
spanServerConfigurationSpan | nullSource location of this directive

Multiple entries with the same name can exist in a single block, allowing for repeated directives.

Configuration values

ServerConfigurationValue is a tagged union representing different value types:

VariantJSON structureExample
String["String", [value, span]]["String", ["/var/www/html", {"line": 8, "column": 8, "file": "ferron.conf"}]]
Number["Number", [value, span]]["Number", [8080, null]]
Float["Float", [value, span]]["Float", [3.14, null]]
Boolean["Boolean", [value, span]]["Boolean", [true, {"line": 3, "column": 14, "file": "ferron.conf"}]]
InterpolatedString["InterpolatedString", [parts, span]]See interpolated strings section below

Span information is optional and can be null.

Interpolated strings

Interpolated strings use {{name}} syntax and are represented as an array of parts:

Part typeJSON structureDescription
String["String", literal_text]Literal text content
Variable["Variable", var_name]Variable reference to be resolved

Variables are resolved at runtime:

  • env.NAME — resolved from environment variables
  • NAME — resolved from the consumer’s variable map

If a variable cannot be resolved, the placeholder is kept as {{NAME}} in the output.

Configuration example:

{
  "InterpolatedString": [
    [
      ["String", "/certs/"],
      ["Variable", "env.DOMAIN"],
      ["String", ".crt"]
    ],
    {
      "line": 15,
      "column": 10,
      "file": "ferron.conf"
    }
  ]
}

Port configurations

Each entry in the ports map represents a protocol:

FieldTypeDescription
portu16 | nullPort number (may be inherited from protocol defaults)
hostsVec<(ServerConfigurationHostFilters, ServerConfigurationBlock)>Host configurations with filters

Host filters

ServerConfigurationHostFilters controls which host/IP a port configuration applies to:

FieldTypeDescription
ipIpAddr | nullIP address to match (for multi-homed servers)
hostString | nullHost/domain name to match (for SNI)

When both are null, the configuration applies to all hosts on that port (e.g., *:8080).

Match expressions

Named matchers contain expressions for conditional configuration:

FieldTypeDescription
exprsVec<ServerConfigurationMatcherExpr>List of expressions to evaluate
spanServerConfigurationSpan | nullSource location

Each expression has three components:

FieldTypeDescription
leftServerConfigurationMatcherOperandLeft operand
rightServerConfigurationMatcherOperandRight operand
opServerConfigurationMatcherOperatorComparison operator

Operands can be:

VariantJSON structureExample
Identifier["Identifier", name]["Identifier", "request.method"]
String["String", value]["String", "GET"]
Integer["Integer", value]["Integer", 8080]
Float["Float", value]["Float", 3.14]

Supported operators:

OperatorJSON valueMeaning
==["Eq"]String equality
!=["NotEq"]String inequality
~["Regex"]Regex match
!~["NotRegex"]Regex non-match
in["In"]Membership check

Span metadata

ServerConfigurationSpan tracks source locations for error reporting:

FieldTypeDescription
lineusizeLine number (1-indexed)
columnusizeColumn number (1-indexed)
fileString | nullSource file path

Span information is preserved in JSON output to provide accurate error messages during validation and runtime.

Configuration adapters

Ferron uses a pluggable adapter system for loading configuration from different sources.

Built-in adapters

AdapterFile extensionsDescription
config-ferronconf.confParses Ferron’s custom configuration syntax
config-json.jsonLoads JSON configuration directly

Selecting an adapter

The adapter is auto-detected based on file extension. You can override it explicitly:

ferron run -c config.json --config-adapter json
ferron validate -c myconfig.conf --config-adapter ferronconf

Adapter interface

Adapters implement the ConfigurationAdapter trait, which defines:

  • adapt(params) — load and parse configuration, returning a ServerConfiguration and a ConfigurationWatcher
  • file_extension() — list of file extensions this adapter handles

The ConfigurationWatcher monitors the configuration source for changes and triggers hot-reload when the source is updated.

Configuration example:

{
  "global_config": {
    "directives": {
      "runtime": [
        {
          "args": [],
          "children": {
            "directives": {
              "io_uring": [
                {
                  "args": [
                    {
                      "Boolean": [true, null]
                    }
                  ],
                  "children": null,
                  "span": null
                }
              ]
            },
            "matchers": {},
            "span": null
          },
          "span": null
        }
      ]
    },
    "matchers": {},
    "span": null
  },
  "ports": {
    "http": [
      {
        "port": 8080,
        "hosts": [
          [
            {
              "ip": null,
              "host": null
            },
            {
              "directives": {
                "root": [
                  {
                    "args": [
                      {
                        "String": ["/var/www/html", null]
                      }
                    ],
                    "children": null,
                    "span": null
                  }
                ]
              },
              "matchers": {},
              "span": null
            }
          ]
        ]
      }
    ]
  }
}

Working with JSON configurations

JSON configurations are typically used in the following scenarios:

  • Programmatic generation — tools and scripts can generate configuration without parsing Ferron’s custom syntax
  • API integration — external systems can push configuration as JSON
  • Debugging — inspect how Ferron parsed your .conf file
  • Testing — precise control over configuration structure in automated tests

Loading JSON configurations

You can load JSON configurations directly:

ferron run -c config.json
ferron validate -c config.json

The adapter is auto-detected from the .json extension.

Hot-reload support

JSON configuration files support hot-reload. When the file changes, Ferron detects the update and reloads the configuration gracefully. The ConfigurationWatcher monitors the file for modifications.

To enable hot reloading, specify a watch configuration adapter parameter:

ferron run --config-params 'watch=1;file=ferron.json' --config-adapter json

Notes and troubleshooting

  • The JSON output from ferron adapt is a faithful representation of the parsed configuration, including all span metadata for error reporting.
  • Boolean directives can be represented as "args": [] (flag-style, treated as true) or "args": [{"Boolean": [true, null]}] (explicit boolean).
  • When spans are null, the configuration was likely constructed programmatically rather than parsed from a file.
  • The ports map is organized by protocol name (e.g., “http”, “https”, “tcp”). Each protocol can have multiple ports.
  • Host configurations are stored as tuples of (filters, block). The filters control which hosts the configuration applies to.
  • For the .conf file format and syntax details, see Syntax and file structure.
  • For conditional matchers and variables, see Conditionals and variables.
  • For how configuration is processed at runtime, see Core directives.