DisclosureOS documentation · v2 Beta
Documents and custody
Identify the exact document edition, preserve its handling history and check that citations point to the right copy.
Two copies can share a title and still have different pages, redactions or release histories. A document edition identifies the copy being described. A digital artifact identifies the file. Custody actions record how that file was handled; a review records what someone concludes about it.
This guide follows two fictional editions of the same memo. You will check their saved references, trace a page citation and deliberately point it beyond the end of one edition.
Requirements: Node.js 22 and the archival example download. Extract it into its own folder. This exercise is self-contained and uses fictional declarations, not ELDÆON data. It includes JSON records about the memos, but not the original memo files.
1. Run the archival example
Open a terminal in the extracted archival-editions folder and run:
npm install --ignore-scripts
npm startThe archive pins @disclosureos/records and @disclosureos/schema to 2.0.0-beta.4. Its source.json identifies the source commit and retained file digests. The runner prints a full report. These are the fields to read first:
{
"success": true,
"checks": {
"structural": "passed",
"semantic": "passed",
"profile": "not_checked",
"external": "passed"
},
"sourceArtifactIntegrity": "not_checked"
}The supplied JSON snapshots and their connections pass. The original memo files have not been checked. A digest declared inside a record is different from a digest calculated from the source file's bytes.
| File | What it holds |
|---|---|
observation.json | The source inventory: two memo copies and the archive log cited by their metadata. |
entities.json | Edition descriptions, file identities, release declarations, custody actions and a catalog identifier check. |
history.json | A passage cited to edition A and a separate review comparing editions A and B. |
To call the validation APIs yourself, save this as check-editions.mjs in the extracted folder:
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import {
parseArchivalEntities,
parseArchivalClaimHistory,
} from "@disclosureos/records/experimental/v2";
import { evaluateArchivalClaimHistory } from "@disclosureos/schema/experimental/v2";
const read = (name) => readFileSync(name);
const entities = JSON.parse(read("entities.json").toString("utf8"));
const history = JSON.parse(read("history.json").toString("utf8"));
const local = [
parseArchivalEntities(entities),
parseArchivalClaimHistory(history),
];
if (local.some((result) => !result.success)) {
console.error(
JSON.stringify(
local.map((result) => result.issues),
null,
2,
),
);
process.exitCode = 1;
} else {
const documents = new Map(
["observation.json", "entities.json"].map((name) => {
const bytes = read(name);
return [createHash("sha256").update(bytes).digest("hex"), bytes];
}),
);
const result = await evaluateArchivalClaimHistory(history, { documents });
console.log(
JSON.stringify(
{
success: result.success,
checks: result.checks,
sourceArtifactIntegrity: result.sourceArtifactIntegrity,
},
null,
2,
),
);
if (!result.success) {
console.error(JSON.stringify(result.issues, null, 2));
process.exitCode = 1;
}
}Run node check-editions.mjs. It prints the compact result above. Local parsers check structure and local relationships. The evaluator also checks the supplied snapshots, their SHA-256 digests, identities and typed connections. It does not fetch URLs or read the original source files.
2. Follow an edition and its citation
Open entities.json and compare the two source_edition entries:
| Detail | Edition A | Edition B |
|---|---|---|
| Edition ID | edition-a | edition-b |
| Declared title | Memo A | Memo A |
| Source reference | source:memo-a | source:memo-b |
| Declared pages | 3 | 4 |
| Release date | September 10, 2026 | Not supplied |
| Declassification date | Explicitly unknown | Not supplied |
The two artifacts have different declared SHA-256 digests. The shared title does not make them interchangeable. The review cites page 2 of A and page 3 of B; that comparison remains an attributed review with an inconclusive outcome.
The first claim in history.json, passage, quotes edition A. Its editionCitation connects four things:
documentidentifies the exactentities.jsonsnapshot by document ID, schema ID and SHA-256.editionIdselectsedition-ainside that snapshot.artifactidentifiessource:memo-aand its declared file digest.locatorselects page 2 of that edition.
The claim's provenance repeats the same artifact and locator and names the extractor and attributed source. The parser rejects disagreements between those two citations. The supplied-document evaluator can then check the page against the edition's declared page count.
There are two different digests here. document.sha256 identifies the JSON snapshot containing edition metadata. artifact.digest.value identifies the declared source file. Only the snapshot bytes are available in this exercise.
3. Try an out-of-bounds citation
Save this as break-citation.mjs beside the other files. It changes both locators to page 4 of edition A, which declares only three pages. The changes stay in memory.
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
import { evaluateArchivalClaimHistory } from "@disclosureos/schema/experimental/v2";
const read = (name) => readFileSync(name);
const history = JSON.parse(read("history.json").toString("utf8"));
const passage = history.claims.find((claim) => claim.id === "passage");
passage.provenance.locator.page = 4;
passage.editionCitation.locator.page = 4;
const documents = new Map(
["observation.json", "entities.json"].map((name) => {
const bytes = read(name);
return [createHash("sha256").update(bytes).digest("hex"), bytes];
}),
);
const result = await evaluateArchivalClaimHistory(history, { documents });
console.log(
JSON.stringify({ success: result.success, checks: result.checks }, null, 2),
);
console.error(JSON.stringify(result.issues, null, 2));
if (!result.success) process.exitCode = 1;Run node break-citation.mjs. success becomes false, semantic becomes failed, and the process exits with status 1. It prints this issue:
[
{
"code": "ARCHIVE.PAGE_BOUNDS",
"pointer": "/claims/0/editionCitation",
"message": "Citation exceeds the edition's explicitly declared page count.",
"stage": "semantic"
}
]external remains passed: the supplied snapshot bytes match, but the citation is invalid. Page 4 exists in edition B's declaration, which does not make it valid for edition A. Run node check-editions.mjs again to confirm the original files still pass.
This check relies on a single known page count. Unknown or conflicting counts do not supply a reliable bound. A valid page number does not prove that the page contains the quoted passage.
Describe each edition
Use research_entities version 0.2.0 for archival declarations. It includes six archival entity types alongside the four witness and account types. The field reference provides direct entrances to each type and preserves every nested branch.
| Entity | What it describes |
|---|---|
source_edition | One edition's title, authorship, agency, issue date, archive location, identifiers, markings, pages and redactions. |
document_event | A creation, release, classification or declassification event for one edition, with its own date, authority and citation. |
digital_artifact | An exact source or product file, capture metadata, declared hashes, derivation links and custody status. |
digital_custody_action | An action on that file, its sender and recipient, time, location and predecessor. |
external_identifier | A catalog or other identifier scoped to the observation or one edition. |
identifier_check | A dated check of a particular identifier assertion, including the checking agent, method and outcome. |
To assemble your own document:
- Put each source or derived product in the Observation inventory. Preserve its actual access declaration and available digest.
- Set the entity document's
observationRefto the exact observation snapshot. IncludecontextRefswhen declarations reference event or setting snapshots. - Create a separate edition for each copy you need to cite. Bind it to the appropriate source or product and declared SHA-256. Do not invent a digest when file identity is unresolved; retain the unresolved source inventory entry instead.
- Add sourced assertions for metadata, document events and handling history. Run
parseArchivalEntities, then save the JSON and calculate its digest from those exact bytes. - Declare the entity snapshot in an archival claim history's
entityRefs. Build citation and review references from that same snapshot, then supply the document bytes toevaluateArchivalClaimHistory. An emptyclaimslist can check connections without adding a review.
Every field assertion retains its own identity and provenance. known means a value was supplied, not independently verified. Omission, unknown, redacted, approximate and unmapped have different meanings. Keep conflicting assertions separate instead of silently choosing one.
Page counts are positive integers. A redaction percentage is 0–100; a category such as partial does not supply that percentage. Dates retain their precision. In the example, the release date is known while the declassification date and current marking remain unknown. A historical marking does not establish the current status.
Preserve digital custody
In the example, file-a identifies edition A's artifact. Its first custody action, receive-a, declares an unknown predecessor. store-a explicitly follows receive-a. The gap remains visible even though these references pass validation.
Each action identifies one artifact. Its predecessor is another action on that artifact, a declared beginning or an explicit unknown gap. Array order is not the handling sequence. Predecessors and derivation links must be acyclic. Unambiguous sender/recipient declarations and comparable times must agree across a handoff.
Keep derived files distinct from their originals. Additional SHA-256, SHA-512, SHA-1 and MD5 declarations retain their algorithm and scope. The example preserves an MD5 declaration for an unavailable original; that value does not verify either retained edition. Capture metadata is also a declaration, and GPS altitude retains its unit or null when unknown.
A catalog identifier identifies a catalog record, not necessarily the reported event. An identifier check records which assertion was checked, when and how. A reachable URL does not establish an identity match. Physical sample handling belongs to Materials and laboratory.
Cite and review the exact copy
Archival claim history 0.4.0 uses entity documents 0.2.0. Its subject identifies what is being reviewed; editionInputRefs identifies the exact edition passages used as inputs. Citations can locate a page, time range or JSON pointer.
Source statements can retain a reportedArtifactReview. An assessed claim uses artifactReview with explicit inputs, evaluator, method/version, evaluation time and rationale. A review may include credentials, organization, historical review date, findings, report reference, qualitative confidence and manipulation declarations. Keep independent reviews as separate claims. Parsing an attributed “authentic” finding does not perform or endorse that review.
Even a whitespace change to an entity snapshot changes its SHA-256. For a new review, construct references from the new saved bytes; keep earlier reviews bound to the original snapshots. Replacing a digest alone does not re-evaluate a claim. Entity versions are not projected automatically.
A successful archival check leaves source-artifact integrity, scientific interpretation, profile applicability, time normalization and sensor fusion not_checked. It does not establish complete custody, authenticity or redistribution rights. Typed public fields reject extra properties, but free text, names, URLs and extensions still require publication review; validation is not a privacy sanitizer.
Check source provenance
The released-document profile checks selected public source files, release citations and supplied bytes. It is separate from the archival snapshot checks above.
Use its own history and selection. This profile accepts claim-history 0.1.0, not the archival 0.4.0 history. The shared provenance exercise supplies compatible documents and local source bytes. Do not remove archival detail to make its history fit that profile.
Select the sources to check
A released-document selection names the exact historyId and observationId, then a nonempty list of unique source references. Each entry includes release:
- A known release supplies
releasedByandreferencefor the declared authority/publisher and citation. - An unknown release supplies a reason. That preserves missing context but does not satisfy the required release citation.
This profile requires kind: document and access: public. Restricted or withheld sources can be valid elsewhere in the standard. Do not relabel them to make the checklist pass. Public access does not establish permission to redistribute a file.
Run the release-provenance evaluator
Use evaluateReleasedDocuments(history, selection, { assets }) from @disclosureos/schema/experimental/v2. The shared exercise demonstrates this call. Supply original file bytes in an assets map keyed by sourceRef; the evaluator does not fetch source URLs. This map holds source files, whereas the archival evaluator's documents map holds JSON snapshots keyed by SHA-256.
Preserve passage provenance
Current source statements and observation value assertions that directly cite selected sources form the extraction scope. A supplied direct extraction requires a locator and extractor. Author/speaker attribution is recommended here; it is required by the testimony profile. Superseded statements do not determine the current extraction checklist.
No supplied extraction yields not_applicable, which does not mean extraction is exhaustive. This profile does not inspect locator bounds or passage contents. OCR, translations and other derived products need their own processing provenance.
Read the result
| Result | Meaning |
|---|---|
| Missing required metadata | The corresponding requirement fails. |
| Eligible declarations, unavailable bytes | The file check remains not_checked. |
| Supplied bytes differ from the digest | The file check fails. |
| Required declarations and matching nonempty bytes | The selected-file checks pass within their stated scope. |
Titles and URIs are recommended citation aids. Incomplete unselected sources do not penalize the selected documents. Here, external: passed means supplied source bytes match their declared digests. Release authenticity, redistribution rights, locator contents, extraction accuracy, assessment support and scientific eligibility remain not_checked.
Field reference
- Edition and custody fields: inspect edition metadata, custody actions, reviews and source-file selection separately.
- Documents in the showcase: explore the full fictional case's editions and facsimiles. It has different source files from this focused exercise.
- Materials and laboratory: connect physical samples to their custody and analytical results.
Exact schema exports are @disclosureos/records/experimental/v2/entities/schema/0.2.0, @disclosureos/records/experimental/v2/claims/schema/0.4.0 and @disclosureos/schema/experimental/v2/released-documents/schema. The selection URN is urn:disclosureos:experimental:released-document-selection:0.1.0. Schema URNs identify contracts, not downloadable HTTP endpoints.