STree Specification
===================
Encoding
--------
- File paths and [field](#field-syntax) values must be encoded according the rules outlined in [percent encoding](#percent-encoding)
- The document **MUST** use US-ASCII/UTF-8 encoding.
- Anywhere in this specification that 'space' is used, this is referring strictly to the ASCII
space character (decimal `32`, hex `20`, Unicode codepoint `U+0020`), not
a horizontal/vertical tab, form feed, carriage return or line feed.
- Anywhere in this specification that 'newline' is used, this is referring strictly to the ASCII
line feed character (decimal `10`, hex `0A`, Unicode codepoint `U+000A`) **ONLY**.
- All lines must be terminated with line feeds (`\n`), carriage returns are not supported.
Percent Encoding
----------------
The following characters present in [field](#field-syntax) values and paths must be 'escaped'
using the following conversion rules:
| `=` | `%3D` |
| [space](#encoding) | `%20` |
| `%` | `%25` |
Manifest Anatomy
----------------
An STree manifest consists of:
- The first line, beginning with `#stree`, followed by a single [space](#encoding) character and
an integer denoting the number of seconds elapsed since the [Unix epoch](https://en.wikipedia.org/wiki/Unix_time) at the time of capturing.
- The second line, referring to the *top*/*base* of the tree which **MUST** be represented as `.`
- A sequence of lines, each beginning with the path to the file (from the *top* of the tree), including
**AT LEAST** the [base fields](#base-fields) and any number of the [optional fields](#optional-fields).
All of which must be separated by [space](#encoding) characters. The line/entry (as with all lines) must
be terminated by a [newline](#encoding).
Field syntax
------------
Every field consists of its' key and value separated by an `=` (equal sign), with no spacing of any kind between
the key, equal sign and value.
```
type=directory
device=7:0:7
```
Base Fields
-----------
These fields **MUST** be present in every document
| `type` | One of the [file types](#file-types) |
| `mode` | Exactly four digits, denoting the file mode in octal notation (e.g.: `0755`) |
| `user` | The owning user account name and UID, separated by a colon (e.g.: `user:1000`). A sentinel value of `<unknown>` may be used for unresolvable users |
| `group` | The owning group name and GID, separated by a colon (e.g.: `users:99`). A sentinel value of `<unknown>` may be used for unresolvable groups |
| `time.modify` | The time of the file's last modification in [time notation](#time-notation). "modification" meaning the files' content, not its metadata as in `time.change` |
| `size` | Only required for `type=regular` entries; The file size in bytes |
Optional Fields
---------------
These fields are completely optional
| `inode` | A 64-bit unsigned integer denoting the file's inode number |
| `nlink` | A 64-bit unsigned integer denoting the amount of hard links to a file |
| `time.access` | The time of the file's last access in [time notation](#time-notation) |
| `time.change` | The time of the file's last change, in [time notation](#time-notation) |
| `time.birth` | The time of the file's creation in [time notation](#time-notation) |
| `device` | Three unsigned integers denoting (in order) the combined major and minor identifiers, the major and the minor, separated by colons (e.g.: `7:0:7`) |
| `device.special` | Only applicable to entries with `type=block`/`type=character`; Three unsigned integers denoting (in order) the combined major and minor identifiers, the major and the minor, separated by colons (e.g.: `7:0:7`) |
| `hash.<algorithm>` | Only applicable to entries with `type=regular`; The hash of the file produced by [`<algorithm>`](#checksum-algorithms) |
| `link.target` | Only applicable to entries with `type=link`; The target which the symlink points to, regardless of whether the target exists or not |
| `link.real` | Only applicable to entries with `type=link`; The path a symlink resolves to after following any number of redirections. A sentinel value of `<broken>` may be used when the resolved destination does not exist. |
Extension Fields
----------------
Unlike [optional fields](#optional-fields), these may be ignored by an implementations lacking the capability
to support them when parsing manifests and validating trees, and **SHOULD NOT** be relied upon for integrity.
| `mime.type` | The mime type of the file, as produced by [`libmagic`](https://github.com/cdd/libmagic) (e.g.: `inode/symlink`, `text/plain`). A sentinel value of `<unknown>` may be used if the information cannot be obtained from libmagic |
| `mime.encoding` | The mime encoding of the file, as produced by [`libmagic`](https://github.com/cdd/libmagic) (e.g.: `binary`, `us-ascii`). A sentinel value of `<unknown>` may be used if the information cannot be obtained from libmagic |
File Types
----------
- `regular`
- `directory`
- `link`
- `socket`
- `fifo`
- `block`
- `character`
Time Notation
-------------
Times are represented as two 64-bit unsigned integers, one for the number of seconds,
and one for nanoseconds. Due to the nature of the return values provided by the C `stat` family
of functions (with seconds being signed and nanoseconds being 32 *or* 64-bit unsigned integer), the value(s)
should be converted to unsigned 64-bit integers, clamping any negative value to `0`. Systems which
do not keep track of nanoseconds may simply use a value of `0` in its' slot.
If your usage would require validation of times before the Unix epoch, I suppose I apologize,
but it's just not a likely enough use case to bother with. Perhaps mtree would be a better solution
for you.
Times are written in the format `<seconds>:<nanoseconds>`, e.g.: `1791133798:884526192`.
Checksum Algorithms
-------------------
- `sha256`
- `sha512`
- `sha3-256`
- `sha3-512`
- `blake2b`
- `blake2s`
- `blake3`
- `crc32`
- `crc32c`
Comparison
----------
All fields which are present **MUST** be used when comparing/validating a tree with the exception of
- `time.access`: affected by the actions needed to produce the `STree` format.
- `mime.type`, `mime.encoding`: Extension fields, which are only required to validate if
the implementation is capable of supporting them.