ObjectStackObjectStack

Validation rules — reject bad records

Default validation behavior, required properties, and constraints for each ObjectStack field type

Every ObjectStack field type has built-in validation behavior that runs automatically at the schema level. This reference documents the default constraints, required properties, and validation semantics for each field type.

Source: packages/spec/src/data/field.zod.ts
Import: import { FieldSchema, FieldType } from '@objectstack/spec/data'


Universal Field Constraints

These properties apply to all field types and are validated by the base FieldSchema:

PropertyTypeDefaultValidation Behavior
namestring—Must match ^[a-z_][a-z0-9_]*$ (snake_case)
labelstring—Human-readable display name
typeFieldType—Must be a member of the FieldType enum
requiredbooleanfalseRejects null/undefined at runtime when true
uniquebooleanfalseEnforces database-level uniqueness constraint
multiplebooleanfalseStores value as array (applicable for select, lookup, file, image)
hiddenbooleanfalseExcluded from default UI rendering
readonlybooleanfalseBlocks user edits in UI forms; server-enforced on both create and update — a non-system write to the field is silently dropped from the payload (system-context writes such as import/seed/migration are exempt)
sortablebooleantrueWhether field appears in list view sort options
externalIdbooleanfalseMarks field as external ID for upsert operations
trackHistoryboolean—Render this field's value changes as entries on the record activity timeline
visibleWhenstring | Expression—CEL predicate; field is shown only when TRUE
readonlyWhenstring | Expression—CEL predicate; field is read-only when TRUE
requiredWhenstring | Expression—CEL predicate; field is required when TRUE

Text Types

text

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Rejects values exceeding character count
minLengthnumber—Rejects values below character count
formatstring—Not validated. No write-time check reads it — a regex here is accepted and ignored. On text it is a display hint only (a small set of words such as phone, email or url promote the cell to a richer renderer; see the Field Type Gallery). To constrain the value's shape, use the email / url / phone field type or a format validation rule
valueDomain'iana_time_zone' | 'iso_4217_currency' | 'iso_3166_alpha2'—Constrains the written value to a published standard — an IANA time zone (judged by the Intl.DateTimeFormat probe, so UTC and Asia/Kolkata are members and Europe/Munich is not), an ISO 4217 currency code or an ISO 3166-1 alpha-2 country code (both exact uppercase). Membership, not shape: a pattern such as ^[A-Z]{2}$ admits ZZ; the domain does not. The same closed vocabulary and the same membership test as a settings specifier's valueDomain; a non-member is refused on the write path with the field error code value_domain. text only — declaring it on any other type is refused at parse.

Default constraints: None. Unbounded text unless maxLength is set.

textarea

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Rejects values exceeding character count
minLengthnumber—Rejects values below character count

Default constraints: None. Multi-line text with no length limit by default.

email

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Rejects values exceeding character count
minLengthnumber—Rejects values below character count

Default constraints: Must contain an @ and a domain with a dot — a lightweight pattern check, not full RFC 5322 validation. The check is keyed on type: 'email' itself and has nothing to configure; a field-level format key is not read.

url

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Rejects values exceeding character count
minLengthnumber—Rejects values below character count

Default constraints: Must be a valid URL with protocol prefix. The check is keyed on type: 'url' itself and has nothing to configure; a field-level format key is not read.

phone

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Rejects values exceeding character count
minLengthnumber—Rejects values below character count

Default constraints: Accepts digits, + ( ) - . and spaces (minimum 5 characters) — a lenient character-set check, not strict E.164 structural validation. The check is keyed on type: 'phone' itself and has nothing to configure; a field-level format key is not read. For a stricter shape, add a format validation rule with a regex.

password

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Maximum password length
minLengthnumber—Minimum password length

Default constraints: Validated the same as text (maxLength/minLength only). On a generic (non-better-auth) object a password-typed value is stored plaintext at rest but masked to •••••••• on read through the normal query path (ADR-0100) — the engine does not hash or encrypt it. One-way hashing and verification of real login credentials are owned entirely by the auth subsystem (better-auth); its own credential column (sys_account.password) is a hashed Field.text() column that is exempt from this masking. For a reversible, encrypted-at-rest value on your own objects, use the secret type instead.

secret

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Maximum length of the cleartext value
minLengthnumber—Minimum length of the cleartext value

Default constraints: Reversible, encrypted-at-rest value (DB password, API key, token) — ADR-0100. Fail-closed: with no ICryptoProvider registered, a write throws rather than persisting cleartext. The value is encrypted on write into a sys_secret row, only an opaque handle is persisted on the record, and reads are masked. Distinct from password, which is plaintext at rest (or one-way hashed inside the auth subsystem).


