Help us shape the future of Go! Take the 2026 Go Developer Survey

schema

package
v0.3.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package schema holds the ent schemas: the tables, columns, constraints, indexes and mixins of the data model, from which ent, the scoped client and the migrations are generated.

Index

Constants

View Source
const ExpirableEventPredicate = `` /* 172-byte string literal not displayed */

ExpirableEventPredicate selects the Events that expire by age (ADR 0019): every Event except the evidentiary ones (consent proof, complaints, unsubscribes and permanent bounces). A bounce with no bounceKind counts as transient. It is the single source of truth for both the partial index below and the retention delete (internal/events.PruneEvents), so the delete can always use the index.

Variables

This section is empty.

Functions

func EnumChecks

func EnumChecks(table string, fields []ent.Field) map[string]string

EnumChecks is the CHECK constraint for every enum field of a schema, so the database refuses a value the model does not list: ent stores an enum as a plain varchar. The values come from the fields themselves, so a new value cannot drift from its constraint. Pass the schema's own Fields(); the constraint is named <table>_<column>_check.

func LowerEmailCheck

func LowerEmailCheck(table string) (name, expr string)

LowerEmailCheck is the CHECK constraint that keeps table's email column lower-cased, named <table>_email_lower. An account address is stored lower-cased, so one address is one row whatever case it was typed in and the unique index on the column holds case-insensitively; internal/emailcase lower-cases it on every write through ent, and this makes the database refuse a write that went around it, so a missed path fails loudly instead of making a second account.

func TableWithEnumChecks

func TableWithEnumChecks(table string, fields []ent.Field) entsql.Annotation

TableWithEnumChecks is the table annotation of a schema whose enum fields are checked.

Types

type ApiToken

type ApiToken struct {
	ent.Schema
}

func (ApiToken) Annotations

func (ApiToken) Annotations() []schema.Annotation

func (ApiToken) Fields

func (ApiToken) Fields() []ent.Field

func (ApiToken) Indexes

func (ApiToken) Indexes() []ent.Index

func (ApiToken) Mixin

func (ApiToken) Mixin() []ent.Mixin

type AuditEntry

type AuditEntry struct {
	ent.Schema
}

AuditEntry is one row of a Workspace's append-only Audit log (GLOSSARY, ADR 0022). It is an Enterprise feature (ee/LICENSE): only the ee/audit subscriber writes it, and only the ee/audit reader reads it. ent keeps one schema package per graph, so the table is declared here next to the others and migrated by the same flow; the license gate lives in ee/, not in the schema. No code path updates or deletes a row (the ee/retention prune job, ADR 0014, is the only remover).

func (AuditEntry) Annotations

func (AuditEntry) Annotations() []schema.Annotation

func (AuditEntry) Fields

func (AuditEntry) Fields() []ent.Field

func (AuditEntry) Indexes

func (AuditEntry) Indexes() []ent.Index

func (AuditEntry) Mixin

func (AuditEntry) Mixin() []ent.Mixin

type Audited

type Audited struct {
	// Action is the entity's part of the action name `<Action>.<verb>`, e.g. "tag".
	Action string
	// NameField is the field whose value is snapshotted as the target's name, so an
	// entry stays readable after the target is deleted. Leave empty to snapshot none
	// (a Contact is identified by id only: an email or name is personal data).
	NameField string
	// NameIsURL snapshots NameField as a URL stripped to scheme, host and path: a
	// query string or userinfo may carry a secret, so it never reaches the log.
	NameIsURL bool
	// NamesOnly records the names of changed fields and never a value, on create, update
	// and delete alike (a Contact: its values are personal data, so an immutable log must
	// never hold them and erasing a Contact needs no rewrite of the log).
	NamesOnly bool
	// Always lists fields an update's diff carries even when they did not change, as
	// {from: v, to: v}: they say what kind of thing the entry is about (a Hold's reason
	// code), so a reader can classify an update that touched only another field.
	Always []string
}

Audited opts a Workspace-owned entity into the Audit log (ADR 0022): the generated scoped client wraps its create, update and delete in a transaction and publishes an `audit.entry` with a before/after diff for every write made under a User, API token, Staff member or system actor. Scopes built from a secret (ingest) are never audited.

func (Audited) Name

func (Audited) Name() string

type AuthAttempt

type AuthAttempt struct {
	ent.Schema
}

AuthAttempt is the failed-attempt counter of one normalized email for one action kind (ADR 0025). It follows the account, not the source IP, so a distributed guessing attack on one account hits one counter, and it lives in Postgres so the count is exact across replicas. It has no Workspace (a User is not Workspace-owned) and no edge to User: rows are written for unknown emails too, so the table cannot be used to enumerate accounts. Only internal/accounts and ee/staff (through accounts.Attempts) read and write it. A Staff member's failures count under their own kind, so they never share a counter with a User of the same address.

func (AuthAttempt) Annotations

func (AuthAttempt) Annotations() []schema.Annotation

func (AuthAttempt) Fields

func (AuthAttempt) Fields() []ent.Field

func (AuthAttempt) Indexes

func (AuthAttempt) Indexes() []ent.Index

func (AuthAttempt) Mixin

func (AuthAttempt) Mixin() []ent.Mixin

type Automation

type Automation struct {
	ent.Schema
}

Automation is a workspace-scoped, event-triggered workflow. A contact is enrolled when an Event whose action equals trigger_event is recorded for them; the run then walks the linear list of steps of the Automation version it began on. Branch/goal nodes and a visual builder come later.

func (Automation) Annotations

func (Automation) Annotations() []schema.Annotation

func (Automation) Edges

func (Automation) Edges() []ent.Edge

func (Automation) Fields

func (Automation) Fields() []ent.Field

func (Automation) Indexes

func (Automation) Indexes() []ent.Index

func (Automation) Mixin

