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. |
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.