Rich Content Types

markdown

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Character limit on raw markdown source

Default constraints: Accepts valid Markdown syntax. No length limit by default.

html

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Character limit on raw HTML source

Default constraints: HTML content is sanitized to prevent XSS. No length limit by default.

richtext

PropertyTypeDefaultValidation Behavior
maxLengthnumber—Character limit on serialized content

Default constraints: Stored as structured rich text (e.g., ProseMirror/Tiptap JSON). Sanitized on save.


Number Types

number

PropertyTypeDefaultValidation Behavior
minnumber—Rejects values below minimum
maxnumber—Rejects values above maximum
precisionnumber—Total number of digits allowed
scalenumber—Number of decimal places

Default constraints: Any valid number. No range or precision limits by default.

currency

PropertyTypeDefaultValidation Behavior
minnumber—Minimum monetary value
maxnumber—Maximum monetary value
currencyConfig.currencyModeenumdynamicfixed pins the field to defaultCurrency; dynamic displays the tenant default currency (the localization.currency setting). Not a per-record choice
currencyConfig.defaultCurrencystringCNY3-character currency code (ISO 4217 or crypto): the field's currency under fixed; not the displayed currency under dynamic

Default constraints: Stored as a bare number (a finite numeric scalar — valueSchemaFor routes currency to z.number().finite()); there is no { value, currency } envelope on the value path. The currency code is never stored per record: it is field configuration, carried by currencyConfig above. Decimal places are the currency's ISO 4217 minor unit (2 for USD, 0 for JPY, 3 for KWD) and are declared nowhere — currencyConfig has no decimal-places key (currencyConfig.precision is refused, with a prescription to delete it), and the field-level precision is the total digit count, never compared with the currency.

percent

PropertyTypeDefaultValidation Behavior
minnumber—Minimum percentage value
maxnumber—Maximum percentage value
precisionnumber—Total digits
scalenumber—Decimal places

Default constraints: Stored as decimal (e.g., 0.85 for 85%). No range limit by default.


Date & Time Types

date

Required props: None.
Default constraints: Validates ISO 8601 date format (YYYY-MM-DD). No range restrictions.

datetime

Required props: None.
Default constraints: Validates ISO 8601 datetime format (YYYY-MM-DDTHH:mm:ssZ). Stored in UTC.

time

Required props: None.
Default constraints: Validates ISO 8601 time format (HH:mm:ss). 24-hour format.


Boolean Types

boolean

PropertyTypeDefaultValidation Behavior
defaultValueboolean—Initial value when field is empty

Default constraints: Only accepts true or false. Renders as checkbox.

toggle

PropertyTypeDefaultValidation Behavior
defaultValueboolean—Initial value when field is empty

Default constraints: Identical validation to boolean. Distinct UI — renders as toggle switch.


Selection Types

select

PropertyTypeDefaultValidation Behavior
optionsSelectOption[]—Required — a non-empty static option list, or a picklist naming a shared list instead. With neither (or options: []) the field is refused at parse.
defaultValuestring—Must match an option value

Option validation: Each option value must be a lowercase system identifier — starts with a letter, then letters/digits/underscores/dots (^[a-z][a-z0-9_.]*$), minimum 2 characters.

multiselect

PropertyTypeDefaultValidation Behavior
optionsSelectOption[]—Static option list. Optional: without options the field takes free-form values (tags mode).

Default constraints: Stores array of selected option values. Each value validated against options.

radio

PropertyTypeDefaultValidation Behavior
optionsSelectOption[]—Required — as for select: a non-empty list or a picklist, refused at parse with neither.

Default constraints: Single-value selection. Same validation as select with radio button UI.

checkboxes

PropertyTypeDefaultValidation Behavior
optionsSelectOption[]—Static option list. Parses without one, but draws a completeness warning: a checkbox group with no boxes renders nothing.

Default constraints: Multi-value selection. Same validation as multiselect with checkbox group UI.


Relational Types

lookup

