Version: 1.2.0 Date: January 2026 Status: Draft
License: This annex is part of the Open Okyline Language Specification and is subject to the same license terms (CC BY-SA 4.0). See the Core Specification for full license details.
This annex defines the internal reference mechanism
for composing and reusing schema fragments within a single document. It
introduces $defs for declaring reusable templates,
$ref for including them, and
$override/$remove for adapting included
structures. This enables DRY (Don’t Repeat Yourself) schema design while
maintaining full control over structural composition.
This annex defines the semantics of:
$defs)$ref)$override / $remove mechanismsIt specifies how Okyline schemas can be composed and extended using internal definitions, promoting reuse and consistency within a single document.
For versioned imports and external schema references, see Annex E — External Imports and Versioning.
Okyline supports schema references to promote reuse, modularity, and consistency within a schema document.
References allow a field or an object definition to reuse an existing
schema fragment identified by a logical name stored in
$defs.
Okyline distinguishes two complementary use cases:
field | $ref).In Okyline, $ref is an inclusion
mechanism: a referenced schema is treated as a base that can be
extended, explicitly overridden or partially removed. This differs
intentionally from JSON Schema, where $ref behaves as a
total substitution.
$defsOkyline reserves the special block $defs as a container
for reusable schema fragments. It is declared at the root
level, alongside $oky.
{
"$oky": {
"person": {
"$ref": "&Address",
"name|@ {2,50}": "Dupond"
}
},
"$defs": {
"Address": {
"street|@ {2,100}": "12 rue du Saule",
"city|@ {2,100}": "Lyon"
}
}
}$defs is declared at the root level,
not inside $oky.$defs is optional but, when present,
MUST contain a map of named schemas.$defs are not
interpreted as JSON properties of validated instances; they are reusable
definitions only.$defs MAY be targeted by
$ref using its name.$defs. Nested paths within $defs entries are
not addressable.$defs supports both object schemas and scalar type
definitions:
{
"$oky": {
"user": {
"email | $ref @": "&Email",
"score | $ref": "&Percentage"
}
},
"$defs": {
"Email|~$Email~ {5,100}": "user@example.com",
"Percentage|(0..100)": 50,
"Address": {
"street|@ {2,100}": "12 rue du Saule",
"city|@ {2,100}": "Lyon"
}
}
}Scalar definitions follow standard Okyline syntax: the key contains the name and constraints, the value is the example.
Within a document, definitions are referenced via:
&Name
Where Name is defined in $defs.
Examples:
&Address
&Email
&Person
Rules:
& denotes the current document’s definition
namespace.& refers to an entry in
$defs.Name does not exist in $defs, the
schema MUST be rejected.Note: For external references to definitions in other schemas, see Annex E.
field | $refA field can use a definition as its type via the
$ref constraint suffix:
{
"$oky": {
"person": {
"address | $ref": "&Address",
"name|@ {2,50}": "Dupond"
}
},
"$defs": {
"Address": {
"street|@ {2,100}": "12 rue du Saule",
"city|@ {2,100}": "Lyon"
}
}
}address) is the JSON property
name.$ref constraint indicates that the type and
value constraints of the field are taken from the target
definition.A property-level $ref MAY target:
If the value associated with field | $ref is a
single-element array containing a reference string, the
field is interpreted as a list whose elements use the referenced
schema:
{
"$oky": {
"company": {
"addresses | $ref": ["&Address"]
}
},
"$defs": {
"Address": {
"street|@ {2,100}": "12 rue du Saule",
"city|@ {2,100}": "Lyon"
}
}
}Semantics:
addresses is an array field.addresses must validate against
&Address.List size constraints can be combined:
"addresses | $ref [1,10]": ["&Address"]Okyline distinguishes two categories of constraints with different behaviors:
These constraints depend on the context of use and are NOT included from the referenced definition:
| Constraint | Description |
|---|---|
@ |
Required |
? |
Nullable |
[min,max] |
List size |
! |
Uniqueness in list |
% |
Default value |
| Label | Field description |
The same Address can be required in Order
but optional in UserProfile.
These constraints define the contract of the type
itself and ARE included from the referenced definition. They
can only be modified via $override:
| Constraint | Description |
|---|---|
# |
Key field(s) for object identity |
{min,max} |
String length |
(min..max) |
Numeric range |
('A','B') |
Enumeration |
~pattern~ |
Regex/format |
(%Compute) |
Computed validation |
Example:
{
"$oky": {
"user": {
"primaryEmail | $ref @": "&Email",
"backupEmail | $ref ?": "&Email"
}
},
"$defs": {
"Email|~$Email~ {5,100}": "user@example.com"
}
}~$Email~ and {5,100} → included from
Email@ vs ? → defined locally per usageExample values are included from the referenced
definition. They can be modified via $override if
needed.
An object schema can include another definition as a
template using a top-level $ref field:
{
"$oky": {
"Person": {
"$ref": "&Address",
"name|@ {2,50}": "Dupond"
}
},
"$defs": {
"Address": {
"street|@ {2,100}": "12 rue du Saule",
"city|@ {2,100}": "Lyon"
}
}
}Semantics:
$ref designates a template to
include.$ref (here name)
are treated as additional fields, provided there is no
name collision.The effective Person schema is conceptually equivalent
to:
{
"street|@ {2,100}": "12 rue du Saule",
"city|@ {2,100}": "Lyon",
"name|@ {2,50}": "Dupond"
}For object-level inclusion:
$ref MUST target an
object definition.For property-level $ref (section D.4), the target MAY be
scalar, object or array.
Object-level $ref is a structural
composition: it merges the fields of one or more templates into
the current object.
A definition eligible for inclusion MUST contain only fields
(structure). Definitions containing conditional rules
($requiredIf, $forbiddenIf,
$appliedIf, etc.) or $compute expressions
cannot be included via object-level
$ref.
If a definition contains conditional rules or computes, using it as
an object-level $ref target MUST cause a schema parsing
error.
When a schema includes a template via $ref:
$override → the schema MUST
be rejected.$override → explicitly allowed
(see section D.7).This rule enforces explicit intent when adapting included fields.
An object schema can include multiple templates by
providing an array to $ref:
{
"$oky": {
"Article": {
"$ref": ["&Auditable", "&Deletable"],
"title|@ {1,200}": "Mon article"
}
},
"$defs": {
"Auditable": {
"createdAt|@ ~$DateTime~": "2025-01-01T00:00:00Z",
"updatedAt|@ ~$DateTime~": "2025-01-01T00:00:00Z"
},
"Deletable": {
"deletedAt|? ~$DateTime~": "2025-01-01T00:00:00Z",
"isDeleted|@": false
}
}
}Semantics:
$keep is used to
resolve the conflict (see D.5.6).$ref take precedence over all
templates.If two templates define a field with the same name and neither
$remove nor $keep is used:
To resolve:
$keep to specify which template’s field to retain
(see D.5.7).$remove to exclude the field from all templates,
then re-add locally.$keep — Resolving Inclusion ConflictsWhen multiple inclusions cause field name collisions,
$keep specifies which template’s version of a field to
retain.
{
"$oky": {
"Combined": {
"$ref": ["&A", "&B"],
"$keep": ["&B.config"]
}
},
"$defs": {
"A": {
"config|@|ConfigName": "Config-name-A",
"name|@": "A"
},
"B": {
"config|@|Config num": 25,
"status|@": "active"
}
}
}$keep is an array of strings in the format
&TemplateName.fieldName.fieldName, the
version from TemplateName should be retained, and versions
from other templates should be discarded.$keep,
the schema MUST be rejected.$keep entries referencing non-existent templates or
fields MUST cause a schema parsing error.$keep is only valid when using multiple inclusions
($ref as array).$keep resolves collisions silently — no error is raised
for fields listed in $keep.$keep and $remove can be used together:
$keep resolves which version to use, $remove
can then exclude it entirely if needed.$removeOkyline provides the $remove directive to
exclude fields from included templates.
{
"$oky": {
"AnonymousPerson": {
"$ref": "&Person",
"$remove": ["email", "ssn"]
}
},
"$defs": {
"Person": {
"name|@ {1,50}": "John",
"age|@ (0..150)": 42,
"email|@ ~$Email~": "john@example.com",
"ssn|@": "123-45-6789"
}
}
}$ref injects all fields from the Person
template.$remove specifies fields to exclude
from the effective schema.Effective AnonymousPerson schema:
{
"name|@ {1,50}": "John",
"age|@ (0..150)": 42
}$remove MUST be an array of field names (strings).$remove:
$remove applies to all included
templates: if multiple $ref targets define the same field,
$remove excludes it regardless of origin.When using multiple inclusions, $remove excludes fields
from all templates. This allows resolving field
collisions by removing the conflicting field and re-adding it
locally:
{
"$oky": {
"Article": {
"$ref": ["&Auditable", "&Deletable"],
"$remove": ["updatedAt"],
"updatedAt|@ ~$DateTime~": "2025-06-15T10:30:00Z",
"title|@": "Mon article"
}
},
"$defs": {
"Auditable": {
"createdAt|@ ~$DateTime~": "2025-01-01T00:00:00Z",
"updatedAt|@ ~$DateTime~": "2025-01-01T00:00:00Z"
},
"Deletable": {
"deletedAt|~$DateTime~": "2025-01-01T00:00:00Z",
"updatedAt|@ ~$DateTime~": "2025-01-01T00:00:00Z"
}
}
}Processing order:
$remove fields → {updatedAt}Auditable, skip updatedAt →
{createdAt}Deletable, skip updatedAt →
{createdAt, deletedAt}{createdAt, deletedAt, updatedAt, title}No collision because $remove excludes the field from all
injections.
$overrideOkyline provides the $override constraint to
redefine a field from an included template.
{
"$oky": {
"Employee": {
"$ref": "&Person",
"name | $override ? {10,20}": "Jean Dupont",
"salary|@ (>=0)": 3000
}
},
"$defs": {
"Person": {
"name|@ {1,50}": "John",
"age|@ (0..150)": 42
}
}
}$ref injects all fields from the Person
template.name | $override ... indicates that:
name is
replaced,Employee.name is defined entirely by
the local constraints after $override.age) remain unchanged.salary) are simply
added.Effective Employee schema:
{
"name|? {10,20}": "Jean Dupont",
"age|@ (0..150)": 42,
"salary|@ (>=0)": 3000
}X | $override ... MUST refer to a field X
that exists in a template referenced by $ref (after
removals). Otherwise, the schema MUST be rejected.$override is present, the included definition for
that field is completely replaced by the local one.$override MAY be combined with any standard field
constraints (@, list bounds, formats, etc.), exactly like a
normal field definition.$override, attempting to redefine an included
field name is an error (see D.5.4).$override applies to value constraints
only. Structural constraints are always defined locally
anyway.For an object that uses $ref, $remove,
$override and local fields, the effective schema is
computed conceptually in the following order:
$ref
to a template and inject all fields.$remove
directives.$override
directives.For "$ref": ["&A", "&B", "&C"]:
$keep)$keep)$override)The $remove directives act as a persistent filter,
removing the named field after each injection.
| Situation | Error |
|---|---|
$remove targets non-existent field |
Schema rejected |
$override targets non-existent field (after
removes) |
Schema rejected |
| Local field collides with included (no override) | Schema rejected |
Two templates define same field (not removed, no
$keep) |
Schema rejected |
| Object-level cycle detected | Schema rejected |
$ref, $override and $remove
apply only to structural fields of an object.
A structural field is a field that is part of the object’s schema after:
$ref inclusion,$remove directives,$override directives,Fields that exist only inside $appliedIf (or
related conditional) branches are not
considered structural fields of the base object.
$override and $remove MAY only target
structural fields included from the template.$override or $remove targets a field
that does not exist structurally in the referenced definition, the
Okyline schema MUST be rejected.$requiredIf*,
$forbiddenIf*, $appliedIf*) declared in a
referenced definition are included by the schema that
uses $ref.$override or $remove (they apply to
structural fields, not the directives themselves).During schema loading, conditional directives are interpreted after structural resolution is complete:
status,
paymentMethod) MUST exist as a structural
field of the object after
$ref/$remove/$override.$requiredIf* /
$forbiddenIf* MUST either:
$appliedIf branch).If a conditional directive references a field that does not exist according to these rules, the schema MUST be rejected.
| Feature | Description |
|---|---|
$defs |
Repository for reusable templates (root level, first level only) |
&Name |
Reference syntax (resolves in $defs) |
field \| $ref |
Reuse a template as the type of a field (scalar, object, or array) |
field \| $ref: ["&Name"] |
Array whose elements use the referenced template |
Object-level $ref |
Include all fields from template(s) — structural composition |
"$ref": ["&A", "&B"] |
Multiple inclusions (collision = error unless $keep or
$remove) |
$keep |
Select which template’s field to retain on collision |
$override |
Adapt an included field — replace its definition |
$remove |
Adapt by excluding an included field |
| Structural constraints | @, ?, [...], !,
%, labels — local, at usage |
| Value constraints | #, {...}, (...),
~...~, etc. — included, adapt via
$override |
| Conditional directives | Included, not modifiable |
| Object-level cycles | Forbidden (detected at load time) |
| Property-level recursion | Allowed |
Note: For external references and versioned imports, see Annex E — External Imports and Versioning.
{
"$okylineVersion": "1.2.0",
"$version": "1.0.0",
"$title": "Order Schema with Internal References",
"$oky": {
"order": {
"$ref": "&Auditable",
"orderId|@ # ~$OrderId~": "ORD-12345678",
"customerEmail | $ref @": "&Email",
"status|@ ($ORDER_STATUS)": "PENDING",
"items | $ref @ [1,100]": ["&OrderItem"],
"shippingAddress | $ref @": "&Address",
"billingAddress | $ref": "&Address",
"total|@ (%OrderTotal)": 100.50,
"$requiredIf status('SHIPPED','DELIVERED')": ["trackingNumber"],
"trackingNumber|{10,50}": "TRACK123456"
}
},
"$defs": {
"Email|~$Email~ {5,100}": "user@example.com",
"Auditable": {
"createdAt|@ ~$DateTime~": "2025-01-01T00:00:00Z",
"updatedAt|@ ~$DateTime~": "2025-01-01T00:00:00Z"
},
"Address": {
"street|@ {5,100}": "123 Main Street",
"city|@ {2,50}": "Paris",
"postalCode|@ {5,10}": "75001",
"country|@ {2}": "FR"
},
"OrderItem": {
"sku|@ # {5,20}": "SKU-12345",
"name|@ {2,200}": "Product Name",
"quantity|@ (1..1000)": 1,
"unitPrice|@ (>0)": 100.50
}
},
"$format": {
"OrderId": "^ORD-[0-9]{8}$"
},
"$compute": {
"ItemTotal": "unitPrice * quantity",
"OrderTotal": "total == sum(items, %ItemTotal)"
},
"$nomenclature": {
"ORDER_STATUS": "PENDING,CONFIRMED,SHIPPED,DELIVERED,CANCELLED",
"PAYMENT_METHOD": "CARD,PAYPAL,BANK_TRANSFER"
}
}End of Annex D — Internal Schema References (Normative)