# Validating records

Open Print treats every JSON file as untrusted input, including files produced
by Open Print itself and later read back from disk. A valid-looking signature
is not a substitute for checking the shape of the record first.

The package exposes one validator for each public v1 record:

```ts
import {
  validateV1CertificateRecord,
  validateV1ArchiveRecord,
  validateV1IssuerRecord,
  validateV1RegistryCheckpointRecord,
  validateV1RegistryRecords,
} from "@open-print/core";

const result = validateV1CertificateRecord(value);

if (!result.valid) {
  for (const issue of result.issues) {
    console.error(issue.path, issue.message);
  }
}
```

The validators return every problem they find in one pass. `path` is a JSON
Pointer, so `/claims/id` means the `id` field inside `claims`, while
`/0/signature` means the signature on the first registry event. Checkpoints
have their own validator because they can arrive independently of a registry.
Complete issuer archives have a validator as well; `verifyV1Archive` then checks
their digest and the relationships between all nested records.

There are assertion versions for code that cannot continue with a bad record:

```ts
import { assertV1CertificateRecord } from "@open-print/core";

assertV1CertificateRecord(value);
// TypeScript now treats value as V1Certificate.
```

An assertion throws `OpenPrintValidationError`. Its `issues` property keeps the
same machine-readable paths and keywords returned by the regular validator.

## Unknown fields

The v1 schemas are strict. An extra field is rejected even when every required
field is present. This matters because silently ignoring a field can lead two
implementations to disagree about what was signed.

It also keeps the project's exclusions unambiguous. Adding a field named
`tokenId`, for example, does not turn it into part of an Open Print
certificate; validation rejects it.

The same rule keeps private collector labels local. A
`/claims/collector/privateLabel` field is rejected. Public collector names are
limited to 120 characters, and `null` represents a certificate with no public
collector name.

The v1 claims notice is also a fixed schema value. A certificate cannot replace
the assurance boundary with wording that describes the record as proof of
ownership or financial value.

## Verification

`verifyV1Certificate` and `verifyV1Registry` run schema validation before
reading or checking a record. Malformed input returns an invalid verification
report with useful paths instead of throwing an unexpected JavaScript error.

The included verification commands decode UTF-8 strictly and reject JSON files
larger than 64 MiB before parsing. The library APIs also accept values already
in memory, so a server must enforce its own request-size, nesting, timeout, and
rate limits before calling them.

The schemas remain the source of truth. They are published in `schemas/v1`,
while `conformance/v1/invalid-cases.json` records representative changes that
every other implementation should reject.
