An Object is the foundational metadata type in ObjectStack. It defines a business entity — its fields, capabilities, indexes, and behaviors. Each Object maps to a database table/collection and automatically gets CRUD APIs, UI forms, and query support.
The stored field used as the record display name, e.g. 'name' or 'title' (ADR-0079). The deprecated alias displayNameField is still accepted.
titleFormat
string
optional
Deprecated (ADR-0079 → nameField). Render-only title template (e.g. '{{record.name}} - {{record.code}}'); an explicit nameField takes precedence
highlightFields
string[]
optional
Most-important fields in priority order — default list columns, cards, previews, detail highlight strip (ADR-0085; formerly compactLayout — the old spelling was retired and is now rejected)
stageField
string | false
optional
Linear lifecycle field; false declares the status field non-linear and suppresses stage heuristics (ADR-0085)
Control which platform features are active for this object:
enable: { trackHistory: true, // Field history tracking for audit searchable: true, // Include in global search index apiEnabled: true, // Expose via REST/GraphQL APIs apiMethods: ['get', 'list', 'create', 'update', 'delete'], files: true, // File attachments feeds: true, // Activity feed and comments activities: true, // Tasks and events tracking trash: true, // Soft delete with restore mru: true, // Most Recently Used tracking clone: true, // Deep record cloning}
Row-level tenant isolation in a shared database — the tenant field is injected
on write and enforced on read. Database-per-tenant isolation is an
environment/deployment choice (each environment carries its own database URL),
not object metadata.
Declares how long the object's data lives and how its space is reclaimed
(ADR-0057). The platform LifecycleService (registered by
ObjectQLPlugin, default-on) sweeps declared policies hourly. Objects
without a lifecycle block keep permanent record semantics — nothing is
ever deleted.
Persistence contract (see table below); record is the implicit default
retention.maxAge
Age-based reap on created_at
retention.onlyWhen
Row filter the reap is scoped to (per-field equality or { $in: [...] }); rows outside it are retained regardless of age. Incompatible with rotation storage and archive
ttl
Per-row expiry: field + expireAfter
storage
{ strategy: 'rotation', shards, unit } — time-shard + O(1) shard DROP (SQLite)
archive
Cold-store hand-off: after (must equal retention.maxAge) + to (datasource name) + optional keep
reclaim
Space reclamation after sweeps (default on for non-record)
Class
Contract
Typical use
record
Business truth — permanent, no policies allowed
accounts, invoices
audit
Compliance ledger — retain → archive → delete
audit logs
telemetry
High-frequency log — rotation / short retention
activity streams, job runs
transient
Ephemeral state — TTL auto-expire
receipts, device codes
event
Bus messages — very short TTL (hours)
scheduled fan-out
Enforcement rules:
A non-record class must declare at least one bounding policy
(retention, ttl, or rotation storage) — rejected at parse time
otherwise. Policies on record are rejected too.
Duration literals are <n> + h/d/w/y (e.g. '6h', '14d', '7y');
archive.after must equal retention.maxAge.
An archive-declared object is never hot-deleted before the archive
copy succeeded. No datasource registered under the archive.to name ⇒
rows are retained (safe default), not dropped.
Registering a datasource named telemetry routes every
telemetry/event/audit object to it — separate storage, opt-in purely
by the datasource's existence.
Operations knobs live in the lifecycle settings namespace: a runtime
enabled switch, tenant-scoped retention_overrides (a regulated tenant
sets years while dev keeps days), row quotas and growth_alert_rows
(observe-and-alert only). OS_LIFECYCLE_DISABLED=1 disables sweeping
entirely.
System object, protected from deletion (default: false)
abstract
boolean
Abstract base, cannot be instantiated (default: false)
sharingModel
enum
Org-Wide Default record visibility (ADR-0055/0056/0090). Canonical four only: 'private', 'public_read', 'public_read_write', 'controlled_by_parent' (detail visibility derived from its master). The legacy aliases ('read', 'read_write', 'full') were removed from the enum (ADR-0090 D4) — authoring rejects them. Unset on a custom object resolves to 'private' (ADR-0090 D1)