Skip to main content
Pylon’s data-layer automation is deliberately small and mostly automatic. Two built-in plugins wire themselves from your schema (you never name them in a config block). The SDK adds a few field-level and entity-level helpers. Everything else (cascades, slugs, derived fields, versioning) is a server function or a policy, not a plugin.

tenant_scope

Row-level multi-tenancy. It is automatic. The signal is a tenantId field on the entity. No config entry.
Once an entity has a tenantId (or tenant_id) field:
  • Inserts auto-fill tenantId = auth.tenantId when the caller does not provide it. They reject a write whose tenantId does not match the caller’s. You cannot insert into another tenant.
  • Reads / updates / deletes are scoped to the caller’s tenant.
  • Admin contexts bypass scoping on writes. On reads, an admin with no active tenant bypasses scoping; an admin with an active tenant is scoped to that tenant.
This makes tenant isolation the default, not a where clause you must remember on every query. In your own query/mutation handlers you still have auth.tenantId to scope further.

owner_stamp

Per-row ownership. It is automatic. The signal is field.X().owner() on the field.
On insert the runtime overwrites sellerId with auth.userId from the session. It rejects any client attempt to set a different user. This makes optimistic local-first writes safe for owned data. The client inserts with its own id for an instant local render. The server validates the owner from the session. Pair it with a policy like allowUpdate: "data.sellerId == auth.userId".

Schema behaviors

behaviors([...]) on the entity builder run field-injection helpers before the schema is registered. Two ship today:
Behaviors mutate the EntityDefinition’s fields, so the rest of the framework (storage, sync, policies) treats the injected columns as ordinary columns.
Behaviors add the columns. The defaultNow() marker drives automatic value-stamping, which is not fully wired yet. Until it ships, set the values in your function handler (or on the client). The columns persist normally. softDelete adds the deletedAt column. The DELETE-sets-deletedAt and list-filtering semantics are app-driven for now (filter deletedAt == null in your query).

Field-level constraints & defaults

The field builder is the real “validation” surface. Pylon records it in the manifest. The runtime and codegen enforce it:
For richer validation (length limits, regex, cross-field rules), do the check in a mutation handler before writing, and gate access with policies.

Where other data features live

Pylon implements these features through fields, functions, policies, or auth, not configurable plugins: You configure search per-entity, not as a data plugin. See Search & AI.