1
0
Fork 0
2 File parsers
Damien FLETY edited this page 2026-04-12 20:47:28 +00:00

📝 File Parsers Guide

When you create eggs, you often need to update configuration files automatically. Wings uses parsers to understand different file formats. This guide teaches you how to use them! 🎯

What Are Parsers?

A parser is like a translator. It understands different file formats and can find and replace values in them.

Think of it this way:

  • 📋 JSON — Structured data with braces and quotes
  • 🔹 YAML — Configuration files with indentation
  • 🏷️ Properties — Java-style key=value files
  • ⚙️ INI — Settings with [section] blocks
  • 📄 File — Any plain text file, replaced line by line

Wings can edit all of these automatically!

The Five Main Parsers

1. JSON Parser 📦

Used for: Files with .json extension (like config.json)

What it looks like:

{
  "server": {
    "port": 8080,
    "name": "My Server"
  },
  "features": {
    "enabled": true
  }
}

How to use it in your egg:

{
  "config.json": {
    "parser": "json",
    "find": {
      "server.port": "{{server.build.default.port}}",
      "server.name": "{{server.build.env.SERVER_NAME}}"
    }
  }
}

How it works:

  • Uses dot notation: server.port finds the port key inside the server object
  • Nested objects: database.credentials.host goes three levels deep
  • Arrays: use numbers to access items — listen.0 means the first item in the listen array, listen.1 is the second, etc.

Real example from 5e-tools (array access):

{
  "caddy.json": {
    "parser": "json",
    "find": {
      "apps.http.servers.srv0.listen.0": ":{{server.build.default.port}}"
    }
  }
}

Real example from Corpbot (Discord bot):

{
  "config.json": {
    "parser": "json",
    "find": {
      "token": "{{server.build.env.TOKEN}}",
      "prefix": "{{server.build.env.PREFIX}}"
    }
  }
}

Common JSON config files:

  • config.json
  • settings.json
  • bot.json
  • caddy.json

2. YAML Parser 🔹

Used for: Files with .yml or .yaml extension

What it looks like:

server:
  port: 8080
  name: My Server
  ssl: true

database:
  host: localhost
  port: 5432
  credentials:
    user: admin
    password: secret

How to use it in your egg:

{
  "config.yml": {
    "parser": "yaml",
    "find": {
      "server.port": "{{server.build.default.port}}",
      "server.name": "{{server.build.env.SERVER_NAME}}",
      "database.credentials.user": "{{server.build.env.DB_USER}}"
    }
  }
}

How it works:

  • Uses dot notation like JSON: server.port drills into the nested structure
  • Very strict about spacing — YAML uses indentation (spaces, NOT tabs!) to show structure
  • Case-sensitive: hikari.sql.user is different from Hikari.SQL.User

Real example from Ree6 (Discord bot) — showing the full parser config:

{
  "config.yml": {
    "parser": "yaml",
    "find": {
      "bot.tokens.release": "{{server.build.env.BOT_TOKEN}}",
      "hikari.sql.user": "{{server.build.env.DATABASE_USER}}",
      "hikari.sql.db": "{{server.build.env.DATABASE_DB}}",
      "hikari.sql.pw": "{{server.build.env.DATABASE_PW}}",
      "hikari.sql.host": "{{server.build.env.DATABASE_HOST}}",
      "hikari.sql.port": "{{server.build.env.DATABASE_PORT}}",
      "spotify.client.id": "{{server.build.env.SPOTIFY_CLIENT_ID}}",
      "spotify.client.secret": "{{server.build.env.SPOTIFY_CLIENT_SECRET}}",
      "twitch.client.id": "{{server.build.env.TWITCH_CLIENT_ID}}",
      "twitch.client.secret": "{{server.build.env.TWITCH_CLIENT_SECRET}}",
      "openai.apiToken": "{{server.build.env.AI_TOKEN}}",
      "openai.apiUrl": "{{server.build.env.AI_URL}}",
      "openai.model": "{{server.build.env.AI_MODEL}}",
      "bot.misc.status": "{{server.build.env.MISC_STATUS}}"
    }
  }
}

Notice the dot notation carefully: hikari.sql.host navigates into the hikarisqlhost path in the YAML file. The key names must match the actual YAML structure exactly!

Common YAML config files:

  • config.yml
  • settings.yaml
  • Application-specific configs

