tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

arboricx-bundle-format.md (14924B)


Arboricx Portable Bundle Format Specification

Container: 1.0. Manifest: 1.1. Indexed nodes.

The Arboricx Portable Bundle is a self-contained binary format for distributing Tree Calculus programs. Nodes are identified by their positions in a topologically ordered array. Pure Tree Calculus programs can write bundles, and readers verify them through structural inspection.

Table of Contents

  1. Design Principles
  2. Top-Level Container Layout
  3. Header
  4. Section Directory
  5. Section: Manifest (type 1)
  6. Section: Nodes (type 2)
  7. Node Payload Format
  8. Tree Calculus Reduction Semantics
  9. Binary Primitives
  10. Bundle Verification
  11. Canonicalization
  12. Known Section Types

1. Design Principles

Distribution systems establish artifact identity and integrity using a digest of the complete canonical bundle file. Registries, lockfiles, and content-addressed caches use this file-level identity.


2. Top-Level Container Layout

+------------------+------------------+------------------+------------------+
| Header           | Section Directory| Manifest Section | Nodes Section    |
| (32 bytes)       | (N × 32 bytes)   | (variable)       | (variable)       |
+------------------+------------------+------------------+------------------+

Total bundle size = 32 + (sectionCount × 32) + manifestSize + nodesSize

All multi-byte integers use big-endian byte order.


3. Header

Offset Size Field Description
0 8 bytes Magic ASCII "ARBORICX"
8 2 bytes Major version u16 BE. Currently 1
10 2 bytes Minor version u16 BE. Currently 0
12 4 bytes Section count u32 BE. Number of entries in the section directory
16 8 bytes Flags u64 BE. Reserved; currently all zeros
24 8 bytes Directory offset u64 BE. Byte offset to the section directory (always 32)

4. Section Directory

Array of N entries, each exactly 32 bytes.

Offset (within entry) Size Field Description
0 4 bytes Type u32 BE. Section type identifier
4 2 bytes Version u16 BE. Section-specific version
6 2 bytes Flags u16 BE. Bit 0 (0x0001) = critical section
8 2 bytes Compression u16 BE. 0 = none (currently the only value)
10 2 bytes Reserved u16 BE. Padding; must be zero
12 8 bytes Offset u64 BE. Byte offset from bundle start to section data
20 8 bytes Length u64 BE. Length of section data in bytes
28 4 bytes Reserved Padding; must be zero

Verification:


5. Section: Manifest (type 1)

Binary encoding of bundle metadata. Fixed-order core layout followed by optional TLV tail.

Manifest =
  magic            8 bytes    "ARBMNFST"
  major            u16 BE     Manifest major version (1)
  minor            u16 BE     Manifest minor version (1)

  schema           string     "arboricx.bundle.manifest.v1"
  bundleType       string     "tree-calculus-executable-object"

  treeCalculus     string     "tree-calculus.v1"
  treeHashAlgorithm string    "indexed"
  treeHashDomain   string     "arboricx.indexed.node.v1"
  treeNodePayload  string     "arboricx.indexed.payload.v1"

  runtimeSemantics string     "tree-calculus.v1"
  runtimeEvaluation string    "normal-order"
  runtimeAbi       string     "arboricx.abi.tree.v1"
  capabilityCount  u32 BE     Number of capability strings (currently 0)
  capabilities     string[]   Array of length-prefixed UTF-8 strings

  closure          u8         0 = complete
  rootCount        u32 BE     Number of root entries
  roots            Root[]     Array of root entries
  exportCount      u32 BE     Number of export entries
  exports          Export[]   Array of export entries

  metadataFieldCount u32 BE   Number of metadata TLV entries
  metadataFields   TLV[]      Metadata tag-value entries
  extensionFieldCount u32 BE  Number of extension TLV entries (currently 0)
  extensionFields  TLV[]      Extension entries (skipped by parsers)

String Format

string =
  length      u32 BE    Number of UTF-8 bytes
  bytes       byte[length]  UTF-8 content

Root Entry

Root =
  index       u32 BE     Node index into the nodes section
  role        string     Length-prefixed UTF-8 ("default" for first root, "root" for others)

Export Entry

Export =
  name        string     Length-prefixed UTF-8 export identifier
  root        u32 BE     Node index into the nodes section
  kind        string     Length-prefixed UTF-8 (currently "term")
  abi         string     Length-prefixed UTF-8 ABI string

TLV Entry

TLV =
  tag         u16 BE     Tag identifier
  length      u32 BE     Value length in bytes
  value       byte[length]

Metadata Tags

Tag Name Value
1 package UTF-8 text
2 version UTF-8 text
3 description UTF-8 text
4 license UTF-8 text
5 createdBy UTF-8 text

Unknown metadata tags are ignored. Unknown extension tags are skipped by length.

Semantic Constraints

Constraint Value
schema "arboricx.bundle.manifest.v1"
bundleType "tree-calculus-executable-object"
treeCalculus "tree-calculus.v1"
treeHashAlgorithm "indexed"
treeHashDomain "arboricx.indexed.node.v1"
treeNodePayload "arboricx.indexed.payload.v1"
runtimeSemantics "tree-calculus.v1"
runtimeEvaluation "normal-order"
runtimeCapabilities Empty
Export names Unique and nonempty
Export kind / ABI "term" / "arboricx.abi.tree.v1"
runtimeAbi "arboricx.abi.tree.v1"
closure 0 (complete)
rootCount At least 1
exportCount At least 1

6. Section: Nodes (type 2)

NodesSection =
  nodeCount     u64 BE    Total number of node entries
  entries       NodeEntry[]

Node Entry

NodeEntry =
  payloadLen    u32 BE    Length of payload in bytes
  payload       byte[payloadLen]

Each node’s zero-based position in entries is its identity.


7. Node Payload Format

Child references are u32 big-endian indices into the node array. The array must be topologically sorted: every child index must be strictly less than the entry's own position.

Leaf

Payload = 0x00

Exactly 1 byte.

Stem

Payload = 0x01 || child_index (u32 BE)

Exactly 5 bytes.

Fork

Payload = 0x02 || left_index (u32 BE) || right_index (u32 BE)

Exactly 9 bytes.


8. Tree Calculus Reduction Semantics

The bundle represents a Tree Calculus term. The reduction rules are:

The t operator is left associative.
1.  t  t      a b       -> a
2.  t (t a)   b c       -> a c (b c)
3a. t (t a b) c t       -> a
3b. t (t a b) c (t u)   -> b u
3c. t (t a b) c (t u v) -> c u v

Closure: The bundle declares closure = 0 (complete), meaning all nodes reachable from export roots are present in the nodes section.


9. Binary Primitives

u8

Single byte, value 0-255.

u16 (2 bytes)

value = (byte[0] << 8) | byte[1]

u32 (4 bytes)

value = (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3]

u64 (8 bytes)

value = (byte[0] << 56) | ... | byte[7]

10. Bundle Verification

  1. Magic check: First 8 bytes must be "ARBORICX".
  2. Version check: Major version must be 1.
  3. Section directory: Parse all entries; reject unknown critical sections. Verify reserved fields are zero.
  4. Manifest parsing: Decode fixed-order manifest; validate semantic constraints.
  5. Nodes section: Parse all entries.
  6. Bounds checking:
    • Every root index < nodeCount
    • Every export index < nodeCount
    • In every Stem payload, child_index < entry_position and child_index < nodeCount
    • In every Fork payload, both indices < entry_position and < nodeCount
  7. Acyclicity: Guaranteed by the child < parent rule above.
  8. Closure: Traverse from all root/export indices; confirm every reached index is valid.

11. Canonicalization

A bundle is canonical iff:

  1. Maximal deduplication. Each structurally distinct subtree has exactly one entry.
  2. Topological order. Children precede parents.
  3. Deterministic post-order traversal. Nodes are emitted in the order discovered by a left-to-right recursive post-order walk.
  4. Exact section lengths. Each section ends immediately after its encoded content.
  5. Reserved fields are zero.

Canonical writers produce identical bytes for identical ordered named roots and metadata.


12. Known Section Types

Type Name Required Version Description
1 Manifest Yes 1 Bundle metadata
2 Nodes Yes 1 Topological DAG node entries

Unknown section types are permitted if not marked critical.


Appendix A: Complete Example Layout

For Stem(Leaf) (t t), use two nodes: Leaf at index 0 and Stem(0) at index 1. With one manifest and one node section, the directory occupies 64 bytes and payloads start at offset 96. If the encoded manifest is M bytes, nodes start at 96 + M, and the complete canonical file is 118 + M bytes.

The node section is 22 bytes:

nodeCount: u64(2)                         8 bytes
Leaf:      u32(1), 00                     5 bytes
Stem(0):   u32(5), 01, u32(0)             9 bytes

Appendix B: File Extension

Bundles use the .arboricx file extension. Plain source files use .tri.

Portable library reader profile

lib/binary.tri and lib/arboricx/ implement the following reader profile. Focused tests exercise complete replies and error precedence. Reader profiles differ at the container boundary.

Additional numeric error codes: invalid binary input 15, invalid count 16, unsupported section profile 17, invalid section range 18. Manifest magic/version/ trailing-data errors use 10/11/12; unsupported manifest contracts use 13. Existing missing/duplicate node errors remain 9/7. eof? on nonempty input reports unexpected bytes (2).

Validation is in memory. Directory overlap and export-name checks are quadratic in their small lists. Counted parsers and logical-tree observation still need caller-selected resource limits, including for structurally valid inputs.

Host reader profiles

The Haskell decodeBundle / verifyBundle path validates required sections, manifest execution semantics and export ABIs, roots, backward edges, and duplicate nodes/exports. Its container decoder accepts a broader header/directory profile, with weaker constraints on minor version, header flags, section versions/reserved fields, and general section overlap than the portable reader above. Decode and semantic verification are separate API calls; CLI loading uses both.

The browser reader uses strict header/directory checks, contiguous sections, whole-file SHA-256 verification, and explicit byte/node/count limits. Its package and compiler download profiles are documented in the playground guide.

Canonical writers emit the format described here. Reordered roots/exports or changed metadata affect file identity; canonicalization is deterministic for an ordered named export set and metadata. Programs with equal application results can have distinct representations and bundle identities. Distribution authentication and execution permissions are separate from structural parsing.