func (Automation) Mixin() []ent.Mixin

type AutomationRun

type AutomationRun struct {
	ent.Schema
}

AutomationRun is one contact's progress through an Automation. The unique (automation_id, contact_id) index enforces enroll-once-ever (re-enrollment is a deliberate non-feature for the MVP). current_step points at the next step to execute; resume_at is set while waiting.

func (AutomationRun) Annotations

func (AutomationRun) Annotations() []schema.Annotation

func (AutomationRun) Edges

func (AutomationRun) Edges() []ent.Edge

func (AutomationRun) Fields

func (AutomationRun) Fields() []ent.Field

func (AutomationRun) Indexes

func (AutomationRun) Indexes() []ent.Index

func (AutomationRun) Mixin

func (AutomationRun) Mixin() []ent.Mixin

type AutomationVersion

type AutomationVersion struct {
	ent.Schema
}

AutomationVersion is one copy of an Automation's steps (ADR 0049, GLOSSARY: Automation version). An Enrollment stands on the version it began on, so editing the steps changes only later Enrollments. A version no Enrollment stands on is rewritten in place rather than superseded; the name and Trigger stay on the Automation.

func (AutomationVersion) Annotations

func (AutomationVersion) Annotations() []schema.Annotation

func (AutomationVersion) Edges

func (AutomationVersion) Edges() []ent.Edge

func (AutomationVersion) Fields

func (AutomationVersion) Fields() []ent.Field

func (AutomationVersion) Indexes

func (AutomationVersion) Indexes() []ent.Index

func (AutomationVersion) Mixin

func (AutomationVersion) Mixin() []ent.Mixin

type BrandKit

type BrandKit struct {
	ent.Schema
}

BrandKit is a Workspace's visual identity as data (GLOSSARY "Brand kit", ADR 0040): at most one row per Workspace, created by its first save. Its values are written into a message when the message is authored and never resolved at send time, so no message holds a reference to it.

func (BrandKit) Annotations

func (BrandKit) Annotations() []schema.Annotation

func (BrandKit) Fields

func (BrandKit) Fields() []ent.Field

func (BrandKit) Indexes

func (BrandKit) Indexes() []ent.Index

func (BrandKit) Mixin

func (BrandKit) Mixin() []ent.Mixin

type Broadcast

type Broadcast struct {
	ent.Schema
}

Broadcast is a workspace-scoped one-off email campaign. Its lifecycle is draft -> scheduled -> sending -> sent (or failed). The body is authored as HTML; the plain-text part is derived from the final HTML of each send and never stored (ADR 0056). Its Segment is the one named by segment_id, or all contacts in the workspace when it is unset. Aggregate counters are denormalized for cheap reporting; per-recipient state lives in BroadcastRecipient.

func (Broadcast) Annotations

func (Broadcast) Annotations() []schema.Annotation

func (Broadcast) Edges

func (Broadcast) Edges() []ent.Edge

func (Broadcast) Fields

func (Broadcast) Fields() []ent.Field

func (Broadcast) Indexes

func (Broadcast) Indexes() []ent.Index

func (Broadcast) Mixin

func (Broadcast) Mixin() []ent.Mixin

type BroadcastRecipient

type BroadcastRecipient struct {
	ent.Schema
}

BroadcastRecipient is the per-recipient delivery log for a Broadcast. It is the basis for delivery tracking (opens/clicks) and idempotent sending: the unique (broadcast_id, contact_id) index guarantees one row per recipient.

func (BroadcastRecipient) Annotations

func (BroadcastRecipient) Annotations() []schema.Annotation

func (BroadcastRecipient) Edges

func (BroadcastRecipient) Edges() []ent.Edge

func (BroadcastRecipient) Fields

func (BroadcastRecipient) Fields() []ent.Field

func (BroadcastRecipient) Indexes

func (BroadcastRecipient) Indexes() []ent.Index

func (BroadcastRecipient) Mixin

func (BroadcastRecipient) Mixin() []ent.Mixin

type CapturedHeader

type CapturedHeader struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

CapturedHeader is one header line of a Captured message, in the order it was built.

type CapturedMessage

type CapturedMessage struct {
	ent.Schema
}

CapturedMessage is a message a Test inbox kept instead of sending it out (ADR 0058): the one place the rendered content of a send lives, since an Outbound message holds none. It is owned by the Workspace and by the Integration (the Test inbox) it was captured through, and dies with the Integration (review rule 8: CASCADE for a row that dies with its owner) and with the Workspace (Workspace deletion removes it first). It belongs to the Outbound message of the send when there is one; a Test send has none, and the message stays when the Outbound message goes (SET NULL).

func (CapturedMessage) Annotations

func (CapturedMessage) Annotations() []schema.Annotation

func (CapturedMessage) Edges

func (CapturedMessage) Edges() []ent.Edge

func (CapturedMessage) Fields

func (CapturedMessage) Fields() []ent.Field

func (CapturedMessage) Indexes

func (CapturedMessage) Indexes() []ent.Index

func (CapturedMessage) Mixin

func (CapturedMessage) Mixin() []ent.Mixin

type Confirmation

type Confirmation struct {
	ent.Schema
}

Confirmation is the derived read-model row for a confirmed (double opt-in) subscription on a (workspace, channel, destination) (ADR 0013). It is the positive mirror of Unsubscribe: presence means "confirmed", and it is keyed by destination — not by contact — so the eligibility gate is a single fast lookup symmetric with the negative layers. The source of truth is the immutable marketing.confirmed Event; this row is the projection, so there is no "confirmed" flag on the Contact (ADR 0001/0011 pattern).

Confirmation is transient and one-time: an address confirmed shortly after acquisition operates under the normal subtractive model thereafter. It is only consulted when the workspace opts into confirmed-opt-in; single-opt-in workspaces never read it. A deliberate "everything" unsubscribe deletes the row (stale consent never silently reactivates); the Event log is preserved.

