
Page generated from: SET_File_Core_Spec_v4_3.md
Version 4.3 - Core Implementation
Updated: January 2026
This document contains the complete core specification (Sections 1-3).
For simplified defaults-only implementation, see the Q-Set Specification.
For advanced usage patterns and community contributions, visit setfiles.org/advanced
Introduction & Philosophy
SET files (.set or .qset file extension) are machine-readable and human-readable data files; designed for storing settings & configuration data, as well as structured data. They are intended more for use internal to a program or environment and less for broadly sharing data, with that in mind they allow both considerable optimization and flexibility relative to other similar data storage formats. The SET file format really shines in smaller files with multiple data structure that are mostly in string format. Which might sound limiting but in fact covers the majority of the uses for data storage in small to medium sized files, typically under around 10,000 lines. The format optimizes parsing by eliminating escaping and data-typing (allowed but not needed for strings.) More importantly, SET files allow distinctly structured Groups of data, comments, and larger blocks of text or even binary data to be included in a single file.
Most notable is how the format allows easy human readable recognition of groups by name, and content stored in a compact pipe-delimited format: RS232|9600|8|N|1|Off
A single file might include, author and date information, groups of key-value pairs, several different data-tables and even the full text of a license.
The design philosophy is a format flexible enough to allow the use of as much, or as little of the protocol as is needed. Allowing only a few lines of code to parse a read-only file, and locate relevant data. However, by having a straight forward framework, with a little creativity, SET files make handling even complex, and diverse data sets easy and natural.
With careful use of well designed parsing functions, it is arguably noticeably more efficient than JSON files. Not just in execution, but most especially during development and debugging.
Human First
SET files prioritize simplicity, flexibility, and human readability over complex data handling within a known program or environment; rather than for sharing unknown data between programs.
Simple Specification
The core rules can fit on a single page. To add more complex features there are deeper levels of the protocol but the really advanced features are optional extensions, not core requirements.
Flexible Implementation
Parse as much or as little as you need. Most users will select the parsing functions they want, adapt to their needs, and ignore the rest. A minimal parser can be written in ~50 lines of code. Advanced features are available when needed.
Convention Over Enforcement
The format enables patterns but doesn't mandate them. Implementations may choose their own conventions to match their environment and needs.
No Magic
Everything is explicit and visible. No hidden behaviors, no surprising type coercion, no implicit conversions.
Good fits:
Poor fits:
.set - Standard SET file (may use any features).qset - Using only minimal/simple implementation (conventionally uses defaults only)Note: The "q" in .qset denotes a simplified or "quick" implementation, but it still fully complies with the specification.
This specification defines the complete SET file format. Implementations may choose to support:
This section defines the minimum requirements for a SET file. Everything in this section is required for basic compliance.
SET file organization:
* Each Group MUST have a unique name within the file
* Names are written with any combination of Letters, Numbers, hyphen `-` and underscore `_` ONLY
* Typically but not required Group Names are in all caps
* a Group may contain diverse types of information: Key|Value, Table, Delimited String
* a Text Group may be used to store extensive text information (even multi-line)
* all the contents of a Text-Group are a single entity allowing multiple lines and unusual characters
* no escaping is needed inside a Text-Group, all content is "as-is"
* unlike other Groups Text-Groups do not end on empty lines.
* Text Group names may be use in a Line, as a Linked Reference - `License | [{LICENSE}]`
* a Line could be a single entry array. The parser might look for "line 3" of "\[CONFIG\]" - no delimiter
* a Line may be a Key|Value pair - one delimiter
* a Line may be a delimited array of data like a csv - but normally "|" pipe delimited
* a field in a line, may reference a Text Group in the same SET File
* spaces before/after delimiters may provide readability but the parser must account for its use or not as needed
* a field within a line may itself be an array of data (nested arrays), using the secondary delimiter (default=`!`)
* an empty line is considered an End-of-Group marker and is equivalent to `[EOG]`
* Any comment immediately preceding a Group is assumed to be related to that group
Typically a SET file consists of:
Example:
myconfig.set
This file contains application configuration.
Created: 2026-01-07
[DATABASE]
Host|localhost
Port|5432
[APP_SETTINGS]
Theme|dark
Language|en-US
[PROTOCOL]
RS232|9600|8|N|1|none
It can often be convenient to include settings information inside of some other document or file, particularly in file formats that allow comments, such as Markdown or program files (PHP, Rust, JavaScript, Python, etc.).
When embedding a SET file,
{ and the word SETTAG: (in all caps, ending with a colon)} and a new line{/SETTAG/} a space and the closing comment markupExample (HTML/Markdown):
<!-- {SETTAG:This_Tag_Name}
what follows is the SET file content all the way to
an ending marker
{/SETTAG/} -->
or
// {SETTAG:This_Tag_Name}
// what follows is the SET file content all the way to
// an ending marker
// {/SETTAG/}
Note: The same pattern applies in other languages using their respective comment syntax:
// {SETTAG:ConfigInfo} ... {/SETTAG/}# {SETTAG:ConfigInfo} ... {/SETTAG/}// {SETTAG:ConfigInfo} ... {/SETTAG/}/ {SETTAG:ConfigInfo} ... {/SETTAG/} /Groups are the fundamental data containers in SET files.
Syntax: [GROUPNAME]
Naming Rules:
Examples:
[DATABASE] ✓[App_Settings] ✓[USER-LIST] ✓[Config 2] ✗ (contains space)[My.Config] ✗ (contains period)Regular groups contain delimited data with positional fields - similar to CSV but more readable and less problematic.
Syntax: [GROUPNAME]
Structure:
[GROUPNAME]
{field1|field2|field3} ← Optional field definition
value1|value2|value3 ← Data records
value1|value2|value3
[EOG] ← Optional end marker
Key-Value Pairs:
[SETTINGS]
Key|Value
AnotherKey|Another Value
Mixed Line Arrays
[SETTINGS]
Owner|Kirk
Model|THX1138
Protocol|RS232|9600|8|None|1
Positional Fields (Tables):
[USERS]
{id|name|email}
1|Alice|alice@example.com
2|Bob|bob@example.com
Example with complete table:
[EMPLOYEES]
{id|first_name|last_name|department|hire_date}
101|Alice|Smith|Engineering|2023-01-15
102|Bob|Jones|Marketing|2023-02-20
103|Carol|White|Engineering|2023-03-10
[EOG]
Rules:
value1||value3Field Order: Field order is significant. If field names are used, each line of that Group must match that order.
Nested Arrays:
[PRODUCTS]
{id|name|colors|sizes}
1|T-Shirt|Red!Blue!Green|S!M!L!XL
2|Jeans|Blue!Black|28!30!32!34
In this example, the pipe | delimits the main fields, while the exclamation point ! delimits items within the nested arrays (colors and sizes). Lines in a SET file may end with a single LF character or a LF and a CR (line-feed and carriage return) Group contents are always separated by the defined delimiter, typically a pipe - "|" Lines should not start with or end with a delimiter unless the last field is empty
The same [GROUPNAME] syntax works when used for key-value pairs instead of positional fields.
Structure:
[GROUPNAME]
Key1|Value1
Key2|Value2
Key3|Value3
[EOG]
Example:
[DATABASE_CONFIG]
Host|localhost
Port|5432
Database|myapp
Username|admin
Password|secret123
MaxConnections|100
Timeout|30
[EOG]
Key Naming Conventions:
* Letters, numbers, hyphens, underscores only
* No spaces
* Case-sensitive
Value Conventions:
| is a valueKey|Key|[{TEXTBLOCK}]Distinguishing Key-Value from Positional data: There is no syntactic difference. The distinction is semantic:
{field|names} definition → positional fieldsGroups end when:
[EOG] marker is present[EOG])Examples:
[SETTINGS]
Key|Value
[ANOTHER_GROUP]
[SETTINGS]
Key|Value
[EOG]
[ANOTHER_GROUP]
Both examples are equivalent. The [EOG] marker is optional but recommended for clarity.
Text blocks store raw, unprocessed multi-line content. All content beginning the line after the \[{GROUPNAME}\] all the way to the \[EOG\] is stored as exact text (including non-printing characters).
Syntax: [{GROUPNAME}]
Structure:
[{GROUPNAME}]
Raw text content here.
Everything preserved exactly.
No escaping needed!
[EOG]
Content Rules:
[{GROUPNAME}] and the end marker is preserved exactly[EOG], another group marker, or end of file - but NOT at an empty lineCharacteristics:
Use Cases:
Example:
[{LICENSE_TEXT}]
MIT License
Copyright (c) 2026 Kirk Siqveland
Permission is hereby granted, free of charge...
[EOG]
Example with formatted content:
[{README}]
# My Application
## Installation
bash
npm install my-app
## Usage
Run the application with: `./myapp --config=settings.set`
For more information, see the documentation.
[EOG]
Content Boundaries: Text block content ends at:
[EOG] markerEncoded Binary Data in Text Groups:
Text groups can store base64-encoded binary data, making them useful for embedding small binary assets like icons, cryptographic keys, or checksums.
Example:
[{ICON_PNG}]
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
[EOG]
[{PUBLIC_KEY_ED25519}]
MCowBQYDK2VwAyEA8nE7YvqWKxRHEaoKbqXXEj1cTKN0XjJqKqvKqvKqvKqv
[EOG]
[ASSETS]
{name|type|data}
icon|png|[{ICON_PNG}]
pubkey|ed25519|[{PUBLIC_KEY_ED25519}]
[EOG]
Applications decode the base64 content as needed. This pattern is useful for:
Regular groups can reference text blocks using the syntax [{GROUPNAME}] as a value.
Text blocks can be referenced in regular group values.
Syntax: Use [{GROUPNAME}] as a value
Example:
[APP_INFO]
Name|My Application
Version|2.0.0
Description|[{APP_DESCRIPTION}]
License|[{LICENSE_TEXT}]
Readme|[{README}]
[EOG]
[{APP_DESCRIPTION}]
A powerful tool for managing workflows.
Features include:
- Task tracking
- Team collaboration
- Real-time sync
[EOG]
[{LICENSE_TEXT}]
MIT License
Copyright (c) 2026 Kirk Siqveland
[EOG]
[{README}]
See documentation at: https://example.com/docs
[EOG]
Text Block Location: Text Blocks may be defined anywhere in the file, before or after the groups that reference them. Parsers should be designed to locate text blocks when their references are encountered.
Parser Behavior: Typically when parsing, the value [{APP_DESCRIPTION}] would be replaced with the full content of the text block named APP_DESCRIPTION.
Rules:
Multiple References: The same text block can be referenced by multiple groups:
[CONFIG_EN]
Welcome|[{WELCOME_TEXT}]
HomePageTitle|[{WELCOME_TEXT}]
[{WELCOME_TEXT}]
Welcome to SET Files!
[EOG]
Default Delimiters:
| as the line-delimiterDelimiter|:[]:{}:|:\:…:!:[]:{}:|:\:…:!: is the settings delimiter, primarily just for this line[ begins a GROUP] ends a GROUP{ begins a TEXT-GROUP} ends a TEXT-GROUP| is the line delimiter\ is the escape character… indicates empty remaining fields* the default 10th character can be either `…` or `...`
! is the second level delimiter (for a nested array)Example Delimiter|:[]:{}:#:\:…:! Changes the line-delimiter from | to #
By default the pipe character "|" separates:
Do not begin or end lines with delimiters - this would shift all field positions.
Escape sequences are only needed in regular groups, not in text blocks.
The default Escape Character: \ (backslash)
Primary Use: Escape the field delimiter within data
Syntax: \|
Example:
[SETTINGS]
Expression|value > 10 \| value < 5
Path|C:\Program Files\App\data.txt
Here the \| escapes the pipe character so it's treated as literal text, not a field separator.
Edge Case - Field Ending with Backslash:
If a field value ends with a backslash, add a space before the delimiter to prevent ambiguity:
[PATHS]
WindowsPath|C:\Program Files\App\ |NextField
The space after the trailing backslash prevents \| from being interpreted as an escaped delimiter.
This specific case has important implications when parsing and trim()ing your data
In Text Blocks: No escaping is needed. Everything is literal.
[{CODE_SAMPLE}]
if (value | flag) {
path = C:\Program Files\App\
}
[EOG]
All pipes, backslashes and line-feeds in the text block above are literal - no escaping required.
Note on Character Encoding: Since SET files use UTF-8 encoding by default, Unicode characters can be included directly without escape sequences:
[MESSAGES]
Welcome|Café ☕
Greeting|你好世界
Symbol|★ ♥ ✓
[EOG]
Text Outside Groups:
Any text outside of group markers is ignored by parsers and serves as comments or documentation.
myconfig.set
This is a comment.
It will be ignored by parsers.
[DATABASE]
Host|localhost
Documentation Before Groups:
Text immediately before a group marker (with no blank line) is typically considered documentation for that group.
Database connection settings for production
[DATABASE]
Host|prod.example.com
Port|5432
Unreferenced Text Blocks:
Text blocks that are not referenced anywhere can serve as coherent comment blocks.
[{NOTES}]
These are internal notes.
Not referenced by any group,
so effectively a comment.
[EOG]
[EOG]Groups using positional fields (columns) should define field names on the first line after the group marker. When present, they apply to all data lines in that group. If your group has mixed structures, do not use field definitions - handle field identification in your application code. The use of Nested Arrays allows for Key:Value pairs in a single line ID!007|Name!James|Website!https://www.setfiles.org|Email!james@setfiles.org
Syntax: {field1|field2|field3}
Example:
[USERS]
{id|username|email|role}
1|alice|alice@example.com|admin
2|bob|bob@example.com|user
Field definitions are optional but strongly recommended for clarity and validation.
A single SET file can contain any combination of group types:
myapp.set
[THIS-FILE]
Version|4.3
Created|2026-01-07
[ENVIRONMENT]
Name|Phred
Connection|Serial
Protocol|RS232|9600|8|N|1|Off
[USERS]
{id|username|email}
1|alice|alice@example.com
2|bob|bob@example.com
[{LICENSE}]
MIT License
Copyright (c) 2026...
[EOG]
Syntax: [EOF]
The [EOF] marker is optional.
End of file is implicit when the file ends.
This section describes the recommended conventions for configuring parser behavior and storing file metadata.
Convention: The first line of the file should be the filename.
myconfig.set
[SETTINGS]
This helps identify the file when content is copied, embedded, or transmitted separately from filesystem metadata.
[THIS-FILE] GroupParser configuration and file metadata should be stored in a group named [THIS-FILE].
If using a \[THIS-FILE\] it should be the very first Group in the file.
Example:
myconfig.set
[THIS-FILE]
Version|4.3
Created|2026-01-07
Author|Kirk Siqveland
Delimiters|:[]:{}:|:\:…:!
Encode|UTF-8
Localize|NFC|en-US|LTR
[EOG]
[SETTINGS]
AppName|My App
Recommended keys for [THIS-FILE] group:
Specifies custom delimiter set for the entire file.
Format: Delimiters|:[]:{}:|:\:…:!
How to Read the Delimiter Definition:
The delimiter definition line uses a self-describing format. The first character define how to parse the rest of the line.
Example: :[]:{}:|:\:…:!
Breaking this down:
: [] : {} : | : \ : … : !
^ ^^ ^ ^^ ^ ^ ^ ^ ^ ^
| | | | | | └── Secondary Line Delimiter Socks|White!Black!Gray|S!M!L|$8.99
| | | | | |
| | | | | └─────── Empty Fields Marker
| | | | | (indicated empty fields in remainder of line)
| | | | └───────────── Escape Character
| | | |
| | | └─────────────────── Line Delimiter
| | |
| | └────────────────────────── Text Block Brackets
| └───────────────────────────────── Group Header Brackets
└──────────────────────────────────── Preamble Delimiter
Reading process:
:) is the preamble delimiter - used only to parse this line* `[]` = Group Header brackets
* `{}` = Text Block brackets
* `|` = Field Delimiter (used to parse line data)
* `\` = Escape character
* `…` = Empty Fields Marker (rather than multiple delimiters which may not number correctly)
* the Empty Fields Marker may only be used after the last non-empty field
* `!` = Nested Field Delimiter e.g. Socks|White!Black!Gray|S!M!L|8.99
Custom Example:
[THIS-FILE]
Delimiters|;[];{};,;\;...;!
[EOG]
This sets:
;[]{}, (comma instead of pipe)\... (three periods instead of single character)!Default: If not specified, assumes :[]:{}:|:\:…:!
When to Use Alternative Delimiters:
Consider changing delimiters when:
|) making heavy escaping tedious, as primary delimiter)Recommendations:
Character encoding for the file.
Format: Encode|UTF-8
Common values:
UTF-8 (default and recommended)UTF-16ASCIIISO-8859-1Internationalization settings affecting text processing, sorting, and comparison.
Format: Localize|NORMALIZATION|LOCALE|DIRECTION
Components:
Default: NFC|en-US|LTR
Examples:
Localize|NFC|en-US|LTR # English (US), left-to-right
Localize|NFC|ar-SA|RTL # Arabic, right-to-left
Localize|NFC|multi|AUTO # Multiple languages, auto-detect
Common metadata keys:
Version - Specification version or file format versionCreated - Creation dateModified - Last modification dateAuthor - File creatorCopyright - Copyright noticeDescription - File description or purposeThese are conventions only. Implementations may define their own metadata keys.
The [THIS-FILE] group, if used, should be placed:
This is conventional, not required. The group can be placed anywhere in the file.
However, if alternative delimiters are defined, they may not be available until the parser has read the \[THIS-FILE\] group, causing errors.
This section describes optional features that extend the minimum specification. Implementations may choose to support some, all, or none of these features.
Append data to a single Table-Row without adding a whole column
at the end of the normal line, add ::: (three preamble delimiters) to flag additional information to follow
Note: The preamble delimiter (: by default) can be replaced using the Delimiter definition in [THIS-FILE].
Fields prefixed with ::: allow a single field to be added to a single line, eliminating the need for a large number of empty fields in a typical table definition.
Syntax: :::value or :::fieldname:value
The ::: marker is followed by the value, or a key/name, the preamble delimiter (: by default) and then the value.
Example:
[USERS]
{id|username|email|role}
1|alice|alice@example.com|admin
2|bob|bob@example.com|user|:::temp_note:Pending verification
3|charlie|charlie@example.com|user
In line 2, the temp_note field is added just for that record without modifying the field definition. Other records don't need to have empty values for this field.
Use Cases:
Multiple single-use fields on one line:
[CONTACTS]
{id|name|email}
1|Alice|alice@example.com
2|Bob|bob@example.com|:::phone:555-1234|:::department:Engineering
3|Carol|carol@example.com
With custom preamble delimiter:
If your [THIS-FILE] defines delimiters as ;[];{};,;\;...;!, then single-use fields would use ;:
[DATA]
{id|value}
1|100
2|200;;;note;Special case
The Empty Fields character … (or three periods ...) indicates "remaining fields are empty." Parsers may allow both versions or be strict to a single option.
Example:
[CONTACTS]
{id|name|phone|email|address|city|state|zip}
1|Alice|555-1234|alice@example.com|…
2|Bob|555-5678|…
Is equivalent to:
1|Alice|555-1234|alice@example.com||||
2|Bob|555-5678|||||
Rules:
For individual lines that contain instances of the standard delimiter, you can override the delimiter for just that line.
Syntax: Line starts with preamble delimiter followed by the single-use delimiter to be used
Example:
[SETTINGS]
AppName|My Application
Port|8080
:!URL!https://example.com/api?param1=value|param2=value|param3=value
:!Expression!(a | b) & (c | d) | (e | f)
Database|localhost
[EOG]
How it works:
: (the preamble delimiter from Delimiter|:[]:{}:|:\:…:! in [THIS-FILE])! (single-use for this line only)! instead of | as field delimiterUse cases:
Without single-line override:
Expression|value > 10 \| value < 5 \| value > 100
With single-line override:
:!Expression!(value > 10 | value < 5 | value > 100)
The second approach is more readable when there are many instances of the delimiter character in the data.
This specification is licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0).
Copyright (c) 2026 Kirk Siqveland
You are free to:
Under the following terms:
Full license text: https://creativecommons.org/licenses/by/4.0/
Implementations of this specification may use any license of the implementer's choosing.
Q-Set Specification v4.3
Simplified implementation guide using defaults only
SET File Implementation Guide
Comprehensive guide covering:
Advanced Usage
For advanced usage patterns, specialized implementations, and community-contributed methods, visit:
https://setfiles.org/advanced
Questions or feedback?
Visit: https://github.com/kirksiqveland/setfile
License:
Creative Commons Attribution 4.0 International (CC BY 4.0)
Copyright (c) 2026 Kirk Siqveland