Skip to content

fix(schema): put the spec version at the manifest root and stop rejecting real TDFs - #70

Draft
pflynn-virtru wants to merge 1 commit into
mainfrom
fix/manifest-schema-align-with-implementations
Draft

pflynn-virtru wants to merge 1 commit into
mainfrom
fix/manifest-schema-align-with-implementations

Conversation

@pflynn-virtru

Copy link
Copy Markdown
Member

What

json-schema/schema.json declared tdf_spec_version under payload and listed it in payload.required. manifest.md has always documented the spec version as a top-level field, and payload.md never listed it at all — so the schema was the outlier of the three. This aligns the schema with the documentation and with what implementations actually write.

  • schemaVersion is declared at the root as the canonical name. Nothing declared it before, in any copy of this schema, though it is the name every current SDK writes.
  • tdf_spec_version is declared at the root as deprecated, and kept under payload as deprecated, typed to admit null. Both positions occur in archival files — the root because writers followed manifest.md, payload because they followed this schema's error — so readers have to take either. A new Spec Version Naming section in manifest.md states the reader/writer rules: readers MUST accept both names in both positions and prefer schemaVersion; writers MUST emit only schemaVersion.
  • mimeType leaves payload.required. Platform dropped it in Nov 2024, one day after this schema was forked from it; the fix never came back across.
  • policyBinding accepts a bare string, keyAccess.type gains ec-wrapped and hybrid-wrapped, and ephemeralPublicKey is declared. These have been in platform's copy since 2025 and describe key access objects that exist today.
  • rootSignature.alg is pinned to HS256. The root signature covers the aggregate hash, which AES-GCM never processes, so a GMAC root has no authentication tag to read back out.
  • rootSignature.sig, segments[].hash and policyBinding.hash now state that the pre-4.3.0 hex spelling MUST be accepted indefinitely, and that a reader should tell the two apart by decoded length rather than by the spec version, which nothing authenticates. Requested in fix(sdk): align policy binding encoding with spec, keep legacy compat platform#3597.

Why

The required entry made the normative schema reject every TDF anyone writes. Against the eight golden containers in opentdf/tests it failed 8/8, each time on 'tdf_spec_version' is a required property. With this change it passes 7, matching what platform's copy of this schema accepts. The eighth omits method.isStreamable and fails on platform too; that constraint is left alone here so the two copies stay in step.

The payload placement was not merely unused — it propagated. A writer built from this schema emitted the key under payload, and readers that trusted the documented root position decoded no spec version at all.

Notes

  • Not in this PR: $id still reads https://example.com/manifest.schema.json. Giving the schema a real identifier is a separate decision, still open, and it travels with chore(sdk): give the bundled manifest schemas real $id values platform#4070 rather than riding along here.
  • Companion PR: fix(sdk): read the TDF spec version at the manifest root, and stop trusting it platform#4060 makes the Go SDK read the root placement and stops the reader from trusting the field to pick an integrity-digest encoding. It also adds a CI job that diffs platform's bundled copy of this schema against this one, so the drift this PR is cleaning up cannot recur silently.
  • This is a relaxation in every direction that matters — no manifest that validated before fails now. The one added constraint, rootSignature.alg: ["HS256"], matches what platform has enforced for some time; a cross-SDK run is checking that no SDK writes a GMAC root.

🤖 Generated with Claude Code

…ting real TDFs

json-schema/schema.json declared tdf_spec_version under payload and listed
it in payload.required. manifest.md has always documented the spec version
as a top-level field, and payload.md never listed it at all, so the schema
was the outlier of the three.

The required entry made the normative schema reject every TDF anyone
writes: against the eight golden containers in opentdf/tests it failed 8/8,
each time on `'tdf_spec_version' is a required property`. It now passes 7,
matching what platform's copy of this schema accepts. The eighth omits
method.isStreamable and fails on platform too; that constraint is left
alone here so the two copies stay in step.

Changes:

* schemaVersion is declared at the root as the canonical name. Nothing
  declared it before, in any copy of this schema, though it is the name
  every current SDK writes.
* tdf_spec_version is declared at the root as deprecated, and kept under
  payload as deprecated, typed to admit null. Both positions occur in
  archival files -- the root because writers followed manifest.md, payload
  because they followed this schema's error -- so readers have to take
  either. A new "Spec Version Naming" section in manifest.md states the
  reader/writer rules.
* mimeType leaves payload.required. Platform dropped it in Nov 2024, one
  day after this schema was forked from it; the fix never came back across.
* policyBinding accepts a bare string, keyAccess.type gains ec-wrapped and
  hybrid-wrapped, and ephemeralPublicKey is declared. These have been in
  platform's copy since 2025 and describe key access objects that exist.
* rootSignature.alg is pinned to HS256.
* rootSignature.sig, segments[].hash and policyBinding.hash now state that
  the pre-4.3.0 hex spelling MUST be accepted indefinitely, and that a
  reader should tell the two apart by decoded length rather than by the
  spec version, which nothing authenticates. Requested in platform#3597.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Paul Flynn <pflynn-virtru@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant