Liking cljdoc? Tell your friends :D

TOML schemas

Corium accepts hierarchical TOML schema files when corium db create --schema receives a path ending in .toml. The format is a concise authoring layer over Corium's flat attribute model; EDN schema files remain supported.

The same format is the desired input to the proposed corium schema update plan/apply workflow. An update file may be partial: installed attributes absent from it remain unmanaged unless --prune explicitly requests retirement. schema-version versions this file format; it is not a migration sequence. See the schema migration design.

The current parser accepts only the options documented below. Implementing the migration design also extends the normalized schema model and this authoring format with doc and, when attribute protection is enabled, protection. Those properties are design commitments rather than accepted fields in the current binary.

Grouped attributes

The common form groups attributes under a familiar entity-shaped declaration:

schema-version = 1

[[entity]]
name = "person"

[entity.attributes]
id      = { type = "uuid", unique = "identity" }
name    = { type = "string", index = true }
age     = "long"
tags    = { type = "keyword", many = true }
address = { type = "ref", component = true }

[[entity]]
name = "organization"

[entity.attributes]
name      = "string"
employees = { type = "ref", cardinality = "many" }

The first block declares person/id, person/name, person/age, person/tags, and person/address. A string is shorthand for an attribute with that type and cardinality one.

An entity is only an authoring group. It supplies the keyword namespace but does not create a persisted entity type, constrain which attributes may coexist, or constrain the targets of reference attributes.

An entity block may omit its attributes table and be populated by flat declarations. Each entity group name may appear in at most one [[entity]] block.

Flat attributes

Top-level declarations express ungrouped attributes or add attributes to a group without an entity block:

[[attribute]]
name = "created-at"
type = "instant"
index = true

[[attribute]]
group = "audit"
name = "created-by"
type = "ref"

These declare created-at and audit/created-by, respectively. Declaring the same canonical attribute through grouped and flat syntax is an error.

Attribute options

Every detailed declaration requires type. Supported values are boolean, long, double, instant, uuid, keyword, string, bytes, and ref.

The remaining options are:

OptionValuesDefault
manyBoolean cardinality shorthandfalse
cardinality"one" or "many""one"
unique"identity" or "value"unset
indexBooleanfalse
componentBooleanfalse
no-historyBooleanfalse
docString documentation (:db/doc)unset
protectionA [protect.<name>] class, as "protect/<name>"unset

Use only one of many and cardinality on a declaration. Unique attributes receive index coverage whether or not index = true is present.

Group and attribute names are preserved exactly and must also be valid EDN keyword components so the resulting idents remain usable in queries, transactions, and console input. Names cannot start with a digit or contain whitespace, /, :, or EDN delimiter and reader-macro punctuation. TOML quoted keys support EDN-valid names that are not valid bare TOML keys; quoting does not bypass this validation:

[entity.attributes]
"active?" = "boolean"

Protection classes

A protection class names the key that seals values on the attributes assigned to it. The class never holds key material: the database records only the key identity, and each process resolves it through its own keyring, so who may read a protected attribute is a question about key distribution rather than about Corium (see docs/design/encryption.md and ADR-0018).

[protect.pii]
key = "file:/etc/corium/pii.key"
padding = 64
on-missing-key = "redact"

[[entity]]
name = "person"

[entity.attributes]
name = "string"
ssn = { type = "string", protection = "protect/pii" }

A section [protect.<name>] declares the class :protect/<name>. Its options:

OptionValuesDefault
keyKey identity, e.g. "file:/etc/corium/pii.key"required
algorithm"aes-256-gcm-siv""aes-256-gcm-siv"
scope"attribute" or "entity""attribute"
paddingBytes to round plaintext up to, at least 16unset
on-missing-key"redact", "hide", or "error""redact"
legacy-plaintext"redact" or "pass-through""redact"
epochKey epoch new values seal under1

scope chooses what the sealing determinism leaks. Under "attribute" a reader without the key can tell that two entities share a value on that attribute; "entity" also binds the entity, so it leaks only that one entity's value repeated over time. Entity scope is declarable but not yet sealable — a writing peer refuses it rather than binding the wrong subject.

padding rounds plaintext up to a multiple of that many bytes before sealing, which costs storage and removes the length side channel for short, guessable values.

on-missing-key decides what a reader who cannot open a value gets: "redact" binds it in redacted form (structure visible, value not), "hide" drops the datom out of scans entirely, and "error" fails the read. Under all three, an unopenable value never satisfies a constant or a predicate.

Who may read a class is first a question about key distribution, not about Corium: give the key identity to the processes that should hydrate it, and to no others. A peer server or pgwire server serves many principals from one process, so it narrows further — policy names the key ids each principal may hydrate, and by default a guarded server grants none that policy has not named. See docs/operations.md.

Protection cannot be combined with index, unique, or type = "ref": ciphertext order is not value order, so a protected attribute can never appear in the value-ordered indexes, and the schema rejects the combination rather than surprising a range query later. When an application genuinely needs indexed lookup on a protected field, it adds a second, unprotected attribute holding a keyed hash of the value and accepts that leak explicitly.

Creating a database

corium db create people --schema schema.toml

The CLI selects TOML for .toml paths. Other extensions retain the existing EDN behavior, including a single vector of attribute maps or a sequence of bare attribute maps.

Updating an existing database

corium schema update compares the same file with the schema installed in a database. It is read-only by default and is documented in Operations; the model behind it is schema migrations.

corium schema update people --schema schema.toml

Can you improve this documentation? These fine people already did:
Casey Marshall & Claude
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close