Package format
How an ODS package holds a document, the inputs of each run and a manifest that lets anyone check the files are unchanged.
The launch format is written down, and the viewer reads it. A producer does not yet write every field of the manifest. The format can change until the first release.
Status: draft. This is format 1; it is fixed when ODS launches.
Reference implementation: @opencycle/ods-package (writePackage, readPackage).
An ODS package holds one ODS document and the files that let a reader check it: the full inputs of each run, the terrain those inputs refer to, and a manifest that lists every file with its SHA-256. A reader can prove that the files are the ones the manifest lists, unchanged since the package was written. A package does not prove who wrote it: packages are not signed yet.
The words MUST, MUST NOT, SHOULD and MAY are used as in RFC 2119.
1. Container
- A package is a ZIP archive. Paths use
/and are relative to the archive root. - File name:
<name>.odspkg. A reader MUST NOT depend on the name. - A media type will be registered with IANA (proposed:
application/vnd.ods.package+zip).
2. Encodings
- Text. Every JSON file is UTF-8.
- JSON values. A writer serializes each value as JSON first: a date becomes its ISO 8601 UTC string, an id becomes its string. The package holds only JSON values (objects, arrays, strings, numbers, true, false, null).
- Canonical JSON is RFC 8785, as canonical-json.md sets out, with shared test vectors.
- Hash. SHA-256 of the file's bytes, as 64 lowercase hex characters.
3. Files
| Path | Required | Holds |
|---|---|---|
manifest.json |
yes | The manifest (§4). Not listed in its own files. |
ods.json |
yes | The ODS document (§6), as canonical JSON. |
snapshots/<hash>.json |
no | A run's full inputs. <hash> is the hash the run recorded. |
bundles/<hash>.json |
no | The context a run was given. <hash> as above. |
terrain/<hash>.json |
no | A terrain grid that a snapshot or bundle refers to as {"$terrain": "<hash>"}. |
outputs/<name> |
no | A run output kept as a file, for example one CSV of 1-minute rows per monitor. |
| any other path | no | A file the document names by a File record (files[].path), such as an open grid grids/<sha256>.json or a large value values/<sha256>.json. The manifest lists it under outputs. |
A file under snapshots/, bundles/ or terrain/ is content-addressed: its
hash MUST equal its name. A writer that has a file whose content does not
match the recorded hash still includes it, and the manifest says so (§4).
4. Manifest
| Field | Type | Holds |
|---|---|---|
format |
string | "ods-package". |
formatVersion |
string | "1". |
odsVersion |
string | The ODS release the document follows, for example "1.0.0". Each schema is named by its $id on opendevelopmentstandards.org under that release. |
created |
string | When the package was written (ISO 8601). |
producer |
object | name, and optional engine: the versions that ran. |
subject |
object | What the package is about: type, id and optional name. type is "Trace" for a run, with optional flowVersion and flowUpdatedAt (flowChangedSinceRun is true when both times are present and differ), or "Document" for a document with no run, such as a layout export: its id is the document's, and it has no snapshots. |
sharing |
string | Where the contents may go (§7): open, approved or local. |
personalData |
array | The personal information classes the document holds (§7). Empty when it holds none. |
snapshots |
array | One entry per run snapshot (below). Empty for a Document package. |
terrain |
array | Present only when a snapshot or bundle refers to terrain. |
bundles |
array | Present only when the package holds context bundles. |
outputs |
array | Present only when the package holds output files: their paths. |
files |
array | Every file except manifest.json: path, bytes, sha256. |
integrity |
object | algorithm ("sha256") and a description. |
packageHash |
string | The hash of the canonical JSON of this manifest without packageHash. |
An entry in snapshots, bundles or terrain has hash, included and,
when included, verified (the content matched the hash when written).
Entries in snapshots and bundles also name the run: traceId.
An optional array or object is left out when it is empty, except
personalData, which is always present.
5. Verification
A reader MUST run these checks before it shows the content:
- Each file in
filesis present, with the listedbytesandsha256. packageHashequals the hash of the manifest's canonical JSON withoutpackageHash.- Each included content-addressed file hashes to its name.
- A file that
filesdoes not list is a warning.
Result:
- verified: every check passed.
- incomplete: no check failed, but at least one warned (an unlisted
file, a blob not included, an unknown
formatVersion). - failed: a file is missing, changed or has the wrong size, the manifest changed, the package hash is missing, or a blob does not match its name.
Notes about the content never change the result. Examples: a run whose snapshot the manifest does not list; a flow that changed after the run.
A reader SHOULD refuse an archive above a size limit it states, before it unzips it (the reference reader refuses 1 GiB unzipped).
6. Document
ods.json is one ODS Document:
| Key | Holds |
|---|---|
odsVersion |
As in the manifest. |
generatedAt |
When the document was made. |
producer |
The software that made the document. |
sharing |
As in the manifest. |
risk |
frameworks: the frameworks the runs judged against, with the controls the runs had to meet. |
context |
What the runs evaluated, and who took part (stakeholders). |
environment |
The conditions. Left out when not filled. |
methodology |
flows: each flow the runs used, once. traces: the subject trace first, then the runs its flow steps started. algorithms, evidence, assumptions: the methods the runs used, the evidence for them and the readings the producer chose. |
files |
The files that records point at. A trace names its snapshot as one of them, at snapshots/<hash>.json. |
The schema is core/Document.json. A document with no odsVersion at its
root was written before ODS had releases: a reader checks its hashes and
does not read its records as ODS records.
Sealed steps
A step that runs a closed engine is sealed. Its trace keeps the step's full inputs and outputs and names the method it claims to follow (an Algorithm, with version and publisher), but not how the engine works. A package MAY carry the Evidence that the Algorithm cites, such as a conformance report.
7. Sharing
A document can hold personal information, such as the address of a
residence. The manifest's sharing says where the contents may go:
open: anywhere.approved: only to services the reader approves.local: nowhere; the contents stay on the reader's device.
A reader, and any agent or service connector it offers, MUST follow
sharing, and MUST show what it will send before it sends anything. The
writer fills personalData from the classes the schemas mark, so a writer
cannot leave it out by mistake.
8. Conformance
- A writer conforms when every package it writes passes §5 with the
result verified, given complete inputs, and fills
sharingandpersonalData. - A reader conforms when it runs §5 in full, reports the result and the failed checks, and follows §7.