func (Confirmation) Annotations

func (Confirmation) Annotations() []schema.Annotation

func (Confirmation) Edges

func (Confirmation) Edges() []ent.Edge

func (Confirmation) Fields

func (Confirmation) Fields() []ent.Field

func (Confirmation) Indexes

func (Confirmation) Indexes() []ent.Index

func (Confirmation) Mixin

func (Confirmation) Mixin() []ent.Mixin

type Contact

type Contact struct {
	ent.Schema
}

func (Contact) Annotations

func (Contact) Annotations() []schema.Annotation

func (Contact) Edges

func (Contact) Edges() []ent.Edge

func (Contact) Fields

func (Contact) Fields() []ent.Field

func (Contact) Indexes

func (Contact) Indexes() []ent.Index

func (Contact) Mixin

func (Contact) Mixin() []ent.Mixin

type ContactTag

type ContactTag struct {
	ent.Schema
}

ContactTag is the edge table of the Contact-Tag many-to-many: a pure presence row with no id of its own and no timestamps. It is a schema only so the table can carry the index on tag_id that deleting a Tag needs; it has no Workspace column, since both its ends are Workspace-owned, and is written only through the Contact's tags edge.

func (ContactTag) Annotations

func (ContactTag) Annotations() []schema.Annotation

func (ContactTag) Edges

func (ContactTag) Edges() []ent.Edge

func (ContactTag) Fields

func (ContactTag) Fields() []ent.Field

func (ContactTag) Indexes

func (ContactTag) Indexes() []ent.Index

type CustomField

type CustomField struct {
	ent.Schema
}

CustomField is the workspace-scoped, typed, named definition of a Contact attribute beyond the core fields (email / phone / subject_id / name). It is the one attribute concept — there is no schemaless "trait". An unknown key arriving from Identify is auto-created here with an inferred type (declared-by-use), but is a first-class, renameable definition from the first sight. Per-contact values live in Contact.custom_fields keyed by this field's `key`.

func (CustomField) Annotations

func (CustomField) Annotations() []schema.Annotation

func (CustomField) Fields

func (CustomField) Fields() []ent.Field

func (CustomField) Indexes

func (CustomField) Indexes() []ent.Index

func (CustomField) Mixin

func (CustomField) Mixin() []ent.Mixin

type DiscardMixin

type DiscardMixin struct {
	mixin.Schema
}

DiscardMixin gives an entity the nullable discarded_at stamp (ADR 0049, glossary Discard): the row is hidden from lists and from new work but kept, and Undiscard clears the stamp. There is no discarded_by: the Audit entry (discard, restore) names the actor.

A Workspace-owned entity that embeds it gets Discard, Undiscard and Purge on the scoped client and no Delete; list queries hide discarded rows (QueryWithDiscarded sees all), and by-id reads and UpdateOneID see every row. The Workspace takes only the field: it has no scoped wrapper and keeps its compare-and-swap lifecycle (ADR 0046).

A unique index on a discardable entity MUST be partial, entsql.IndexWhere("discarded_at IS NULL"): otherwise a discarded row holds the name for ever.

func (DiscardMixin) Fields

func (DiscardMixin) Fields() []ent.Field

type EmailTemplate

type EmailTemplate struct {
	ent.Schema
}

EmailTemplate is a workspace-scoped, reusable email body. Broadcasts copy from a template (no FK) so editing or deleting a template never affects a sent or in-flight broadcast.

func (EmailTemplate) Annotations

func (EmailTemplate) Annotations() []schema.Annotation

func (EmailTemplate) Fields

func (EmailTemplate) Fields() []ent.Field

func (EmailTemplate) Indexes

func (EmailTemplate) Indexes() []ent.Index

func (EmailTemplate) Mixin

func (EmailTemplate) Mixin() []ent.Mixin

type Erasure

type Erasure struct {
	ent.Schema
}

Erasure is one row of a Workspace's Erasures Export stream (ADR 0045, GLOSSARY: Export stream): the keys an Erasure matched on, so a warehouse consumer deletes the same rows. It is an Enterprise table: only the ee/export bus subscriber writes it (one row per `contact.erased` message, deduplicated by the envelope id) and only the ee/export prune job removes it, at the instance's Event retention window. The addresses are never stored, only their SHA-256, and the contact id is a plain number with no edge: the Contact is gone.

func (Erasure) Annotations

func (Erasure) Annotations() []schema.Annotation

func (Erasure) Fields

func (Erasure) Fields() []ent.Field

func (Erasure) Indexes

func (Erasure) Indexes() []ent.Index

func (Erasure) Mixin

func (Erasure) Mixin() []ent.Mixin

type Event

type Event struct {
	ent.Schema
}

func (Event) Annotations

func (Event) Annotations() []schema.Annotation

func (Event) Edges

func (Event) Edges() []ent.Edge

func (Event) Fields

func (Event) Fields() []ent.Field

func (Event) Indexes

func (Event) Indexes() []ent.Index

func (Event) Mixin

func (Event) Mixin() []ent.Mixin

type Hold

type Hold struct {
	ent.Schema
}

Hold is a Staff hold (ADR 0009): one reversible freeze of a Workspace's outbound sending, set from outside through the service endpoints under a key its setter chooses. Several coexist per Workspace; each lapses at its expiry, which the setter must renew, and the send path ignores an expired row at once (a read compares expires_at with the clock; nothing deletes it).

func (Hold) Annotations

func (Hold) Annotations() []schema.Annotation

func (Hold) Fields

func (Hold) Fields() []ent.Field

func (Hold) Indexes

func (Hold) Indexes() []ent.Index

func (Hold) Mixin

func (Hold) Mixin() []ent.Mixin

type InstanceState

type InstanceState struct {
	ent.Schema
}