PropertyTypeDefaultValidation Behavior
referencestring—Required. Target object name
referenceFiltersstring[]—Removed (#2377, ADR-0049) — no longer part of the Field schema; use lookupFilters + dependsOn instead
deleteBehaviorenumset_nullset_null, cascade, or restrict
multiplebooleanfalseAllow multiple references; the value is validated as an array of ids

Default constraints: Validates that referenced record exists. Foreign key integrity enforced.

Multi-value lookups (multiple: true). The empty set is representable: an emptied multi-value lookup is stored and read back as [], never null — including when deleteBehavior: 'set_null' removes the last remaining member. The declared contract lives in the multiple and required doc blocks of packages/spec/src/data/field.zod.ts (rendered in the Field reference), and it makes required on a multi-value lookup mean non-empty array.

The required-means-non-empty half is enforced by the record validator (#9476, per the #9447 ruling): an explicit [] is rejected on insert, and on any update that supplies the field — exactly as null and "" are on a single-value required field. An update that omits the field never fails this check, so legacy rows stay editable. No application-level emptiness check is needed.

user

PropertyTypeDefaultValidation Behavior
multiplebooleanfalsetrue stores a JSON array of user ids
defaultValue'current_user'—Stamps the acting user's id on insert

Default constraints: Person picker — a lookup specialized to the built-in sys_user object, so reference is implied and must not be authored. Stored as a foreign key to sys_user.id and resolved through the same $expand machinery as lookup; with multiple: true the stored value is a JSON array of ids.

master_detail

PropertyTypeDefaultValidation Behavior
referencestring—Required. Parent object name
deleteBehaviorenumcascadeMaster-detail cascades at runtime unless set to restrict

Default constraints: Enforces parent-child ownership. Child records cascade-delete with the parent by default.

tree

PropertyTypeDefaultValidation Behavior
referencestring—Optional. If given, must be this object's own name — a tree whose reference names any other object is refused at parse (fields.<field>.reference); a link to a different object is a lookup

Default constraints: Self-referencing lookup for hierarchical structures — reference is optional and, if given, must be this object. Stored and expanded like a lookup; the engine does not run a cycle check on write, so a self-reference chain that loops back on itself is not automatically rejected.


Media Types

image

PropertyTypeDefaultValidation Behavior
multiplebooleanfalseAllow multiple image uploads

Default constraints: Accepts common image MIME types. The schema has no field-level attachment-config property — fileAttachmentConfig was removed in the 16.x line (#2377).

file

PropertyTypeDefaultValidation Behavior
multiplebooleanfalseAllow multiple file uploads

Default constraints: Accepts any file type. The Field schema exposes no field-level upload-validation config — fileAttachmentConfig (and its maxSize / allowedTypes / virus-scan sub-keys) was removed in the 16.x line (#2377). File-storage constraints such as virus scanning are configured on the file-storage integration, not on the field.

// File field
{
  name: 'contract_pdf',
  label: 'Contract',
  type: 'file',
  multiple: false
}

avatar

Default constraints: Single image upload. Typically constrained to square aspect ratio and small file sizes.

video

Default constraints: Accepts common video MIME types. The field type has no per-field size/type config property in the schema.

audio

Default constraints: Accepts common audio MIME types. The field type has no per-field size/type config property in the schema.


Computed & System Types

formula

PropertyTypeDefaultValidation Behavior
expressionstring—Required. CEL formula expression
returnTypeenum—Optional inferred result type: number, text, boolean, date, or currency

Default constraints: Read-only. Value computed at runtime from expression. Not directly writable.

{
  name: 'full_name',
  label: 'Full Name',
  type: 'formula',
  expression: 'record.first_name + " " + record.last_name'
}

summary

PropertyTypeDefaultValidation Behavior
summaryOperationsobject—Required. Roll-up definition
summaryOperations.objectstring—Child object to aggregate
summaryOperations.fieldstring—Field to aggregate
summaryOperations.functionenum—count, sum, min, max, or avg

Default constraints: Read-only. Value computed from child records. Only valid on master objects.

autonumber

PropertyTypeDefaultValidation Behavior
autonumberFormatstring—Display format pattern (e.g., CASE-{0000})

Default constraints: Read-only. Auto-incremented. Cannot be manually set after creation.


Embedded Structured Types

These types store structured values as JSON on the parent row — no separate table and no foreign key. Sub-field shapes are declared on the field, and the stored value is validated as an open object map (valueSchemaFor), so sub-keys are not constrained by the field type itself.

composite

Default constraints: Single embedded sub-object. Stored as a JSON object map (Record<string, unknown>). No per-type config properties.

repeater

Default constraints: Repeating embedded sub-object array. Stored as a JSON array of object maps (Array<Record<string, unknown>>); a non-array value is rejected. No per-type config properties.

record

Default constraints: Name-keyed map of embedded sub-objects (Record<string, SubObject>) — ADR-0007. Stored as a JSON object map; insertion order is display order. No per-type config properties.


Enhanced Types

location

Default constraints: Stored as { lat, lng, altitude?, accuracy? } — the keys are lat/lng, not latitude/longitude (LocationValueSchema, ADR-0104 D1). lat: -90 to 90. lng: -180 to 180. No per-type config properties.

address

Default constraints: Stored as structured object with street, city, state, postalCode, country, countryCode, formatted (all parts optional). No per-type config properties.

code

PropertyTypeDefaultValidation Behavior
languagestring—Programming language for syntax highlighting

Default constraints: Stored as plain text. Language used for UI syntax highlighting only.

json

Default constraints: Must be valid JSON. Parsed and validated on save.

color

Default constraints: Stores the color value as a string. No per-type config properties.

rating

PropertyTypeDefaultValidation Behavior
maxnumber5Maximum rating value (set by the Field.rating(max) factory)

Default constraints: Integer between 0 and max.

slider

PropertyTypeDefaultValidation Behavior
minnumber—Minimum slider value
maxnumber—Maximum slider value
stepnumber1Step increment

Default constraints: Numeric value between min and max in step increments.

signature

Default constraints: Stored as base64-encoded image data. Immutable after creation in most workflows.

qrcode

Default constraints: Stores the encoded value; rendered as a scannable code. No per-type config properties.

progress

PropertyTypeDefaultValidation Behavior
minnumber—Minimum value (typically 0)
maxnumber—Maximum value (typically 100)

Default constraints: Numeric value rendered as a progress bar. Usually 0–100.

tags

Default constraints: Stored as array of strings. A lone scalar value is coerced into a single-element array; the engine does not trim or deduplicate entries.


AI/ML Types

vector

PropertyTypeDefaultValidation Behavior
dimensionsnumber—Required. Vector size (1–10,000)

Default constraints: Must be a numeric array of exactly dimensions length. Set the flat dimensions property directly — the legacy nested vectorConfig object was removed in the 16.x line (#2377) and is no longer part of the schema.

{
  name: 'content_embedding',
  label: 'Content Embedding',
  type: 'vector',
  dimensions: 1536
}

Security & Compliance Properties

For sensitive data, use the properties and types the platform actually enforces:

PropertyTypeDefaultDescription
requiredPermissionsstring[]—Capabilities required to read/edit the field — masked on read, denied on write unless the caller holds all of them (ADR-0066 D3)
trackHistoryboolean—Render the field's value changes as entries on the record activity timeline

For reversible encrypted-at-rest values (API keys, tokens, DB passwords), use the secret field type — it is the type with an enforced encryption/masking code path (encrypted at rest via sys_secret, masked on read). password is validated like plain text and stored plaintext at rest with no built-in hashing or encryption; it is masked to •••••••• on read but offers no at-rest protection (see the password section above). See the Field Type Gallery.


Quick Validation Summary

Field TypeRequired PropsKey Constraints
text—maxLength, minLength, valueDomain (format is a display hint, not a constraint)
textarea—maxLength, minLength
email—Basic local@domain shape (not full RFC 5322)
url—Valid URL with protocol
phone—Permissive character set, not strict E.164
password—Validated like text; masked on read but plaintext at rest (no hashing/encryption)
secret—Encrypted at rest via sys_secret, masked on read; fail-closed — writes throw with no ICryptoProvider
markdown—maxLength
html—Sanitized, maxLength
richtext—Sanitized, maxLength
number—min, max, precision, scale
currency—currencyConfig (mode, code)
percent—min, max, stored as decimal
date—ISO 8601 date
datetime—ISO 8601 datetime, UTC
time—ISO 8601 time, 24h
boolean—true / false only
toggle—true / false only
selectoptionsOption values must be lowercase system identifiers
multiselectoptionsArray of valid option values
radiooptionsSingle value from options
checkboxesoptionsArray of valid option values
lookupreferenceForeign key integrity
user—Lookup specialized to sys_user; multiple: true stores an id array
master_detailreferenceCascade delete, ownership
tree—Self-referencing; no automatic cycle check
image—Common image MIME types; multiple for many
file—Any file type; multiple for many (no field-level upload config)
avatar—Single image, typically square
video—Common video MIME types
audio—Common audio MIME types
formulaexpressionRead-only, computed at runtime
summarysummaryOperationsRead-only, roll-up from children
autonumber—Read-only, auto-incremented
composite—Single embedded sub-object; stored as a JSON object map
repeater—Embedded sub-object array; stored as a JSON array of object maps
record—Name-keyed map of embedded sub-objects (ADR-0007)
location—Lat: -90–90, Lng: -180–180
address—Structured object (street, city, …)
code—Plain text, language for highlighting
json—Must be valid JSON
color—Stored as a string
rating—0 to max (default 5)
slider—min to max in step increments
signature—Base64 image, typically immutable
qrcode—Format-specific validation
progress—Numeric, typically 0–100
tags—String array; no automatic trim/dedup
vectordimensionsNumeric array of exact dimensions length

On this page