JSON configuration and adapt command

This page covers the ferron adapt command, the JSON configuration format, and how to work with JSON-based configurations. The config-ferronconf module parses .conf files. The config-json module parses .json files.

Info

For configuration format details, see Syntax and file structure, Conditionals and variables, and Core directives.

The adapt command⁠#

The adapt command converts .conf configuration files to JSON. 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
    }
}

*: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"
              }
            }
          ]
        ]
      }
    ]
  }
}
Note

The ferron adapt command produces JSON that faithfully represents the parsed configuration. You can represent Boolean directives as "args": [] (flag-style, treated as true) or with explicit booleans. When spans are null, the system likely constructed the configuration programmatically.

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 (for example, “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 | nullWhere this block is

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 (for example, runtime { io_uring })

Directive entries⁠#

Each directive entry represents one directive:

FieldTypeDescription
argsVec<ServerConfigurationValue>Arguments provided to this directive
childrenServerConfigurationBlock | nullOptional nested configuration block
spanServerConfigurationSpan | nullWhere this directive is

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

Configuration values⁠#

ServerConfigurationValue uses a tagged union to represent 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 can be null.

Interpolated strings⁠#

Interpolated strings use {{name}} syntax. The system represents them as an array of parts:

Part typeJSON structureDescription
String["String", literal_text]Literal text content
Variable["Variable", var_name]Variable reference that the system resolves

Variables resolve at runtime:

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

If the system cannot resolve a variable, the placeholder stays 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 (the protocol may provide a default)
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 (for example, *: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

The system preserves span information 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⁠#

Ferron detects the adapter 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): loads and parses configuration, returning a ServerConfiguration and a ConfigurationWatcher
  • file_extension(): lists file extensions this adapter handles

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

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

Ferron detects the adapter 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

Configuration drift hints⁠#

By default, hot-reload is off. Ferron can still detect when the JSON configuration file changes on disk but you have not reloaded it. Drift hints are on by default. Disable with drift_hints=false:

ferron run --config-params 'drift_hints=false;file=ferron.json' --config-adapter json

See Configuration drift hints for details.

See also⁠#