3. Properties Parser 🏷️

Used for: Java-style key=value files

What it looks like:

server-port=25565
max-players=20
online-mode=true
difficulty=hard

How to use it in your egg:

{
  "server.properties": {
    "parser": "properties",
    "find": {
      "server-ip": "0.0.0.0",
      "server-port": "{{server.build.default.port}}",
      "query.port": "{{server.build.default.port}}"
    }
  }
}

🚨 How it works — this is the most important part!

  • The find key is the property name (e.g. server-port)
  • The find value is ONLY the new value — NOT the full key=value line
  • Wings handles writing key=value for you automatically

So if you write "server-port": "{{server.build.default.port}}", Wings finds the server-port line and rewrites it as server-port=25565 (or whatever port was assigned). You never write server-port= yourself — Wings does it!

Real example from Paper (Minecraft) egg:

{
  "server.properties": {
    "parser": "properties",
    "find": {
      "server-ip": "0.0.0.0",
      "server-port": "{{server.build.default.port}}",
      "query.port": "{{server.build.default.port}}"
    }
  }
}

Real example from PhantomBot (Twitch bot):

{
  "config/botlogin.txt": {
    "parser": "properties",
    "find": {
      "baseport": "{{server.build.default.port}}",
      "channel": "{{server.build.env.CHANNEL_NAME}}",
      "owner": "{{server.build.env.CHANNEL_OWNER}}",
      "paneluser": "{{server.build.env.WEBPANEL_USERNAME}}",
      "youtubekey": "{{server.build.env.YOUTUBE_API_KEY}}"
    }
  }
}

Common properties files:

  • server.properties (Minecraft)
  • system.properties
  • bot.properties
  • Custom key=value config files

4. File Parser 📄

Used for: Any plain text file where you want to replace an entire line

What a target file might look like:

bind-addr: 127.0.0.1:8080
auth: password
password: changeme
cert: false

How to use it in your egg:

{
  ".config/code-server/config.yaml": {
    "parser": "file",
    "find": {
      "password": "password: {{server.build.env.PASSWORD}}",
      "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
    }
  }
}

🚨 How it works — this is the most important part!

  • The find key is text that appears at the start of the line you want to replace
  • The find value is the entire replacement line — you write the complete key: value yourself
  • Wings finds any line starting with that text and replaces the whole line with exactly what you wrote

So "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}" tells Wings: "find any line starting with bind-addr and replace the whole line with bind-addr: 0.0.0.0:25565" (using whatever port Wings assigned).

Real example from Code Server:

{
  ".config/code-server/config.yaml": {
    "parser": "file",
    "find": {
      "password": "password: {{server.build.env.PASSWORD}}",
      "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
    }
  }
}

Important note: This is a .yaml file but it uses "parser": "file" — and that is completely correct! The parser name does NOT have to match the file extension. The file parser treats everything as plain text and replaces lines. You pick the parser that fits how you want to edit the file, not just what the extension says.

Common file parser use cases:

  • Config files where you need full control over the replacement line
  • YAML-ish files where line-by-line replacement is simpler than full YAML parsing
  • Any text file with an unusual key: value format

5. INI Parser ⚙️

Used for: INI files with [section] blocks (like .my.cnf for MySQL/MariaDB)

What it looks like:

[mysqld]
port = 3306
bind-address = 0.0.0.0
max_connections = 100

[client]
user = admin
port = 3306
socket = /run/mysqld/mysqld.sock

How to use it in your egg:

{
  ".my.cnf": {
    "parser": "ini",
    "find": {
      "mysqld.port": "{{server.build.default.port}}",
      "mysqld.bind-address": "0.0.0.0",
      "client.port": "{{server.build.default.port}}",
      "client.user": "container"
    }
  }
}

How it works:

  • Uses section.key dot notation: mysqld.port targets the port key inside the [mysqld] section
  • Each section is independent — mysqld.port and client.port are two different keys even though they share the name port

Real example from MariaDB egg:

{
  ".my.cnf": {
    "parser": "ini",
    "find": {
      "mysqld.port": "{{server.build.default.port}}",
      "client.port": "{{server.build.default.port}}",
      "mysqld.bind-address": "0.0.0.0",
      "mysqld.pid-file": "/home/container/run/mysqld/mysqld.pid",
      "mysqld.socket": "/home/container/run/mysqld/mysqld.sock",
      "client.socket": "/home/container/run/mysqld/mysqld.sock",
      "client.user": "container"
    }
  }
}

