Reader requirements
Compatibility
Section titled “Compatibility”These four rules are what make the format survivable. They are normative.
- Readers MUST ignore unknown JSON keys and unknown container entries. New keys and new entries appear in minor revisions. A reader that fails on one breaks on files it was supposed to handle.
formatVersionincreases only on breaking changes. A reader encountering a version greater than it supports MUST refuse with a clear “created with a newer version” error. It MUST NOT guess-read: a partial parse of a format you do not know produces a design that is wrong in ways nobody can see.- Entries marked
"derived": trueare regenerable. Readers and re-writers MAY drop them. document.jsonis the single source of truth. In particular,plan.jsonMUST NOT be imported back into editable state (why).
Security
Section titled “Security”.hoop files are untrusted input — including files your own application
wrote, because you do not know what happened to them between then and now. A
conforming reader enforces all of the following.
Archive limits
Section titled “Archive limits”| Limit | Reference value | Why |
|---|---|---|
| Entry count | ≤ 256 | A project has a handful of entries; thousands means an attack or a bug. |
| Per-entry uncompressed size | ≤ 64 MB | |
| Total uncompressed size | ≤ 256 MB | |
| Compression ratio per entry | ≤ 100:1 | Zip bombs. A 1 MB entry inflating to 1 GB is not a design. |
These are the values the reference implementation uses. A reader MUST enforce caps of this kind; the exact numbers are an implementation choice, and a reader that sets them higher should be able to say why.
Entry names
Section titled “Entry names”Reject any archive containing an entry name that contains a .. component,
begins with /, or contains a NUL byte. Never construct a filesystem path from
an archive entry name without this check — this is the classic Zip Slip, and it
is how an embroidery file overwrites something outside its folder.
Integrity
Section titled “Integrity”- CRC-32 per entry, as the archive is read. This is ZIP’s own check.
- SHA-256 per manifest record, afterwards. Recovery behaviour on failure is
defined in manifest.json: a bad
document.jsonis fatal, a bad derived or reference entry is dropped.
JSON sanity
Section titled “JSON sanity”Parsing succeeding does not make the content safe.
| Check | Reference value |
|---|---|
| Objects per document | ≤ 10 000 |
| Points per path | ≤ 50 000 |
| Palette entries | ≤ 512 |
| Reference images | ≤ 64 |
| Coordinates | Reject non-finite values (NaN, ±∞) |
| Hoop size | Reject absurd dimensions |
Non-finite coordinates deserve particular attention: JSON permits a parser to produce them, geometry code rarely checks for them, and a single NaN propagates through a bounding box into a layout that renders nothing and reports no error.
Numeric parameters SHOULD additionally be clamped into their sensible ranges while decoding rather than validated afterwards — a density of 0.0001 mm is not a file to reject, it is a file to bring back into range.
Images
Section titled “Images”Decode reference images through a hardened decoder with a pixel-count limit. An
image decoder is the largest attack surface in the whole format; it is also the
one part of a .hoop reader that is usually somebody else’s code.
Writing
Section titled “Writing”Writers have far fewer obligations than readers, and the ones they have are in Container. The asymmetry is deliberate: a strict writer and a lenient reader is how formats rot. This one asks for a strict reader and a conservative writer.
Reference implementation
Section titled “Reference implementation”ZipArchive.swift and HoopFile.swift in the StitchPencil source show one
conforming approach, including every limit above and the tests that hold them in
place. They are Swift, but nothing in the format depends on the language — the
container is ZIP and the payload is JSON.
Swift and C# packages are planned. Until they exist, the pages here plus the sample files are the intended starting point, and the description above is complete enough to implement from without reading any Swift.