InstanceState is the one row an Instance keeps about itself (ADR 0032): its random Instance id, the Owner's telemetry setting, whether the Telemetry report notice was acknowledged, when a report was last sent and the newest release the Release check found. It belongs to the Instance, not a Workspace (no Workspace edge, no scoped wrapper). Row id 1 is created at first start by internal/instance.

func (InstanceState) Annotations

func (InstanceState) Annotations() []schema.Annotation

func (InstanceState) Fields

func (InstanceState) Fields() []ent.Field

func (InstanceState) Mixin

func (InstanceState) Mixin() []ent.Mixin

type Integration

type Integration struct {
	ent.Schema
}

Integration is a workspace-scoped connection to an external sending provider. It is channel-agnostic on purpose: "email" today (smtp/ses), "sms" and others plug in the same way later by adding enum values + a catalog descriptor, with no schema reshape. Provider-specific credentials live encrypted in config_encrypted (the cleartext JSON shape is owned by the messaging catalog).

func (Integration) Annotations

func (Integration) Annotations() []schema.Annotation

func (Integration) Edges

func (Integration) Edges() []ent.Edge

func (Integration) Fields

func (Integration) Fields() []ent.Field

func (Integration) Indexes

func (Integration) Indexes() []ent.Index

func (Integration) Mixin

func (Integration) Mixin() []ent.Mixin

type Invitation

type Invitation struct {
	ent.Schema
}

Invitation is a pending offer of Membership to an email address. It carries its own token + expiry (before any User exists), and becomes a Membership on accept. owner is never invitable — only owner/admin manage members, and ownership is transferred, not invited.

func (Invitation) Annotations

func (Invitation) Annotations() []schema.Annotation

func (Invitation) Edges

func (Invitation) Edges() []ent.Edge

func (Invitation) Fields

func (Invitation) Fields() []ent.Field

func (Invitation) Indexes

func (Invitation) Indexes() []ent.Index

func (Invitation) Mixin

func (Invitation) Mixin() []ent.Mixin

type JoinDomain

type JoinDomain struct {
	ent.Schema
}

JoinDomain is a Workspace-scoped, DNS-verified domain whose Users may join the Workspace automatically. An Owner adds the domain and publishes the token as a TXT record at _sphericon-join.<domain>; the domain is verified while the record is published. Any Workspace may hold a pending claim of a domain, and only one holds it verified (the partial unique index below).

func (JoinDomain) Annotations

func (JoinDomain) Annotations() []schema.Annotation

func (JoinDomain) Fields

func (JoinDomain) Fields() []ent.Field

func (JoinDomain) Indexes

func (JoinDomain) Indexes() []ent.Index

func (JoinDomain) Mixin

func (JoinDomain) Mixin() []ent.Mixin

type Membership

type Membership struct {
	ent.Schema
}

Membership is the join that grants a User access to a Workspace with a Role. Workspaces are reached through Memberships, not owned by a single User.

func (Membership) Annotations

func (Membership) Annotations() []schema.Annotation

func (Membership) Edges

func (Membership) Edges() []ent.Edge

func (Membership) Fields

func (Membership) Fields() []ent.Field

func (Membership) Indexes

func (Membership) Indexes() []ent.Index

func (Membership) Mixin

func (Membership) Mixin() []ent.Mixin

type OAuthClient

type OAuthClient struct {
	ent.Schema
}

OAuthClient is an MCP client that registered itself through dynamic client registration (RFC 7591) or whose client_id is an HTTPS URL of a Client ID Metadata Document (ADR 0016). All clients are public (PKCE, no secret).

func (OAuthClient) Annotations

func (OAuthClient) Annotations() []schema.Annotation

func (OAuthClient) Edges

func (OAuthClient) Edges() []ent.Edge

func (OAuthClient) Fields

func (OAuthClient) Fields() []ent.Field

func (OAuthClient) Mixin

func (OAuthClient) Mixin() []ent.Mixin

type OAuthCode

type OAuthCode struct {
	ent.Schema
}

OAuthCode is a short-lived, single-use authorization code minted when a user approves the consent screen. The token endpoint exchanges it (with the PKCE verifier) for an ordinary scoped ApiToken in the chosen workspace.

func (OAuthCode) Annotations

func (OAuthCode) Annotations() []schema.Annotation

func (OAuthCode) Edges

func (OAuthCode) Edges() []ent.Edge

func (OAuthCode) Fields

func (OAuthCode) Fields() []ent.Field

func (OAuthCode) Indexes

func (OAuthCode) Indexes() []ent.Index

func (OAuthCode) Mixin

func (OAuthCode) Mixin() []ent.Mixin

type OAuthRefreshToken

type OAuthRefreshToken struct {
	ent.Schema
}

OAuthRefreshToken is a single-use refresh token of an MCP connector's grant. The refresh_token grant claims it, rotates the grant's ApiToken secret and writes the next one; a used row stays so that presenting it again is recognised as reuse and revokes the grant.

func (OAuthRefreshToken) Annotations

func (OAuthRefreshToken) Annotations() []schema.Annotation

func (OAuthRefreshToken) Edges

func (OAuthRefreshToken) Edges() []ent.Edge

func (OAuthRefreshToken) Fields

func (OAuthRefreshToken) Fields() []ent.Field

func (OAuthRefreshToken) Indexes

func (OAuthRefreshToken) Indexes() []ent.Index

func (OAuthRefreshToken) Mixin

func (OAuthRefreshToken) Mixin() []ent.Mixin

type OutboundMessage

type OutboundMessage struct {
	ent.Schema
}

