Convex-skjemaet inneholder ~80 kjernetabeller fordelt på 27 komponenter. Alle skriv-spørringer scopes til
tenantId utledet fra sesjonen. Offentlige lese-spørringer (markedsplassen) er un-scoped.
Identity & Auth
Tabell Felter (utdrag) Indekser usersid, email, name, role, mfaEnabled, idPortenPidby_email, by_idPortenPidsessionsuserId, token, expiresAt, device, ipHashby_token, by_usermfaCredentialsuserId, kind (totp|webauthn), secretEncby_useroauthAccountsuserId, provider (id-porten|bankid|feide), subjectby_provider_subjectpasswordResetsuserId, token, expiresAt, usedAtby_token
Tenants & RBAC
Tabell Felter (utdrag) Indekser tenantsid, slug, name, plan, kind (kommune|forening|privat), orgnrby_slug, by_orgnrtenantUserstenantId, userId, role, invitedAt, acceptedAtby_tenant, by_userrolestenantId, name, permissions[], isSystemby_tenant_nameuserRolesuserId, roleId, tenantIdby_user, by_rolecapabilitieskind, scope, enabledByDefault(system)
Booking & Calendar
Tabell Felter (utdrag) Indekser resourcestenantId, slug, name, kind, state, openingHoursby_tenant_slug, by_statebookingstenantId, resourceId, userId, startsAt, endsAt, state, priceCentsby_resource_time, by_user, by_tenant_statebookingApprovalsbookingId, approverId, decidedAt, decision, noteby_bookingavailabilityBlocksresourceId, startsAt, endsAt, reasonby_resource_timeseasonstenantId, name, startsAt, endsAt, allocationRulesby_tenant_activerecurringRulesbookingId, rrule, untilby_booking
Payments & Ledger
Tabell Felter (utdrag) Indekser paymentstenantId, bookingId, provider, intentId, state, amountCentsby_intent, by_bookingrefundspaymentId, amountCents, reason, stateby_paymentpayoutstenantId, provider, periodStart, periodEnd, amountCents, stateby_tenant_periodledgerEntriestenantId, kind, bookingId, amountCents, account, tsby_tenant_ts, by_bookingcommissionstenantId, bookingId, rateBps, amountCents, snapshotAtby_bookinginvoicestenantId, customerId, kid, state, amountCents, ehfSentAtby_kid, by_tenant_state
Notifications & Messaging
Tabell Felter (utdrag) Indekser notificationsuserId, kind, channel, payload, sentAt, readAtby_user_sentAtnotificationPrefsuserId, kind, channels[]by_user_kindmessagestenantId, threadId, senderId, body, sentAtby_thread_sentAtmessageThreadstenantId, bookingId, participantIds[], lastMessageAtby_booking, by_tenant_lastMsg
Audit & GDPR
Tabell Felter (utdrag) Indekser auditEventstenantId, actorId, kind, targetType, targetId, payload, tsby_tenant_ts, by_targetdataExportsuserId, requestedAt, completedAt, urlEnc, expiresAtby_userdataDeletionsuserId, requestedAt, scheduledAt, executedAt, gracePeriodEndby_user_stateconsentsuserId, kind, givenAt, revokedAt, versionby_user_kind
Outbox (Hendelse-buss)
Tabell Felter (utdrag) Indekser outboxEventstopic, payload, producerComponentId, createdAt, stateby_topic_createdAt, by_stateoutboxDeliverieseventId, subscriberId, attempt, nextAttemptAt, stateby_subscriber_state, by_eventeventTopicsname, schema, producerComponent, subscriberComponents[](system)
Tenant-første-prinsipp
Alle disse tabellene (med unntak av capabilities, eventTopics, og noen system-tabeller)
har tenantId som første index-felt. Alle skriv-fasader verifiserer
session.tenantId === input.tenantId før de muterer noe. Lese-spørringer på markedsplassen
bypasser scope-sjekk siden de er public.
// Eksempel: en skriv-fasade med tenant-scope
export const update = mutation ( {
args: { id: v . id ( " bookings " ) , patch: v . object ( { /* … */ } ) },
handler : async ( ctx , { id , patch } ) => {
const session = await requireSession (ctx) ;
const booking = await ctx . db . get (id) ;
if ( ! booking || booking . tenantId !== session . tenantId ) {
throw new ConvexError ( { kind: " not_found " , id } ) ;
await ctx . db . patch (id , patch) ;
await emit (ctx , " booking.updated " , { id , by: session . userId } ) ;
Beslektet