Contribution Bundle v0 — Format Specification
Status: implemented (schema/format only) See: DR-0023 Local/Offline and Hosted Submission Model, contribution-bundle-v0-format-spec roadmap
What this document covers
A contribution bundle is a portable scientific contribution package. It carries selected scientific payloads, source-instance metadata, exporter metadata, submission metadata, bundle-local references, and integrity metadata in a format that can later be validated and ingested by a hosted TCKDB instance.
This document defines bundle v0 (bundle_version == "0.1") and the
schemas implemented in
backend/app/schemas/workflows/contribution_bundle.py.
What v0 is
- A schema/format milestone only.
- Re-uses existing thermo and kinetics upload schemas as the bundle's scientific payload, so nested validation runs through the same code paths as a normal API upload.
- Validatable without database access.
What v0 is not
- v0 does not export records from any database.
- v0 does not import into any hosted instance.
- v0 does not create submissions, jobs, or moderation events.
- v0 does not support raw database synchronization between instances.
- v0 does not ship artifact packaging — only the manifest shape.
- v0 does not support full network bundles, arbitrary graph export, or mixed thermo+kinetics bundles.
- v0 does not introduce any FastAPI route, service, or DB write.
Supported v0 bundle families
Allowed bundle_kind values:
| Value | Meaning |
|---|---|
thermo |
Bundle carries thermo upload-equivalent payloads only. |
kinetics |
Bundle carries kinetics upload-equivalent payloads only. |
Future families intentionally not allowed in v0 (to avoid shipping bundles no exporter or importer is actually validating yet):
statmechtransportnetworkmixedcomputed_reaction
Top-level shape
{
"bundle_format": "tckdb-contribution-bundle",
"bundle_version": "0.1",
"bundle_kind": "thermo",
"created_at": "2026-04-25T00:00:00Z",
"source_instance": { "...": "..." },
"exporter": { "...": "..." },
"submission": { "...": "..." },
"records": { "thermo_uploads": [], "kinetics_uploads": [] },
"local_refs": { "species:ethanol": { "record_type": "species", "label": "ethanol" } },
"manifest": { "sha256": null, "files": [] }
}
bundle_format
Must be the literal string tckdb-contribution-bundle.
bundle_version
Must be the literal string 0.1 for v0.
bundle_kind
One of thermo | kinetics.
created_at
ISO-8601 UTC timestamp (Pydantic datetime).
source_instance
Identifies the TCKDB instance that produced the bundle.
| Field | Required | Notes |
|---|---|---|
instance_kind |
yes | local or lab_server. hosted is not allowed in v0. |
instance_name |
yes | Free-text label, e.g. calvin-laptop or pi-rmgteam-server. |
schema_version |
yes | The Alembic revision the local DB is at, e.g. d861dfd60891. |
software_version |
no | Optional TCKDB software version label. |
created_by_local_user |
no | Optional local username/label. |
notes |
no | Free-text. |
exporter
Provenance-only metadata about who produced the bundle. This is not the hosted actor identity. Hosted actor identity comes from hosted authentication during the future import milestone.
| Field | Required | Notes |
|---|---|---|
local_user_label |
yes | Local label such as calvin. |
orcid |
no | ORCID iD if known. Not validated for format in v0. |
affiliation |
no | Free-text. |
email |
no | Free-text contact. |
notes |
no | Free-text. |
submission
Bundle-level submission metadata. The hosted instance maps this to its own
submission row at import time; the bundle does not create submissions.
| Field | Required | Notes |
|---|---|---|
title |
yes | Short title for the contribution. |
summary |
yes | One-paragraph summary. |
source_kind |
yes | Must be local_bundle in v0. |
rights |
no | Deposit-time license agreement for every record in the bundle; see below. |
submission.rights
The rights object is the wire fragment tckdb_schemas.rights.DepositRights,
the same one every direct upload request carries:
"rights": {
"license": "CC-BY-4.0",
"depositor_attests_right_to_license": true,
"source_terms": null
}
| Field | Required | Notes |
|---|---|---|
license |
yes | SPDX identifier (1–64 characters). A hosted release compares it with its own data_license by exact, case-insensitive match; there is no compatibility lattice. |
depositor_attests_right_to_license |
yes | Must be literally true. A false fails validation — the bundle cannot carry a stored "no"; it omits the object instead. |
source_terms |
no | Citation or quotation of the terms the records were taken under, for records that are not the depositor's own work. |
On hosted import the fragment is recorded as a depositor_agreement rights
attestation on the submission, attested by the authenticated submitter
— never by the local exporter label. A bundle without rights imports
normally, but its records cannot be selected into a dataset release until a
curator records a basis for the submission.
scripts/export_contribution_bundle.py fills the object from the standing
rights attestation of the submission that deposited the exported records on
the source instance. If the records were deposited under different licenses
the export refuses (one bundle, one agreement); if only some of the records
are attested the export also refuses, rather than extending one deposit's
agreement to records nobody licensed; only if nothing at all is attested is
the object omitted — the exporter never invents consent.
Note on
local_bundleand the database enum. Thesubmission.source_kindfield on the bundle uses a format-level enum (BundleSubmissionSourceKind). The database enumSubmissionSourceKindinbackend/app/db/models/common.pydoes not yet includelocal_bundle. Adding it is deferred to the hosted-import milestone — the bundle format only states "this submission came from a local bundle", and the hosted importer will be responsible for translating that to its own submission machinery.
records
Container for the upload-equivalent scientific payloads. Reuses the existing workflow upload schemas directly:
thermo_uploads: list[ThermoUploadRequest]kinetics_uploads: list[KineticsUploadRequest]
Family rules for v0:
- A
thermobundle must contain ≥ 1thermo_uploadsentry and must not contain anykinetics_uploads. - A
kineticsbundle must contain ≥ 1kinetics_uploadsentry and must not contain anythermo_uploads. - Mixed bundles are explicitly rejected.
local_refs
A map from a bundle-local reference key to a small descriptor.
Local refs are not hosted IDs and not raw DB primary keys. They exist so that future bundle versions can name records inside the bundle (e.g. for cross-payload references) without claiming any hosted identity.
Local ref key rules
A local ref key has the form <namespace>:<label>:
- Namespace: lowercase ASCII, may contain
_, must start with a letter. - Label: alphanumeric plus
_,-,.. Must start with alphanumeric. - Purely numeric labels (
species:123) are rejected — they look like raw database primary keys and that is exactly what bundles must not carry as canonical identity.
Recommended namespaces (informational, validated as record_type):
species:ethanol
species_entry:ethanol-singlet
reaction:h_abstraction_001
transition_state:ts_h_abstraction_001
calculation:sp_001
thermo:ethanol_nasa_001
kinetics:h_abstraction_rate_001
literature:zhang_2024_jpca
Keys are unique by Python dict semantics — duplicate keys in the source JSON collapse to the last value, so the schema validates the deduped map.
Annotation-only in v0
In v0, local_refs is an annotation map, not a cross-reference graph.
It may describe records, labels, or local identities that help a future
exporter/importer reason about the bundle, but v0 scientific payloads
(ThermoUploadRequest, KineticsUploadRequest) do not yet cite local
refs.
The v0 schema therefore validates only:
- local-ref key shape (namespaced
<namespace>:<label>) - local-ref
record_type - rejection of purely numeric (raw-DB-PK shape) labels
- overall map shape
The v0 schema does not enforce coverage between local_refs and
records — having a thermo:* ref does not prove it refers to a
specific ThermoUploadRequest, because nothing in the upload payload
binds to it yet.
Promoting local_refs to a first-class cross-reference is deferred to a
future bundle version, where scientific payloads can explicitly cite
local refs and the hosted importer can define a deterministic local-ref
contract for import preview/diff.
manifest
Integrity metadata for any external artifacts shipped alongside the bundle. Artifact packaging is not implemented in v0; the manifest just defines the shape so future artifact bundling has a place to land.
| Field | Required | Notes |
|---|---|---|
sha256 |
no | Optional 64-char lowercase-hex covering hash. |
files |
yes (may be []) |
List of BundleManifestFile entries. |
created_by_tool |
no | Free-text. |
notes |
no | Free-text. |
manifest.files[*]
| Field | Required | Notes |
|---|---|---|
path |
yes | Path inside the bundle. Must be unique across files. |
sha256 |
yes | 64-char lowercase-hex SHA-256. |
size_bytes |
no | Non-negative integer. |
content_type |
no | Free-text MIME or label. |
role |
no | Free-text role hint (e.g. output_log, input_geometry). |
A bundle without external artifacts can use:
{ "sha256": null, "files": [] }
Validation rules implemented in v0
bundle_format == "tckdb-contribution-bundle".bundle_version == "0.1".bundle_kind∈{thermo, kinetics}.- All required top-level metadata is present (
source_instance,exporter,submission,records,manifest,created_at). - The selected bundle kind has at least one record of the matching family.
- Mixed thermo+kinetics record sets are rejected.
manifest.filespaths are unique.local_refskeys match the namespaced format rule above.- Purely numeric local-ref labels (raw-DB-PK shape) are rejected.
extra="forbid"on every bundle schema rejects unknown top-level fields, so a future field cannot accidentally slip in unnoticed.- Nested
ThermoUploadRequest/KineticsUploadRequestpayloads run their full upload-time validators (e.g. thermo must carry actual scientific content, kineticsa_unitsmust match molecularity).
DB-backed scientific resolution (species lookup, level-of-theory resolution, deduplication, etc.) is not done in v0.
Examples
Future extension points (not in v0)
- Adding
local_bundleto the DBSubmissionSourceKindenum, when the hosted-import milestone needs to persist bundle-origin submissions. - Wider
bundle_kindsupport (statmech,transport,network, etc.). - Cross-payload references that consume
local_refskeys directly inside scientific upload payloads. - Real artifact packaging (tarball/zip layout, content-addressed storage).
- Hosted dry-run, hosted import, and hosted submission lifecycle.
- Frontend UX for bundle authoring and review.