Common INI files:

  • .my.cnf (MySQL/MariaDB)
  • .ini Windows-style config files
  • Application settings with [section] structure

Properties vs File Parser — What's the Difference? 🤔

These two are easy to mix up! Here's the key difference side by side:

properties parser file parser
find key The property name (server-port) Text at the start of the line (bind-addr)
find value ONLY the new value (25565) The ENTIRE replacement line (bind-addr: 0.0.0.0:25565)
Wings writes key=YOUR_VALUE for you Exactly what you put as the value
Best for Clean key=value files like .properties Files where you need full control of the output line

Properties parser example:

{
  "parser": "properties",
  "find": {
    "server-port": "{{server.build.default.port}}"
  }
}

Wings finds server-port=25565 and rewrites it as server-port=NEW_PORT. You supply only the new value.

File parser example:

{
  "parser": "file",
  "find": {
    "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
  }
}

Wings finds any line starting with bind-addr and replaces the WHOLE line with bind-addr: 0.0.0.0:NEW_PORT. You write the full replacement line yourself.

Choosing the Right Parser

Here's a quick reference:

File Type Extension Parser Notes
JSON .json json Dot notation for nested keys
YAML .yml, .yaml yaml Dot notation for nested keys
Properties .properties, .txt properties find value is ONLY the new value
INI .ini, .cnf ini section.key notation
Plain Text any file find value is the ENTIRE replacement line

Quick decision tree:

Does your file have {...}?         → Use json
Does your file use indentation?    → Use yaml
Does your file have [sections]?    → Use ini
Is it simple key=value?            → Use properties
Need full line replacement?        → Use file

Remember: the file parser works on any text file, even .yaml ones, if you prefer simple line replacement!

Practical Examples

Example 1: Minecraft Server

Minecraft's server.properties is a Java properties file, so use the properties parser:

# server.properties
server-port=25565
max-players=20
online-mode=true
difficulty=hard
gamemode=survival

In our egg:

{
  "server.properties": {
    "parser": "properties",
    "find": {
      "server-ip": "0.0.0.0",
      "server-port": "{{server.build.default.port}}",
      "query.port": "{{server.build.default.port}}",
      "max-players": "{{server.build.env.MAX_PLAYERS}}"
    }
  }
}

The values in find are just the new values — {{server.build.default.port}} becomes 25565 (or whatever Wings assigned). Wings writes server-port=25565 automatically. You do NOT include the = sign in the key!

Example 2: Discord Bot

Discord bots often use JSON config files:

Option 1: JSON config

{
  "config.json": {
    "parser": "json",
    "find": {
      "token": "{{server.build.env.DISCORD_TOKEN}}",
      "prefix": "{{server.build.env.BOT_PREFIX}}",
      "owner_id": "{{server.build.env.OWNER_ID}}"
    }
  }
}

Option 2: Properties-format config file

{
  "config.properties": {
    "parser": "properties",
    "find": {
      "DISCORD_TOKEN": "{{server.build.env.DISCORD_TOKEN}}",
      "BOT_PREFIX": "{{server.build.env.BOT_PREFIX}}"
    }
  }
}

With the properties parser, the find value is only the new value — not the full KEY=VALUE line. Wings will write DISCORD_TOKEN=your_token_here for you.

Example 3: Database Configuration

MySQL/MariaDB uses INI format:

[mysqld]
port=3306
bind-address=0.0.0.0
max_connections=100

[client]
user=admin
password=secret

In our egg:

{
  ".my.cnf": {
    "parser": "ini",
    "find": {
      "mysqld.port": "{{server.build.default.port}}",
      "mysqld.bind-address": "0.0.0.0",
      "client.user": "{{server.build.env.DB_USER}}",
      "client.password": "{{server.build.env.DB_PASSWORD}}"
    }
  }
}

Example 4: Code Server (File Parser on a YAML file)

Code Server stores its config as YAML, but we use the file parser for simple line replacement:

bind-addr: 127.0.0.1:8080
auth: password
password: changeme
cert: false

In our egg:

{
  ".config/code-server/config.yaml": {
    "parser": "file",
    "find": {
      "password": "password: {{server.build.env.PASSWORD}}",
      "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
    }
  }
}

Why file instead of yaml? Because we want simple, direct line replacement and full control over what the output line looks like. The parser choice is based on how we want to edit the file, not just its extension.

Advanced: Managing Multiple Files

You can manage multiple files in one egg, each with its own parser:

{
  "config": {
    "files": {
      "config.json": {
        "parser": "json",
        "find": {
          "server.port": "{{server.build.default.port}}"
        }
      },
      "config/database.yaml": {
        "parser": "yaml",
        "find": {
          "database.host": "{{server.build.env.DB_HOST}}"
        }
      },
      "server.properties": {
        "parser": "properties",
        "find": {
          "server-port": "{{server.build.default.port}}"
        }
      }
    }
  }
}

This egg manages three different configuration files with three different parsers — all at once!

Tips and Tricks 💡

Tip 1: Escape Special Characters

When your value contains quotes or backslashes, escape them:

{
  "find": {
    "server.motd": "Welcome to \"My Server\"!"
  }
}

Tip 2: Be Precise with Search Strings for the File Parser

When using "parser": "file", your search key must match the exact start of the line:

{
  "parser": "file",
  "find": {
    "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
  }
}

This matches any line starting with bind-addr. If the line is commented out (starts with # bind-addr), it won't match.

Tip 3: Use Dot Notation Correctly for JSON/YAML

For JSON and YAML, dot notation navigates the nested structure. Follow the exact nesting in the file:

{
  "parser": "yaml",
  "find": {
    "hikari.sql.user": "{{server.build.env.DATABASE_USER}}"
  }
}

This navigates: hikarisqluser. If your file has database.host instead of hikari.sql.host, use database.host!

Tip 4: Test Your Config Before Releasing

Always test with sample values before sharing your egg:

  1. Create a fresh server with your egg
  2. Check the config files look right after startup
  3. Try edge cases — empty values, special characters in passwords

Tip 5: Create Default Config Files During Installation

Wings can only modify files that already exist. Create them during installation:

#!/bin/bash
# Create a default config for Wings to modify at startup
cat > /home/container/config.json << 'EOF'
{
  "server": {
    "port": 8080,
    "name": "Default Server"
  }
}
EOF

Then Wings modifies it every time the server starts!

Common Mistakes

Mistake 1: Properties Parser — Putting the Full Line in the Value

{
  "parser": "properties",
  "find": {
    "server-port": "server-port={{server.build.default.port}}"
  }
}

Wrong! With properties parser, the value should be ONLY the new value:

{
  "parser": "properties",
  "find": {
    "server-port": "{{server.build.default.port}}"
  }
}

Wings writes server-port=25565 for you — you just supply 25565!

Mistake 2: File Parser — Forgetting to Write the Full Line

{
  "parser": "file",
  "find": {
    "bind-addr": "{{server.build.default.port}}"
  }
}

Wrong! With file parser, the value must be the ENTIRE replacement line:

{
  "parser": "file",
  "find": {
    "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
  }
}

Wings replaces the whole line with exactly what you wrote!

Mistake 3: Using Minecraft server.properties With the File Parser

{
  "server.properties": {
    "parser": "file",
    "find": {
      "server-port=": "server-port={{server.build.default.port}}"
    }
  }
}

This works technically, but it's more fragile than necessary. Use the properties parser instead:

{
  "server.properties": {
    "parser": "properties",
    "find": {
      "server-port": "{{server.build.default.port}}"
    }
  }
}

Cleaner and more correct for Java .properties files!

Mistake 4: Wrong Dot Notation Path for YAML

{
  "parser": "yaml",
  "find": {
    "database.host": "{{server.build.env.DATABASE_HOST}}"
  }
}

Wrong if the actual YAML key path is hikari.sql.host! The dot notation must match the real structure in the file:

{
  "parser": "yaml",
  "find": {
    "hikari.sql.host": "{{server.build.env.DATABASE_HOST}}"
  }
}

Always check the actual YAML structure. Case matters too!

Mistake 5: Forgetting to Create the Config File During Installation

Wings can only modify files that exist. If the file hasn't been created during installation, the parser silently does nothing. Always create your config files in the install script!

Troubleshooting 🔧

Problem: Variables Not Being Substituted

Symptoms: File shows {{server.build.env.TOKEN}} instead of the actual token

Solutions:

  1. Check the variable name is spelled exactly right (case-sensitive!)
  2. Make sure the variable is defined in your variables section
  3. Check the user actually entered a value in the panel
  4. For JSON/YAML, verify the dot notation path matches the real file structure

Problem: File Not Being Modified

Symptoms: Your configuration file isn't changing at all

Solutions:

  1. Make sure the file actually exists (did your install script create it?)
  2. For file parser: check your search key exactly matches the start of a line
  3. For properties parser: make sure the key name (without =) matches exactly
  4. For yaml/json parser: validate the dot notation path

Problem: Syntax Error in Modified File

Symptoms: Server won't start because the config file has syntax errors

Solutions:

  1. For properties parser: double-check you're not putting key=value in the value — just the value!
  2. For file parser: make sure your replacement line is well-formed
  3. For JSON/YAML: check that special characters in values don't break the format

Problem: Right File, Wrong Parser Results

Symptoms: Some keys change but others don't, or the format looks garbled

Solution: Double-check the parser choice:

  • server.propertiesproperties parser (keys without =)
  • config.jsonjson parser
  • config.ymlyaml parser (or file if you prefer line replacement)
  • .my.cnfini parser

Checklist Before Using Parsers

  • Chosen the correct parser for how you want to edit the file
  • For properties parser: find value is ONLY the new value (no key= prefix!)
  • For file parser: find value is the ENTIRE replacement line
  • Search string / key is exactly correct (case-sensitive!)
  • File exists before Wings tries to modify it (created in install script)
  • File format is valid before Wings modifies it
  • Special characters are properly escaped
  • Variables are defined in your egg
  • Tested with sample values

Real Examples from Real Eggs

Example: Paper (Minecraft)

{
  "server.properties": {
    "parser": "properties",
    "find": {
      "server-ip": "0.0.0.0",
      "server-port": "{{server.build.default.port}}",
      "query.port": "{{server.build.default.port}}"
    }
  }
}

Example: Code Server

{
  ".config/code-server/config.yaml": {
    "parser": "file",
    "find": {
      "password": "password: {{server.build.env.PASSWORD}}",
      "bind-addr": "bind-addr: 0.0.0.0:{{server.build.default.port}}"
    }
  }
}

Example: 5e Tools

{
  "caddy.json": {
    "parser": "json",
    "find": {
      "apps.http.servers.srv0.listen.0": ":{{server.build.default.port}}"
    }
  }
}

Example: Ree6 Bot (partial — shows correct dot notation)

{
  "config.yml": {
    "parser": "yaml",
    "find": {
      "bot.tokens.release": "{{server.build.env.BOT_TOKEN}}",
      "hikari.sql.host": "{{server.build.env.DATABASE_HOST}}",
      "hikari.sql.user": "{{server.build.env.DATABASE_USER}}",
      "hikari.sql.pw": "{{server.build.env.DATABASE_PW}}",
      "hikari.sql.port": "{{server.build.env.DATABASE_PORT}}"
    }
  }
}

Note: hikari.sql.host — NOT database.host! The dot notation must match the actual YAML structure of the Ree6 config file.

Example: MariaDB

{
  ".my.cnf": {
    "parser": "ini",
    "find": {
      "mysqld.port": "{{server.build.default.port}}",
      "client.port": "{{server.build.default.port}}",
      "mysqld.bind-address": "0.0.0.0",
      "client.user": "container"
    }
  }
}

See Also 📚

Summary 📝

Five main parsers:

  1. JSON — For .json files; dot notation for nested keys
  2. YAML — For .yml files; dot notation for nested keys
  3. Properties — For key=value files; the find value is ONLY the new value
  4. INI — For [section] files; use section.key dot notation
  5. File — For any text file; the find value is the ENTIRE replacement line

The single most important rule:

  • properties parser → you provide the new value only, Wings writes key=value
  • file parser → you provide the complete replacement line, Wings uses it verbatim

Now you can manage any configuration file! 🎉

Learning Path 📚

You should read the documentation in this order:

  1. Egg Basics — What are eggs and basic structure
  2. Configuration Variables — How to make your egg customizable
  3. Extending Eggs — Creating and extending eggs
  4. Wings Variables — Special variables from the panel
  5. File Parsers — This page! Managing configuration files

Happy parsing! 🥚