modules.md (32341B)
Modules and packages
A native tricu module declares dependencies and exports, then defines its values. Each exported value is a closed executable tree. A module manifest names those trees, and an Arboricx bundle carries them for distribution.
Write a module
!import prelude *
!import math as M
!export main
main = M.add 20 22
The CLI discovers the nearest tricu.workspace in the input file's directory
or its ancestors, independently of the caller's working directory. Without a
declaration, dependencies remain sibling NAME.tri files. Module names start
with an ASCII letter or underscore and continue with letters, digits,
underscores, or hyphens.
Source locations without a build directory
A workspace declaration maps import names to authored source paths. For example, place this at your project root (columns are separated by an actual tab):
tricu.workspace.v1
math<TAB>libraries/math.tri
prelude<TAB>libraries/prelude.tri
Then apps/main.tri can import math and prelude without copying files
beside it. Paths are relative to the declaration, must end in .tri, and can
include .. for explicitly shared sources. LF endings and a final newline are
required; every line must contain the header or a mapping row. Declarations are
limited to 64 KiB; names to 128 ASCII characters. All mapped files must exist.
Direct symlinks and nonregular input files are rejected; use stable, trusted
directories.
The repository declaration provides its native library locations, including
native_math and arboricx_server. Compile the application directly:
result/bin/tricu arboricx compile --file tricu-apps/arboricxServer.tri \
--store "$WORK/application" --output "$WORK/server.arboricx" \
--compile-timeout 900
Sibling modules remain available. A sibling and mapping with the same name must
refer to the same file; conflicting and case-colliding names fail. The nearest
declaration supplies the complete mapping. A nearer
header-only tricu.workspace establishes an isolated flat workspace; the
self-contained module demos use this boundary.
The host copies the selected source locations into a temporary flat workspace and removes it after compilation, including on failure. Source text is unchanged; the portable compiler still reads headers, discovers the reached dependency closure, rejects cycles, and owns compilation and cache semantics. Only reached modules are compiled, though all mapped and validly named sibling files are copied. Source discovery is limited to the declared mappings and siblings; registry fetching is a separate preparation step. Keep sources stable during the copy/build. Full-hash imports and package-generated binding modules work as before.
eval, arboricx compile, and provenance capture share these location rules.
Capture retains reached source bytes under their module names; replay uses that
flat layout. Executable and receipt identities depend on content, independently
of the workspace paths. Use Build and run
to select the stable compiler.
Headers
A directive header precedes definitions. Blank lines and -- comments may appear
throughout it. Words are separated by ASCII spaces, with LF line layout.
| Declaration | Effect |
|---|---|
!import NAME |
Acquire NAME.tri and qualify its public exports with NAME. |
!import NAME as Alias |
Acquire the dependency under a chosen qualifier. |
!import NAME * |
Open its public exports. |
!import HASH as Alias or !import HASH * |
Select an immutable manifest by full hash. |
!export name |
Publish a local definition. |
!export * |
Publish all local definitions. |
!export * from REF |
Acquire and re-export that dependency's public interface. |
!source NAME |
Declare a build-only dependency. |
Qualifiers use simple ASCII identifiers. Full-hash imports require as Alias or
*. Public interfaces contain at least one export; eval selects exported main.
A header-only re-export module is valid. !export * selects local definitions,
while imported values are re-exported with !export * from REF.
The workspace explicitly supplies every dependency, including the prelude. Repeated imports under different qualifiers share one dependency build. Local edges are deduplicated in first header-occurrence order. Duplicate build-only edges, redundant build-only/import edges, name collisions, missing dependencies, and cycles are errors. Bodies contain definitions; expose the entrypoint through an export.
Resolution and identity
Workspace names resolve through the selected workspace. A source map assigns paths, aliases, frontends, and ordered dependencies for a larger build. Full hashes select immutable manifests. Graph builds resolve workspace names through that map. Store aliases are managed separately.
Imports hydrate and verify the complete public interface. This includes unused exports: namespace validation and cache keys depend on the interface as a whole. Locks pin that interface. Re-exports preserve the exact executable tree hashes.
Different labels can name the same tree. Source or receipt changes can leave an executable identity unchanged. Conversely, functions with the same observed results may have different trees; tricu programs can observe that difference.
Native module objects use arboricx.indexed-tree.v1, and their manifests use
arboricx.module-manifest.v1. The CLI's default indexed import supplies native
imports, stored execution, and compiler selection without flat expansion. Storage defines the representations, hashes,
conversion budget, receipts, and cache rules.
Publication
The tricu object layer verifies content digests on reads. It publishes immutable objects with no-replace writes and publishes individual aliases, locks, and cache indices atomically. File and parent-directory syncing support crash durability on the tested local filesystems. A post-publication sync failure may leave the new name visible; retry and verify it.
A build spans several objects, so an interrupted build can leave completed objects behind. Stabilize source files throughout a build, and use trusted stores whose contents remain stable during verified reads. A receipt records input identities and output references; preserving the corresponding source bytes is part of maintaining a reproducible build.
The lower-level sourceModulePinAlias API explicitly updates a module alias in
the neutral alias-file format. This is separate from graph workspace resolution.
Implementations
lib/source/{module,object,lock,build,map,bundle}.tri owns native module policy.
The Haskell host supplies primitive IO and typed tree transport. Its content store
supports CLI artifact operations; source compilation uses the portable compiler.
Compiler architecture describes the build boundary;
Testing lists the module and graph probes.
Exact-version packages from trusted registries
The initial package profile is one compiled native module per release. Versions are matched as exact opaque labels. Names and versions contain 1–128 ASCII letters, digits, dots, underscores or hyphens, starting with a letter or digit. Registry policy lives in the Haskell CLI; the native server serves the index and bundles, and native compilation continues to consume local CAS objects.
Publish
Compile a library normally, then publish from an administrator account with direct access to the server's trusted store directory:
result/bin/tricu arboricx compile --file library.tri \
--store "$WORK/producer" --output "$WORK/library.arboricx"
result/bin/tricu package publish --root "$REGISTRY_ROOT" \
--name library --version 0.1.0 --file "$WORK/library.arboricx"
Publication validates the bundle and its native interface, writes the bundle before the index record, and refuses to replace an existing name/version with different bytes. Identical retries are allowed, including concurrent publishers. Failure may leave an unreferenced bundle. Publication requires local administrator access to the registry store.
The current lib/arboricx/server.tri serves GET /_arboricx/package/NAME/VERSION from that same store. Compile the updated
server source and deploy the resulting bundle before using this route. Use an
HTTPS reverse proxy for non-loopback service. A read-only registry deployment
should restrict public methods/routes and grant the server only read access; the
older bundle-upload route still exists.
Resolve, fetch, bind
Create an explicit registry configuration. This loopback example assumes the native server is listening on port 9050:
printf 'arboricx.registries.v1\nhome\thttp://127.0.0.1:9050\n' > "$WORK/registries"
result/bin/tricu package resolve --registries "$WORK/registries" \
--registry home --name library --version 0.1.0 --lock "$WORK/library.lock"
result/bin/tricu package fetch --registries "$WORK/registries" \
--lock "$WORK/library.lock" --store "$WORK/application"
result/bin/tricu package bind --lock "$WORK/library.lock" \
--store "$WORK/application" --output "$WORK/library.tri"
Configuration rows are tab-separated registry name and origin URL. Names must be
unique. URLs are HTTPS origins without trailing slash, path, credentials, query
or fragment; HTTP is allowed only for localhost, 127.0.0.1 and [::1].
Each request uses the explicitly configured registry. Redirects are rejected.
resolve alone consults the index. It records registry name/origin, exact
version, module-manifest hash and whole-bundle hash. Changing a selection
requires --update; a changed mapping for the same locked
registry/origin/name/version is rejected even with that flag. This detects
changes relative to the current lock. Retain older locks separately to track
historical registry claims.
fetch uses only the lock's bundle pin, verifies the download and its
advertised module identity before importing into the application CAS, while
leaving mutable module aliases unchanged. Its configured origin must still match
the lock. A complete, verified local module is reused offline. Corrupt or
incomplete objects under an existing manifest fail and require explicit repair.
bind is offline. It verifies the local module and writes a normal workspace
module containing !export * from HASH. An application beside that file can use
!import library as L and call L.someExport. Use the same application store
for compilation. Commit the lock and generated binding; after an explicit
update, fetch and regenerate the binding. The compiler reads the source
binding and local CAS. Registry resolution and lock updates belong to the
package commands. For several dependencies, use the project declaration and
consolidated lock below to manage them in one lock.
Trusting a registry accepts its name/version assertions. Establish build provenance separately and grant program IO permissions explicitly. TLS uses the host client trust configuration. Metadata downloads are bounded to 4 KiB, bundles to 64 MiB, and requests to a 60-second overall deadline (30-second response timeout). These limits bound transport. Set heap limits and execution isolation separately; the native server reads/encodes complete files. This profile supports explicitly trusted public-read registries and exact one-module releases. Credential handling, signatures, version solving, multi-module records, and provenance require a separate package profile.
Project dependencies
tricu project prepares a whole dependency set while keeping registry
resolution, fetching, workspace binding and compilation separate. Declare each
dependency and exact version explicitly, fetch it, and import its generated
source binding. Compilation uses local inputs under the ordinary workspace rules
above.
A tricu.project file declares local binding, registry label, package name,
exact version, separated by tabs, with LF endings and a final newline:
arboricx.project.v1
foundation<TAB>home<TAB>tricu-base<TAB>0.1.0
fileio<TAB>home<TAB>tricu-file-io<TAB>0.1.0
inspection<TAB>home<TAB>arboricx-inspection<TAB>0.1.0
<TAB> above denotes one actual tab. The checked-in inspector declaration is ready to copy.
Selection is independent of declaration order; generated locks are sorted by
binding name. Bindings use the flat workspace name grammar above, at most 128
ASCII characters, excluding names that would parse as full manifest hashes.
Duplicate and case-colliding names are rejected, even on case-sensitive systems.
Project metadata is bounded to 64 KiB and 128 direct dependencies. Empty
projects are valid. Versions are opaque exact labels: 1.x selects the literal
version 1.x. Each row must contain the header or a dependency declaration.
With an explicitly trusted registry configuration whose home entry serves those
releases, prepare a fresh workspace (the site releases must be deployed or served
locally first):
mkdir -p "$WORK/inspector"
cp demos/package-inspector/main.tri demos/package-inspector/tricu.project "$WORK/inspector/"
PROJECT="$WORK/inspector/tricu.project"
result/bin/tricu project resolve --project "$PROJECT" --registries "$REGISTRIES"
result/bin/tricu project fetch --project "$PROJECT" --registries "$REGISTRIES" --store "$APP_STORE"
# The registry can now be stopped.
result/bin/tricu project bind --project "$PROJECT" --store "$APP_STORE"
result/bin/tricu project check --project "$PROJECT" --store "$APP_STORE"
result/bin/tricu arboricx compile --file "$WORK/inspector/main.tri" \
--store "$APP_STORE" --output "$WORK/inspector.arboricx"
From within that workspace, omit --project: it defaults to tricu.project.
The lock defaults to tricu.lock beside the declaration; --lock FILE
overrides it. Bind/check default to that same directory; --directory WORKSPACE
overrides where generated modules live. Authored sources stay in their existing
locations. Explicit paths are relative to the caller's working directory. Local
or global --store selects the application CAS, just as with the other CLI
operations.
Frozen resolution and deliberate updates
resolvecreates the complete lock on first use, resolving each distinct origin/name/version once. With an existing lock it is frozen: it checks the declaration and configured origins against that lock without an index request. Any addition, removal, version, registry-label or origin change requires update. This differs deliberately from the low-level single-packageresolve, which rechecks its index entry on every call.update --dependency BINDINGre-resolves only the selected binding; repeat the option for several, or use--allexplicitly. Edit the declaration first to request a new version, add a dependency, or remove one. Unselected bindings must match the declaration and configured origins exactly; otherwise the command fails before any GET. Removals are handled locally. A rename is a removal plus an addition: select both names, or use--all. For a case-only rename, remove the old dependency and bind before adding the new spelling.- Every successful update prints before/after selections, full module and bundle hashes, and whether each identity changed. Labels, artifact identities, and observed behavior can vary independently; assess behavioral changes separately.
- All resolutions and validation must succeed before one atomic lock publication;
their failure leaves the previous lock intact. A post-publication filesystem
sync failure may leave the new complete lock visible: retry and verify, as in
the publication rules above. Executable import occurs during
fetch. A changed assertion for an origin/name/version in the current lock is rejected, even across binding or registry-label renames. Historical claims can be tracked by preserving older locks in version control.
For example, after changing only the foundation version in the declaration:
result/bin/tricu project update --project "$PROJECT" --registries "$REGISTRIES" \
--dependency foundation
result/bin/tricu project fetch --project "$PROJECT" --registries "$REGISTRIES" --store "$APP_STORE"
result/bin/tricu project bind --project "$PROJECT" --store "$APP_STORE"
result/bin/tricu project check --project "$PROJECT" --store "$APP_STORE"
Fetching, binding and recovery
fetch first checks the whole declaration/lock and every configured origin. It
fetches by locked bundle hashes. Verified complete local modules are reused
offline. Transport and executable verification use the same package controls
above; an interrupted fetch may leave valid immutable objects available for
retry. Corrupt/incomplete existing modules fail and require explicit repair.
This verifies local executable availability. Retaining the original distribution
bundle’s bytes is a separate responsibility.
bind verifies all required local interfaces and preflights all output
paths before changing bindings. It writes normal !export * from HASH modules.
A derived .arboricx-project-bindings ledger tracks which files it owns,
allowing safe updates and removal of obsolete bindings. Output paths must be
fresh or already owned by the ledger; unmanaged files cause rejection even when
their bytes match. Edited owned files, direct symlinks, directories and
input/output collisions are errors. Unrelated authored files are left alone. To
migrate manually generated bindings, explicitly remove those files after
preserving any edits, then bind into the clean paths.
Multi-file binding uses journaled, per-file updates. A
.arboricx-project-bindings.pending journal is published before any changes;
interruption can leave a mixture of prior and pending files. After interruption,
rerun bind with the same project/lock; it accepts files from the prior or
pending generation but refuses user edits. Finish recovery before another
dependency update. If the lock has already changed, restore the interrupted
generation's declaration/lock first. Use exclusively owned, trusted workspaces
and keep their contents stable throughout bind/check/compile; concurrent updates
or hostile directory mutation are unsupported.
check is read-only and offline. It rejects stale declarations, mismatched or
edited bindings, pending transactions, missing modules and corrupt objects. The
compiler reads ordinary source bindings: run check before compilation when
lock/binding agreement matters. The check covers dependency selections and
bindings. Track source inventories, compiler pins, and build receipts
separately, and configure execution isolation at the host boundary.
Commit the declaration and lock. Either regenerate bindings in fresh workspaces, or commit generated modules and their ownership ledger together. Complete journal recovery before committing. The one consolidated lock stores registry labels/origins, exact releases, module hashes and bundle hashes; the ledger records derived local file ownership.
Explain selections and compare locks
These commands are offline and read-only. They inspect the supplied project metadata and existing local store as data. Store contents, bindings, and caches remain unchanged; missing evidence is reported as unavailable. Registry resolution, fetching, and export execution are separate operations:
result/bin/tricu project explain --project "$PROJECT" --store "$APP_STORE"
result/bin/tricu project diff --before initial.lock --after current.lock --store "$APP_STORE"
explain uses the same declaration/lock/directory defaults as bind/check. It
shows declared and locked selections, exact registry origins, full module and
bundle pins, binding-file agreement, ownership/journal status, and verified or
unavailable local interfaces/exports. Other evidence remains visible even when a
declaration or generated file is stale. A missing or malformed declaration/lock
produces an input error. Registry claims come from the lock; current registry
trust, publisher authentication, and build provenance require independent
checks.
diff needs only two consolidated locks and a local store containing evidence
for either/both. It separates selection additions/removals/changes, export and
ABI changes, object identities, and ordered tree structure for common
exports. Unchanged selections are included too. Comparison of unavailable
evidence is unknown, including when the locks contain equal hashes. Every
relevant local object is authenticated and decoded, including unchanged pins. An
absent dependency on one side is distinct from a selected module whose objects
are missing.
Structural comparison operates on validated indexed DAG records. Bottom-up interning of constructor/child-ID tuples in a shared table gives exact structural equality directly over stored records. It ignores physical node numbering, unreachable records and irrelevant packaging metadata, while retaining constructor and child order. Work for an export pair scales with stored records (with logarithmic map operations), independently of the number of logical occurrences in the unfolded tree. Normal native package import already canonicalizes exports into name-neutral indexed objects; the inspector acceptance case therefore also has unchanged common-export object hashes. The comparison accepts readable noncanonical indexed encodings and compares their tree structure directly. Normalization and behavioral equivalence require separate analysis. Distinct trees can have identical observed results; tricu can also observe their structure.
The first report profile supports closed nonempty native package modules with
indexed-tree exports and arboricx.abi.tree.v1. Unknown kinds/ABIs are
explicitly unsupported; available manifest interfaces can still be compared.
Corrupt bytes, malformed graphs, missing files, nonregular CAS paths and
unsupported profiles are reported as invalid or unavailable evidence. Display
fields percent-escape non-ASCII/control bytes and %, so package metadata
cannot insert terminal control sequences.
Report commands return 0 when all requested evidence is available/verified (and explain's declaration/bindings/ownership state agree), even when a valid diff finds changes. They return 1 for incomplete/invalid/unsupported evidence or stale project state, printing the available report to stdout. Invalid input files produce a diagnostic instead. Report wording is intended for human readers and may change between releases.
Reads are bounded to 64 KiB per module manifest and 64 MiB of retained authenticated executable bytes per module. Repeated object references within a module share inspection results; report text is materialized per dependency before proceeding to the next. A budget-limited export is reported unavailable. These limits cover encoded bytes; parsed graphs and comparison tables take additional heap space. Use stable, trusted stores and external time/heap controls for large inputs. Filesystem safety relies on that trust and stability. Bundle pins are reported as locked claims; authenticated executable evidence comes exclusively from CAS modules and objects. Authenticate original distribution bundles separately when their bytes are needed.
The inspector acceptance example compares its retained initial.lock with the
updated consumer/tricu.lock: foundation gained revision; its three common
exports remained structurally identical; fileio and inspection stayed pinned.
The acceptance harness separately records whether the rebuilt inspector itself
remained byte-identical. Determining a downstream build’s identity requires
inspecting its own artifacts.
Retained build inputs and reproduction
The native receipt records input and output identities. A clean rebuild also
requires the corresponding source bytes and executable inputs. provenance
retains that closure and keeps three assurances distinct: verified
bytes, recorded producer claims, and a reproduced build.
First compile with an explicit trusted toolchain, as in build/run. Keep the source workspace stable and use the same application/tool stores for capture:
result/bin/tricu arboricx compile --file "$WORK/inspector/main.tri" \
--store "$APP_STORE" --toolchain "$COMPILER_MANIFEST" --tool-store "$TOOL_STORE" \
--output "$WORK/inspector.arboricx"
result/bin/tricu provenance capture --file "$WORK/inspector/main.tri" \
--store "$APP_STORE" --toolchain "$COMPILER_MANIFEST" --tool-store "$TOOL_STORE" \
--project "$WORK/inspector/tricu.project" --output "$WORK/inspector-inputs"
Capture prints the input-pack hash. It discovers the native root receipt through
the latest cache locator, or accepts an explicit --receipt HASH if that
locator is absent or another historical build is desired. Every reached source
file must still match its receipt. --project is optional (omit it for a
library workspace); its lock defaults to tricu.lock beside the declaration,
with --lock FILE as an explicit override. Capture is limited to reached
sources, required executable inputs, receipts, and optional project metadata;
registry credentials and unrelated checkout files stay outside the pack. Paths
are caller-relative; output directories must be new. A failed capture can leave
an incomplete directory without a descriptor.
Copy the pack directory to another machine or isolated working directory and transfer its hash through the channel whose assertions you intend to accept:
INPUTS_HASH=PASTE_THE_PRINTED_HASH
result/bin/tricu provenance verify --pack "$PACK" --pin "$INPUTS_HASH"
# This executes the retained compiler. Select its hash from an independently
# trusted source and authorize only a compiler you approve.
result/bin/tricu provenance rebuild --pack "$PACK" --pin "$INPUTS_HASH" \
--trust-compiler "$COMPILER_MANIFEST" --output "$WORK/fresh-rebuild" \
--compile-timeout 900
verify is read-only and checks the authenticated descriptor and its referenced
closure as data. Its verification scope is that closure; unrelated files
alongside it remain unverified. rebuild verifies first, requires explicit
compiler authorization, and creates new workspace, application and tools
directories. It uses only retained sources and executable inputs. The retained
compiler and pack supply all compilation inputs, independently of the registry,
producer cache, and host’s packaged compiler.
A successful result reports exact native module and receipt identities plus
structural comparison of exported trees, and saves rebuild.txt. The resulting
module is runnable from the new application CAS using arboricx run. An exact
reproduction returns 0; a mismatch reports available structural differences and
returns nonzero. Exact reproduction requires matching module and receipt
identities as well as export structure. A compiler/validation failure also
returns nonzero and may leave partial local artifacts. Retry into a new
directory. Application exports are compared as tree data. The workspace restores
input snapshots; project binding ownership must be established separately. A
project may also declare unused dependencies intentionally absent from the pack.
The reproduction target is the native module. Recreating a distribution bundle’s export selection and metadata requires those settings separately. The pack retains the compiled compiler; preserve its source provenance, host executable, OS environment, and repository history separately as needed. Supply a compatible Haskell host and transfer the pack as files. Registry discovery, HTTP transport, signatures, and remote attestations require separate services. Before distribution, review source/toolchain contents for secrets and licensing requirements, and supply repository license and origin metadata explicitly.
Use trusted, stable directories. Symlinked internal CAS directories and input files are rejected. Keep contents stable throughout each operation. Limits are 128 reached source units, 1 MiB per source/receipt, 64 KiB per descriptor/project file/module manifest, 64 MiB per executable object, and 256 MiB of retained closure bytes. The shared inspection reader additionally bounds retained executable bytes per module. These bounds cover encoded bytes; runtime heap/RSS requires separate controls. Rebuilding executes the selected compiler under the existing host IO model and compiler deadline. OS/network isolation requires external controls. See storage for the identities and validation for the real-library and false-claim acceptance tests.
First published libraries: an offline bundle inspector
The initial reusable libraries expose the following public interfaces:
| Release name | Authored interface | Exports |
|---|---|---|
tricu-base |
lib/packages/base.tri |
append, matchResult, showNumber |
tricu-file-io |
lib/packages/io.tri |
io, bind, readFile, sha256Hex, putStr, abortIO |
arboricx-inspection |
lib/arboricx/inspect.tri |
inspectBundle, renderSummary, describeError |
Their closed executable exports include the required binary, numeric and parser helpers, so consumers can use the closed exports directly. Grant IO permissions separately when executing IO trees. Each release uses the ordinary one-module package profile above.
demos/package-inspector/main.tri is a real
consumer of all three releases. Its tricu.project selects tricu-base as
foundation.tri, tricu-file-io as fileio.tri, and arboricx-inspection as
inspection.tri. Use project resolve/fetch/bind/check above, then compile
that directory using the application store containing the fetched modules; the
registry may be offline. The compiler itself still needs its selected toolchain.
After importing the resulting inspector bundle indexed into APP_STORE, run its
module manifest:
result/bin/tricu arboricx run --store "$APP_STORE" \
--module "$INSPECTOR_MANIFEST" --name main --arg "$INPUT_BUNDLE" \
--io --no-result --allow-read "$INPUT_BUNDLE"
Output is a line-oriented, tab-separated report:
arboricx.inspect.v1
bundle-sha256<TAB>whole-file SHA-256
stored-nodes<TAB>physical record count
export<TAB>name<TAB>root index<TAB>kind<TAB>ABI
The report preserves export order. Fields percent-escape % and bytes outside
printable ASCII, including UTF-8, tabs, newlines and terminal controls. It
hashes the same in-memory byte snapshot it validates. Validation covers every
export and stored node, including unused ones, directly over encoded records.
Exported trees remain inert, with sharing preserved. Malformed bundles and file
permission/read failures produce a nonzero process exit and suppress the success
report. Execution requires only read permission for the input bundle.
inspectBundle returns the library Result protocol: failure is a numeric codec
diagnostic; success is (stored node count, export records), with an empty
Result remainder. An export record is (name bytes, (u32 root bytes, (kind bytes, ABI bytes))). renderSummary requires that validated success payload;
describeError gives a human-readable diagnostic category. Success hides the
internal decoder cursor; failure retains the codec remainder as opaque
diagnostic context. Incremental parsing requires a separate parser interface.
The three listed exports form the complete public interface.
The inspector establishes structural validity and byte identity. Authenticate publishers, verify build provenance, and assess execution safety separately. It reads/parses the whole file in memory; use external process/time/heap limits for untrusted large files. Current formats and supported ABIs follow the underlying native Arboricx reader. The acceptance workflow checks the actual compiled libraries against an independent Haskell decoder, including inspection of the published libraries and the inspector itself.