k0smos Data Exchange Package Specification
This document defines the public k0smos.data_exchange format version 1. It is
the interoperability contract for content moved between k0smos tenants.
Transport forms
Content-only packages may be a single UTF-8 JSON document with extension
.k0sdata.json. Packages carrying binary media should use the ZIP transport
with extension .k0sdata:
manifest.json
checksums.json
signature.json # optional
entities/{entity-code}.json
media/{sha256}.{extension} # optional
YAML and CSV are not format-v1 transports. Readers detect ZIP by its magic bytes, not by the uploaded filename.
Canonical JSON envelope
{
"format": "k0smos.data_exchange",
"format_version": 1,
"generator": "k0smos/0.21.0",
"generated_at": "2026-07-20T00:00:00+02:00",
"locales": ["en", "it"],
"entities": {
"widget": {
"schema_version": 2,
"records": [{"key": "home|en|Hero|10"}]
}
},
"checksums": {
"widget": "<sha256 of the canonical entity document>"
}
}
Object keys are recursively sorted as strings. List order is retained. JSON is encoded with unescaped Unicode and slashes; exports sort records by logical key. Checksums are lowercase SHA-256 hex strings. The reader requires an exact checksum index: missing and orphan checksum entries are rejected.
ZIP manifest.json declares every entity file, schema version and record count.
checksums.json must exactly cover every declared entity and media file. Any
undeclared archive entry, duplicate entry, traversal path, oversized entry,
excessive compression ratio or checksum/count mismatch rejects the package.
Logical identity and references
Tenant-local integer IDs must never be exported. Current entity identities and dependency order are:
| Order | Entity | Logical identity |
|---|---|---|
| 5 | app_setting |
allow-listed setting key |
| 10 | widget |
hook, locale, title, sort order; fixture packs may declare a bounded insert-only match strategy |
| 20 | page |
type, slug, locale |
| 30 | blog |
base slug; localized content is stored in translations{locale} |
| 35 | tax_rate |
tax code |
| 40 | category |
slug; parent reference is a category slug |
| 50 | product |
SKU; category and tax references are slug/code |
| 60 | product_variant |
product SKU plus variant SKU |
| 70 | product_image |
product SKU plus deterministic per-product slot |
The core app_setting provider is deny-by-default. Format v1 currently allows
the reviewed public identity key company.contact_email and the explicit
social-platform keys declared by PortableAppSettingDataExchangeProvider; it
does not accept arbitrary company.* or social.* prefixes.
app_setting schema v2 additionally accepts match_strategy: "empty_as_missing" only for company.contact_email and only changes
insert-missing behavior: a blank schema placeholder is filled, while a
non-empty destination value is preserved. All other records use
logical_key.
Generic cross-record references use {"$ref":"entity:logical-key"} when a
provider declares them. Media references use a checksum handle and are rebound
to a destination Media row; the original tenant Media ID never travels.
Media
JSON media entries contain metadata plus content_base64; ZIP media entries
contain metadata in the manifest and raw bytes under media/. The map key and
file checksum must equal the SHA-256 of the raw bytes. Each binary is limited to
16 MiB and the complete ZIP to 128 MiB. Only image and PDF media are accepted by
the Media gateway. Imports deduplicate by checksum, preserve visibility, rewrite
declared IDs/URLs/embedded /media/{id} references, and clean newly-created
media when the owning import aborts. Successful import/export packages are
archived privately through Media when that module is active.
Signatures
Signatures are optional Ed25519 detached signatures. JSON signs the canonical
envelope without its signature member. ZIP signs the canonical object
{"manifest": ..., "checksums": ...} and stores the result in
signature.json:
{"algorithm":"Ed25519","key_id":"deployment-key","signature":"<base64>"}
Readers reject malformed, invalid or untrusted signatures. Deployments may require a trusted signature. Signing secrets are environment-only and never stored in tenant settings, packages, logs or queue payloads:
K0SMOS_DATA_EXCHANGE_SIGNING_KEY_IDK0SMOS_DATA_EXCHANGE_SIGNING_SECRET_KEY(base64 Ed25519 secret key)K0SMOS_DATA_EXCHANGE_TRUSTED_PUBLIC_KEYS(JSON key-id → base64 public key)K0SMOS_DATA_EXCHANGE_REQUIRE_SIGNATURE(true/false)
Run history records signature status and key ID, never key material.
Import semantics
Modes are validate_only, preview/dry-run, insert_missing, update and
replace. insert_missing is the default. Conflict policies are preserve,
overwrite and fail. Preview retains the selected write mode and reports the
exact creates, updates, deletes, skips and conflicts while rolling back every
write.
replace is authoritative only for entity types present in the package and for
the selected locales. It upserts in dependency order, then deletes missing rows
in reverse order in one package transaction. It requires data_exchange.manage
and confirmation of the checksum returned by an exact-file preview. Import-locked
catalog rows, referenced parents/tax rates and out-of-scope locales remain
protected.
Rich-text fields are validated before providers run. Active browser content such as scripts, event attributes, executable URL schemes and unsafe CSS is rejected. Diagnostics and durable reports are bounded and content-redacted.
Translation
Descriptors opt fields into plain, rich-HTML or GrapesJS translation. Stable
keys, URLs, attributes, CSS, scripts, placeholders, SKU, currency and topology
are never translated. Locale-on-record entities and nested
translations{locale} catalogs are supported. Existing human targets without
machine provenance are preserved unless explicit retranslation is requested.
Provenance records source checksum/locale, target locale, provider/model,
timestamp and estimated character/token usage; credentials and full prompts are
never persisted.