OutboundMessage is the durable record of one Outbound send (ADR 0015): one row per email sent on a Workspace's behalf, whichever surface asked for it. It is claimed BEFORE the provider is called, under a per-logical-send idempotency key, so a retried job or request never sends twice; the outcome and the `email.sent` Event are written in one transaction afterwards. It carries provenance and outcome only — never the rendered content (PII + volume) — and references the surface's own records by foreign keys (review rule 8): RESTRICT for a Broadcast, an Automation and an Integration, which are discarded and never deleted (ADR 0049), SET NULL for a Contact, a Template, an Automation run and a Broadcast recipient, which a physical delete or an Erasure removes while the message stays as a delivery record.

func (OutboundMessage) Annotations

func (OutboundMessage) Annotations() []schema.Annotation

func (OutboundMessage) Edges

func (OutboundMessage) Edges() []ent.Edge

func (OutboundMessage) Fields

func (OutboundMessage) Fields() []ent.Field

func (OutboundMessage) Indexes

func (OutboundMessage) Indexes() []ent.Index

func (OutboundMessage) Mixin

func (OutboundMessage) Mixin() []ent.Mixin

type OutboxMessage

type OutboxMessage struct {
	ent.Schema
}

OutboxMessage is one row of the domain-event outbox, the watermill-sql messages table of the domain_events topic (ADR 0019). watermill owns the table's shape: its primary key is (transaction_id, offset) and the subscribers' query depends on that index order, which an ent id cannot express, so the migration (migrations/*_domain_events_outbox.sql) is written by hand. Skip keeps it out of ent's own diff; ent only generates the client, so the code and the tests that read or prune the outbox use it instead of SQL. The id is the table's "offset".

func (OutboxMessage) Annotations

func (OutboxMessage) Annotations() []schema.Annotation

func (OutboxMessage) Fields

func (OutboxMessage) Fields() []ent.Field

type OutboxOffset

type OutboxOffset struct {
	ent.Schema
}

OutboxOffset is one consumer group's cursor into the domain-event outbox, the watermill-sql offsets table of the domain_events topic. Like OutboxMessage it is owned by watermill (a hand-written migration creates it) and skipped by ent's diff; ent only generates the client. The id is the table's consumer_group.

func (OutboxOffset) Annotations

func (OutboxOffset) Annotations() []schema.Annotation

func (OutboxOffset) Fields

func (OutboxOffset) Fields() []ent.Field

type Passkey

type Passkey struct {
	ent.Schema
}

Passkey is a WebAuthn credential a User registered to log in without a password (ADR 0052). It belongs to the User, not a Workspace, and only internal/secondfactor reads and writes it. The columns are what go-webauthn needs to rebuild a credential and check an assertion against it; the backup flags are stored as registered because the library refuses an assertion whose Backup Eligible flag differs.

func (Passkey) Annotations

func (Passkey) Annotations() []schema.Annotation

func (Passkey) Edges

func (Passkey) Edges() []ent.Edge

func (Passkey) Fields

func (Passkey) Fields() []ent.Field

func (Passkey) Indexes

func (Passkey) Indexes() []ent.Index

func (Passkey) Mixin

func (Passkey) Mixin() []ent.Mixin

type PasskeyChallenge

type PasskeyChallenge struct {
	ent.Schema
}

PasskeyChallenge is the record of a Passkey ceremony challenge that was spent (ADR 0052). A challenge travels in a signed cookie and `begin` writes nothing; the row is inserted when a ceremony finishes, and its unique challenge makes a replay fail. It has no User: a usernameless login has none yet. The daily cleanup prunes rows older than a challenge can live. Only internal/secondfactor reads and writes it.

func (PasskeyChallenge) Annotations

func (PasskeyChallenge) Annotations() []schema.Annotation

func (PasskeyChallenge) Fields

func (PasskeyChallenge) Fields() []ent.Field

func (PasskeyChallenge) Mixin

func (PasskeyChallenge) Mixin() []ent.Mixin

type PrivateRelayRange

type PrivateRelayRange struct {
	ent.Schema
}

PrivateRelayRange is one egress address range of iCloud Private Relay, as Apple publishes them (ADR 0053). A request from inside one is flagged as machine engagement. The table is the whole published list, replaced as one by the refresh job (internal/machine); it holds no Workspace data, so it has no Workspace edge.

func (PrivateRelayRange) Annotations

func (PrivateRelayRange) Annotations() []schema.Annotation

func (PrivateRelayRange) Fields

func (PrivateRelayRange) Fields() []ent.Field

func (PrivateRelayRange) Mixin

func (PrivateRelayRange) Mixin() []ent.Mixin

type RecoveryCode

type RecoveryCode struct {
	ent.Schema
}

RecoveryCode is one single-use code that stands in for a TOTP code when the User's authenticator is lost (ADR 0020). Only its SHA-256 hash is stored: the plaintext is shown once, when the set is generated. Regenerating replaces the whole set. It belongs to a User, not a Workspace; only internal/secondfactor reads and writes it.

func (RecoveryCode) Annotations

func (RecoveryCode) Annotations() []schema.Annotation

func (RecoveryCode) Edges

func (RecoveryCode) Edges() []ent.Edge

func (RecoveryCode) Fields

func (RecoveryCode) Fields() []ent.Field

func (RecoveryCode) Indexes

func (RecoveryCode) Indexes() []ent.Index

func (RecoveryCode) Mixin

func (RecoveryCode) Mixin() []ent.Mixin

type RiverJob

type RiverJob struct {
	ent.Schema
}

RiverJob is river's job table (river_job), modelled as a skipped entity the way OutboxMessage models the outbox: river owns the table and its migrations (migrations/*_river_00N.sql), so Skip keeps it out of ent's diff and ent only generates the client. The code that removes queued work for Erasure (events.QueuePurger) uses it instead of SQL. Only the columns that code reads are declared; river writes the rest.

func (RiverJob) Annotations

func (RiverJob) Annotations() []schema.Annotation

func (RiverJob) Fields

func (RiverJob) Fields() []ent.Field

type Rule

type Rule struct {
	ent.Schema
}

Rule is a stateless reaction to an Event (ADR 0054): when any of its Trigger events is recorded for a Contact and its Condition holds, its Actions run at once. The Trigger events live in rule_triggers (a Rule may have several, and the schema is plain tables); the Condition is the shared tree (internal/condition) stored as JSON; the Actions are an ordered JSON array. A Rule has Firings, which are history: it is discarded, and only a Rule with none is purged (ADR 0049).

func (Rule) Annotations

func (Rule) Annotations() []schema.Annotation

func (Rule) Edges

func (Rule) Edges() []ent.Edge

func (Rule) Fields

func (Rule) Fields() []ent.Field

func (Rule) Indexes

func (Rule) Indexes() []ent.Index

func (Rule) Mixin

func (Rule) Mixin() []ent.Mixin

type RuleFiring

type RuleFiring struct {
	ent.Schema
}

RuleFiring is one execution of a Rule for one Event (ADR 0054): written when the Rule's Condition held, unique per Rule and Event so a redelivered Event does nothing twice. It records the result of every Action. It belongs to the Contact and goes with it on Erasure.

func (RuleFiring) Annotations

func (RuleFiring) Annotations() []schema.Annotation

func (RuleFiring) Edges

func (RuleFiring) Edges() []ent.Edge

func (RuleFiring) Fields

func (RuleFiring) Fields() []ent.Field

func (RuleFiring) Indexes

func (RuleFiring) Indexes() []ent.Index

func (RuleFiring) Mixin

func (RuleFiring) Mixin() []ent.Mixin

type RuleTrigger

type RuleTrigger struct {
	ent.Schema
}

RuleTrigger is one Trigger event of a Rule: the Event action that makes it fire.

func (RuleTrigger) Annotations

func (RuleTrigger) Annotations() []schema.Annotation

func (RuleTrigger) Edges

func (RuleTrigger) Edges() []ent.Edge

func (RuleTrigger) Fields

func (RuleTrigger) Fields() []ent.Field

func (RuleTrigger) Indexes

func (RuleTrigger) Indexes() []ent.Index

func (RuleTrigger) Mixin

func (RuleTrigger) Mixin() []ent.Mixin

type SchemaState

type SchemaState struct {
	ent.Schema
}

SchemaState is the one row that records the database's Major and the oldest release that still runs on it (ADR 0033): the guard in internal/db compares a starting binary with it. The first migration inserts the row (id 1); expand-only migrations never touch it and a destructive migration writes both fields. It belongs to the instance, not a Workspace.

func (SchemaState) Annotations

func (SchemaState) Annotations() []schema.Annotation

func (SchemaState) Fields

func (SchemaState) Fields() []ent.Field

func (SchemaState) Mixin

func (SchemaState) Mixin() []ent.Mixin

type ScopedRef

type ScopedRef struct {
	// Entity is the schema name of the referenced entity, e.g. "Segment".
	Entity string
	// Unchecked marks an id column that deliberately outlives the referenced row and
	// is therefore not verified (Entity stays empty).
	Unchecked bool
	// Reason says why the column has no foreign key (a tombstone or a snapshot). It is required
	// with Unchecked: ent/entc.go fails the generation without it (rule 8), and an Int64 id column
	// that is neither an edge field nor annotated fails too.
	Reason string
}

ScopedRef marks a plain (non-edge) field that stores the id of another Workspace-owned entity, so the generated scoped client (ADR 0017) verifies the id belongs to the same Workspace before it is saved. Edges and edge fields are detected automatically; only plain columns need this annotation.

func (ScopedRef) Name

func (ScopedRef) Name() string

type Segment

type Segment struct {
	ent.Schema
}

func (Segment) Annotations

func (Segment) Annotations() []schema.Annotation

func (Segment) Fields

func (Segment) Fields() []ent.Field

func (Segment) Indexes

func (Segment) Indexes() []ent.Index

func (Segment) Mixin

func (Segment) Mixin() []ent.Mixin

type SendLimiter

type SendLimiter struct {
	ent.Schema
}

SendLimiter is the state of one Integration's Send rate limit (ADR 0023): two token buckets, per-second and daily, in a single row so a reservation can take a token from both or neither in one atomic UPDATE. The row is created lazily on the first limited send. Each bucket stores its fill as a fraction of capacity (1 is full), not a token count: capacity and refill rate derive from the Integration's max_per_second / max_per_day at reservation time, so changing a limit takes effect at once and a bucket that was unlimited for a while is simply full.

func (SendLimiter) Annotations

func (SendLimiter) Annotations() []schema.Annotation

func (SendLimiter) Edges

func (SendLimiter) Edges() []ent.Edge

func (SendLimiter) Fields

func (SendLimiter) Fields() []ent.Field

func (SendLimiter) Indexes

func (SendLimiter) Indexes() []ent.Index

func (SendLimiter) Mixin

func (SendLimiter) Mixin() []ent.Mixin

type SendingDomain

type SendingDomain struct {
	ent.Schema
}

SendingDomain is a workspace-scoped, sphericon-authenticated sending identity (ADR 0010). sphericon signs outbound mail itself: it generates a per-domain DKIM keypair, the user publishes one <selector>._domainkey TXT, and the same verified domain works across every transport (smtp/ses/…) with no re-verification when the Integration changes. Distinct from a Sending source (the unsubscribe consent scope) despite the similar name.

Verification is kept as moments, not a flag a job flips: verified_at is when the domain most recently became verified, verification_lost_at when a re-check found the DKIM DNS gone (nil again once it is verified anew). A domain is verified while verified_at is set and verification_lost_at is not (internal/dkimstate). The private key is stored Tink-encrypted; the public key is the TXT value and is safe to expose.

func (SendingDomain) Annotations

func (SendingDomain) Annotations() []schema.Annotation

func (SendingDomain) Fields

func (SendingDomain) Fields() []ent.Field

func (SendingDomain) Indexes

func (SendingDomain) Indexes() []ent.Index

func (SendingDomain) Mixin

func (SendingDomain) Mixin() []ent.Mixin

type Sensitive

type Sensitive struct{}

Sensitive marks a field of an Audited entity whose value must never reach the log (token hashes, passwords, Integration credentials, webhook signing secrets): an entry records only that it changed.

func (Sensitive) Name

func (Sensitive) Name() string

type ServiceCredential

type ServiceCredential struct {
	ent.Schema
}

ServiceCredential is a machine identity on the Staff surface (ADR 0026): a bearer credential a service presents on the internal listener, limited to named scopes. Like Staff member it belongs to no Workspace, so the scoped client does not cover it; it is created, rotated and revoked only by the CLI and is read and written only from ee/staff. Only a hash of its value is stored. A service may hold several active credentials at once (the name is not unique), so a rotation needs no downtime.

func (ServiceCredential) Annotations

func (ServiceCredential) Annotations() []schema.Annotation

func (ServiceCredential) Fields

func (ServiceCredential) Fields() []ent.Field

func (ServiceCredential) Mixin

func (ServiceCredential) Mixin() []ent.Mixin

type SignupSurveyResponse

type SignupSurveyResponse struct {
	ent.Schema
}

SignupSurveyResponse is one User's answers to the Sign-up survey: one row per User, created when the survey is submitted and removed with the User (the foreign key cascades, ADR 0050). It belongs to a User, not a Workspace, so it has no scoped wrapper; only internal/signupsurvey writes it. Each question has an `other` choice with a short text. heard_from is optional (null when it was skipped); the other answers are required.

func (SignupSurveyResponse) Annotations

func (SignupSurveyResponse) Annotations() []schema.Annotation

func (SignupSurveyResponse) Edges

func (SignupSurveyResponse) Edges() []ent.Edge

func (SignupSurveyResponse) Fields

func (SignupSurveyResponse) Fields() []ent.Field

func (SignupSurveyResponse) Mixin

func (SignupSurveyResponse) Mixin() []ent.Mixin

type StaffMember

type StaffMember struct {
	ent.Schema
}

StaffMember is a platform staff identity (ADR 0008, ADR 0026), separate from User: it has no Membership and no Workspace edge, so the scoped client does not cover it and no Workspace-scoped code can return one. It is created only by the CLI and is read and written only from ee/staff.

func (StaffMember) Annotations

func (StaffMember) Annotations() []schema.Annotation

func (StaffMember) Fields

func (StaffMember) Fields() []ent.Field

func (StaffMember) Mixin

func (StaffMember) Mixin() []ent.Mixin

type Suppression

type Suppression struct {
	ent.Schema
}

Suppression is the workspace's authoritative, (channel, destination)-keyed do-not-send registry for hard, global facts: hard bounce, spam complaint, and manual bans. It is the send path's global hard floor, checked on every send, and a compliance ratchet (bounce/complaint do not auto-clear). Keyed by (channel, destination) — not by contact — so it covers destinations with no contact (e.g. a bounce) and survives contact deletion/re-import. The destination is stored normalized (lower-cased) and unique per (workspace, channel) among the unlifted rows, so ingestion can upsert idempotently. Distinct from Unsubscribe: Suppression is global and hard; an unsubscribe is per-sending-source and toggleable (see the Unsubscribe entity).

func (Suppression) Annotations

func (Suppression) Annotations() []schema.Annotation

func (Suppression) Edges

func (Suppression) Edges() []ent.Edge

func (Suppression) Fields

func (Suppression) Fields() []ent.Field

func (Suppression) Indexes

func (Suppression) Indexes() []ent.Index

func (Suppression) Mixin

func (Suppression) Mixin() []ent.Mixin

type Tag

type Tag struct {
	ent.Schema
}

Tag is a named, workspace-scoped, presence-only label applied to Contacts (see GLOSSARY). It carries no type and no value — a Contact has the Tag or does not — which is what separates it from a CustomField. Auto-created on first use. A Tag is an attribute, never a target set: "everyone tagged X" is a Segment has-Tag rule.

func (Tag) Annotations

func (Tag) Annotations() []schema.Annotation

func (Tag) Edges

func (Tag) Edges() []ent.Edge

func (Tag) Fields

func (Tag) Fields() []ent.Field

func (Tag) Indexes

func (Tag) Indexes() []ent.Index

func (Tag) Mixin

func (Tag) Mixin() []ent.Mixin

type TimeMixin

type TimeMixin struct {
	mixin.Schema
}

TimeMixin gives every entity created_at and updated_at (ADR 0017), including append-only ones, for uniformity. The database default lets the columns be added to tables that already hold rows.

func (TimeMixin) Fields

func (TimeMixin) Fields() []ent.Field

type TrackingHostChange

type TrackingHostChange struct {
	ent.Schema
}

TrackingHostChange is one entry of the change log behind the service read of Tracking domain hosts (ADR 0038): a host entering dns_verified or serving, or leaving them (back to pending, its Sending domain deleted or renamed). The id is the position in the log. An ent hook on SendingDomain (internal/trackinghostlog) writes it in the transaction of the change, so no write path can forget it, and takes an advisory lock held to the end of the transaction so positions commit in the order they are taken: a reader that passed a position cannot miss an entry committed below it. Only that hook writes the table; ee/staff reads it. It has no Workspace edge: an entry must outlive the Workspace row it reports, because the removal of a host is what a platform service reads it for.

func (TrackingHostChange) Annotations

func (TrackingHostChange) Annotations() []schema.Annotation

func (TrackingHostChange) Fields

func (TrackingHostChange) Fields() []ent.Field

func (TrackingHostChange) Indexes

func (TrackingHostChange) Indexes() []ent.Index

func (TrackingHostChange) Mixin

func (TrackingHostChange) Mixin() []ent.Mixin

Mixin gives the change a creation time: when the change was recorded.

type Unsubscribe

type Unsubscribe struct {
	ent.Schema
}

Unsubscribe is a per-(workspace, channel, destination, sending source) opt-out from marketing. It is keyed by destination — not by contact — so it survives contact deletion and re-import (the GDPR-erasure case: the opt-out is the minimum data retained to honor the refusal). Absence means subscribed: there is no positive subscription row and no "subscribed" flag on the Contact; resubscribing deletes the row.

The sending source is automatic, never hand-authored — it IS the sender: "broadcasts" (shared by every broadcast), "automation:<id>" (one per automation), and the reserved "everything" scope (the deliberate "leave entirely" opt-out). A per-source unsubscribe never silently loses the whole contact. Distinct from Suppression: an unsubscribe is per-source and toggleable, where Suppression is global and a compliance ratchet.

func (Unsubscribe) Annotations

func (Unsubscribe) Annotations() []schema.Annotation

func (Unsubscribe) Edges

func (Unsubscribe) Edges() []ent.Edge

func (Unsubscribe) Fields

func (Unsubscribe) Fields() []ent.Field

func (Unsubscribe) Indexes

func (Unsubscribe) Indexes() []ent.Index

func (Unsubscribe) Mixin

func (Unsubscribe) Mixin() []ent.Mixin

type Usage

type Usage struct {
	ent.Schema
}

Usage is one materialized figure of a Workspace: the value of one metric over one Usage period (GLOSSARY, ADR 0009). While the period is open the row is its Live usage (finalized_at unset, refreshed by the usage worker); at period close the same row becomes the Usage snapshot (finalized_at set) and never changes again. Only ee/usage writes and reads it. A new metric needs a Metric constant in ee/usage and no schema change: the metric is a plain string, one row per (Workspace, period, metric).

func (Usage) Annotations

func (Usage) Annotations() []schema.Annotation

func (Usage) Fields

func (Usage) Fields() []ent.Field

func (Usage) Indexes

func (Usage) Indexes() []ent.Index

func (Usage) Mixin

func (Usage) Mixin() []ent.Mixin

type User

type User struct {
	ent.Schema
}

func (User) Annotations

func (User) Annotations() []schema.Annotation

func (User) Edges

func (User) Edges() []ent.Edge

func (User) Fields

func (User) Fields() []ent.Field

func (User) Mixin

func (User) Mixin() []ent.Mixin

type UserIdentity

type UserIdentity struct {
	ent.Schema
}

UserIdentity is a Linked identity (ADR 0048): the pair of a Sign-in provider and the provider's immutable subject for one person, attached to a User. A sign-in finds the User by this pair, never by email, so a person who changes the address at the provider still signs in to the same account. It belongs to a User, not a Workspace; only internal/accounts reads and writes it. The subject is the provider's `sub` (Google) or numeric account id (GitHub), never logged or written to an Audit entry.

func (UserIdentity) Annotations

func (UserIdentity) Annotations() []schema.Annotation

func (UserIdentity) Edges

func (UserIdentity) Edges() []ent.Edge

func (UserIdentity) Fields

func (UserIdentity) Fields() []ent.Field

func (UserIdentity) Indexes

func (UserIdentity) Indexes() []ent.Index

func (UserIdentity) Mixin

func (UserIdentity) Mixin() []ent.Mixin

type Visitor

type Visitor struct {
	ent.Schema
}

Visitor is an anonymous device/browser identity — a visitor_id cookie, unique per workspace. Before Identify it may belong to no Contact (contact_id null); Identify binds it to a Contact, and one Contact owns many Visitors (the same person across devices).

func (Visitor) Annotations

func (Visitor) Annotations() []schema.Annotation

func (Visitor) Edges

func (Visitor) Edges() []ent.Edge

func (Visitor) Fields

func (Visitor) Fields() []ent.Field

func (Visitor) Indexes

func (Visitor) Indexes() []ent.Index

func (Visitor) Mixin

func (Visitor) Mixin() []ent.Mixin

type WebhookEndpoint

type WebhookEndpoint struct {
	ent.Schema
}

WebhookEndpoint is a workspace-scoped HTTP destination that receives domain events. The signing secret is stored encrypted (like integration creds); the "webhooks" bus subscriber fans matching events out to a river delivery job.

func (WebhookEndpoint) Annotations

func (WebhookEndpoint) Annotations() []schema.Annotation

func (WebhookEndpoint) Fields

func (WebhookEndpoint) Fields() []ent.Field

func (WebhookEndpoint) Indexes

func (WebhookEndpoint) Indexes() []ent.Index

func (WebhookEndpoint) Mixin

func (WebhookEndpoint) Mixin() []ent.Mixin

type Workspace

type Workspace struct {
	ent.Schema
}

func (Workspace) Annotations

func (Workspace) Annotations() []schema.Annotation

func (Workspace) Edges

func (Workspace) Edges() []ent.Edge

func (Workspace) Fields

func (Workspace) Fields() []ent.Field

func (Workspace) Indexes

func (Workspace) Indexes() []ent.Index

func (Workspace) Mixin

func (Workspace) Mixin() []ent.Mixin

type WorkspaceMixin

type WorkspaceMixin struct {
	mixin.Schema

	Ref string
}

WorkspaceMixin scopes an entity to a Workspace (ADR 0017): the workspace_id column and the required workspace edge. Ref names the reverse edge on Workspace, which stays hand-written there. Schema-specific indexes over workspace_id stay in their schemas.

func (WorkspaceMixin) Edges

func (m WorkspaceMixin) Edges() []ent.Edge

func (WorkspaceMixin) Fields

func (WorkspaceMixin) Fields() []ent.Field

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL