Skip to content

D1 Database Schema

Neemias' database is an SQLite managed by Cloudflare D1. Migrations are applied sequentially in the migrations/ folder. Below is the complete schema after all 19 migrations.

v0.56.0: The users.role column was migrated to a user_roles join table (migration 0019).

Conventions

  • UUIDs are stored as TEXT PRIMARY KEY (generated via crypto.randomUUID())
  • Timestamps use ISO 8601 format (TEXT)
  • Booleans are INTEGER (0 = false, 1 = true)
  • JSON is stored as TEXT (serialized)
  • Soft-delete uses status column (ACTIVE | DELETED)

Tables

users

System users with PBKDF2 authentication.

ColumnTypeConstraints
user_idTEXTPRIMARY KEY
emailTEXTUNIQUE NOT NULL
display_nameTEXTNOT NULL
statusTEXTNOT NULL, DEFAULT 'ACTIVE'
password_hashTEXT
created_atTEXTNOT NULL
updated_atTEXTNOT NULL

v0.56.0: role column removed. Roles are now stored in user_roles join table (see below).

user_roles

User-role join table for multi-role support (v0.56.0+).

ColumnTypeConstraints
user_idTEXTNOT NULL, REFERENCES users(user_id) ON DELETE CASCADE
roleTEXTNOT NULL
created_atTEXTNOT NULL

Primary key: composite (user_id, role).

students

Registered students with complete registration data.

ColumnTypeConstraints
student_idTEXTPRIMARY KEY
display_nameTEXTNOT NULL
photo_refTEXT
statusTEXTNOT NULL, DEFAULT 'ACTIVE'
created_atTEXTNOT NULL
updated_atTEXTNOT NULL
deleted_justificationTEXT
guardian_nameTEXTNOT NULL, DEFAULT ''
guardian_name_altTEXT
birth_dateTEXTYYYY-MM-DD format
phonesTEXTNOT NULL, DEFAULT '[]' (JSON array)
address_streetTEXT
address_numberTEXT
address_complementTEXT
address_neighborhoodTEXT
address_cityTEXT
address_stateTEXT
address_zipTEXT
class_idTEXTREFERENCES classes(class_id)
nucleus_participatesINTEGERNOT NULL, DEFAULT 0
nucleus_regionTEXT
nucleus_nameTEXT
allergiesTEXT
special_needsTEXT
family_membership_statusTEXTNOT NULL, DEFAULT 'DESCONHECIDO'

Indexes:

  • idx_students_status_updated_at(status, updated_at DESC)

family_membership_status values: MEMBRO, NAO_MEMBRO, DESCONHECIDO

classes

Classes by age range, with seed of 7 default classes.

ColumnTypeConstraints
class_idTEXTPRIMARY KEY
nameTEXTNOT NULL UNIQUE
age_minINTEGER
age_maxINTEGER
statusTEXTNOT NULL, DEFAULT 'ACTIVE'
created_atTEXTNOT NULL
updated_atTEXTNOT NULL

Indexes:

  • idx_classes_status(status, name)

Seed: Nursery (1-2), Toddler (3), Kindergarten (4-5), Children (6), Juniors 1 (7), Juniors 2 (8), Pre-Teens (9-11)

nuclei

Nuclei (cells) organized by region.

ColumnTypeConstraints
nucleus_idTEXTPRIMARY KEY
regionTEXTNOT NULL
nameTEXTNOT NULL
statusTEXTNOT NULL, DEFAULT 'ACTIVE'
created_atTEXTNOT NULL
updated_atTEXTNOT NULL

Indexes:

  • idx_nuclei_region(region, status)

Fixed regions: Sky Blue, Blue, Yellow, White, Red, Orange, Green

attendance_events

Immutable attendance events (event sourcing). Each call generates an event.

ColumnTypeConstraints
event_idTEXTPRIMARY KEY
student_idTEXTNOT NULL, REFERENCES students(student_id)
action_typeTEXTNOT NULL (MARK_PRESENT or MARK_ABSENT)
happened_atTEXTNOT NULL
actor_user_idTEXTNOT NULL
actor_roleTEXTNOT NULL
server_timestampTEXTNOT NULL
is_conflict_loserINTEGERNOT NULL, DEFAULT 0
created_atTEXTNOT NULL

Indexes:

  • idx_attendance_events_student_id(student_id, server_timestamp DESC)

student_events

Immutable student mutation events (CREATE, UPDATE, DELETE).

ColumnTypeConstraints
event_idTEXTPRIMARY KEY
student_idTEXTNOT NULL, REFERENCES students(student_id)
event_typeTEXTNOT NULL
actor_user_idTEXTNOT NULL
actor_roleTEXTNOT NULL
payloadTEXTNOT NULL (JSON)
server_timestampTEXTNOT NULL
is_conflict_loserINTEGERNOT NULL, DEFAULT 0
created_atTEXTNOT NULL

Indexes:

  • idx_student_events_student_id(student_id, server_timestamp DESC)

user_events

Immutable user mutation events.

ColumnTypeConstraints
event_idTEXTPRIMARY KEY
target_user_idTEXTNOT NULL, REFERENCES users(user_id)
event_typeTEXTNOT NULL
actor_user_idTEXTNOT NULL
actor_roleTEXTNOT NULL
payloadTEXTNOT NULL (JSON)
server_timestampTEXTNOT NULL
created_atTEXTNOT NULL

auth_refresh_sessions

Refresh token sessions with rotation and compromise detection.

ColumnTypeConstraints
session_idTEXTPRIMARY KEY
user_idTEXTNOT NULL, REFERENCES users(user_id) ON DELETE CASCADE
refresh_token_hashTEXTNOT NULL UNIQUE
csrf_tokenTEXTNOT NULL
expires_atTEXTNOT NULL
revoked_atTEXT
replaced_byTEXTREFERENCES auth_refresh_sessions(session_id)
rotated_atTEXT
suspected_compromise_atTEXT
created_atTEXTNOT NULL

Indexes:

  • idx_auth_refresh_sessions_user_id(user_id, created_at DESC)
  • idx_auth_refresh_sessions_expires_at(expires_at)

idempotency_ledger

Idempotency record for detecting mutation replay.

ColumnTypeConstraints
keyTEXTPRIMARY KEY (composite with user_id)
user_idTEXTPRIMARY KEY (composite with key)
payload_hashTEXTNOT NULL
status_codeINTEGERNOT NULL
response_bodyTEXTNOT NULL (JSON)
created_atTEXTNOT NULL

Indexes:

  • idx_idempotency_ledger_created_at(created_at DESC)

audit_logs

Audit trail for LGPD compliance.

ColumnTypeConstraints
audit_idTEXTPRIMARY KEY
correlation_idTEXTNOT NULL
actor_user_idTEXTNOT NULL
actor_roleTEXTNOT NULL
endpointTEXTNOT NULL
outcomeTEXTNOT NULL
detailsTEXTNOT NULL (JSON)
created_atTEXTNOT NULL

roles

Role catalog with granular permissions.

ColumnTypeConstraints
nameTEXTPRIMARY KEY
display_nameTEXTNOT NULL
permissionsTEXTNOT NULL, DEFAULT '[]' (JSON array)
is_systemINTEGERNOT NULL, DEFAULT 0
created_atTEXTNOT NULL
updated_atTEXTNOT NULL

Seed (8+ system roles, is_system=1):

RolePermissions
ADMINattendance, reports, students.*, users.*, settings, import-export, sessions.*, nuclei.manage, classes.manage
CHAMADORattendance, students.search
RELATORIOSreports, students.search
CADASTROstudents.add, students.search, sessions.add, sessions.edit, nuclei.manage, classes.manage

System roles also include VOLUNTARIO, COORDENACAO_KIDS, ADMINISTRATIVO_KIDS, RESPONSAVEL. Full list in packages/permissions/index.ts.

rate_limits

D1-based rate limiting control.

ColumnTypeConstraints
keyTEXTPRIMARY KEY
countINTEGERNOT NULL, DEFAULT 1
window_startINTEGERNOT NULL
expires_atINTEGERNOT NULL

Migration History

MigrationFileDescription
00010001_init.sqlCore schema: users, students, attendance_events, student_events, user_events, idempotency_ledger, audit_logs + indexes
00020002_auth_sessions.sqlauth_refresh_sessions table with rotation and revocation
00030003_student_fields.sqlStudent expansion (guardian, phones, address) + new classes and nuclei tables + class seed
00040004_family_membership.sqlfamily_membership_status column in students
00050005_compromise_detection.sqlsuspected_compromise_at column in auth_refresh_sessions
00060006_roles.sqlroles table + seed of the 4 system roles
00070007_rate_limits.sqlrate_limits table for rate control
00080008_sociodemographic.sqlSociodemographic fields for students (9 pastoral fields)
00090009_atomic_token_rotation.sqlAtomic refresh token rotation with compromise detection
00100010_events.sqlEvent registration tables (capacity, slots, registrations)
00110011_class_slots_sessions.sqlClass slots and sessions (weekly + one-off)
00120012_onboarding.sqlParent onboarding approval queue + registration links
00130013_username.sqlStudent username for OTP-based authentication
00140014_guardians.sqlGuardian relationship tracking & approval workflow
00150015_events.sqlEvent registration refinements (naming fix)
00160016_notifications.sqlNotification infrastructure (SSE feed, push tokens)
00170017_roles_seed.sqlSeed additional system roles (VOLUNTARIO, RESPONSAVEL, etc.)
00180018_capacity.sql + 0018_merge_voluntario.sqlClass capacity limits + VOLUNTEER/VOLUNTARIO_KIDS merge
00190019_user_roles.sqluser_roles join table. DROP COLUMN users.role. Migrates existing roles. Multi-role support.

Total: 19 migrations (0001–0019). The 0018_merge_voluntario.sql is a companion migration alongside 0018_capacity.sql.

Source: migrations/

Distributed under MIT License.