---
url: https://docs.nemo12.com/reference/data-dictionary/identity-family.md
description: >-
  Từ điển dữ liệu D1, domain Danh tính & gia đình: 10 bảng, audit_log tới users;
  cột, kiểu, khoá ngoại, index.
---

# Data Dictionary · Danh tính & gia đình

> ⚙️ **Trang này sinh tự động** từ source code bằng `scripts/gen-reference.mjs` — đừng sửa tay, sửa code rồi chạy `npm run gen:reference`.

Thuộc [Data Dictionary](../data-dictionary.md). 10 bảng, `audit_log` tới `users`.

## `audit_log`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `id` | TEXT | PRIMARY KEY |
| `actor_user_id` | TEXT | — |
| `action` | TEXT | NOT NULL |
| `target_type` | TEXT | NOT NULL |
| `target_id` | TEXT | NOT NULL |
| `detail_json` | TEXT | — |
| `occurred_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| `actor_role` | TEXT |  — thêm ở 0033_audit_privacy.sql |
| `ip` | TEXT |  — thêm ở 0033_audit_privacy.sql |
| `user_agent` | TEXT |  — thêm ở 0033_audit_privacy.sql |
| `request_id` | TEXT |  — thêm ở 0033_audit_privacy.sql |

**Index:** `idx_audit_target`(target_type, target_id) · `idx_audit_actor`(actor_user_id, occurred_at DESC)

## `auth_identities`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `provider` | TEXT | NOT NULL |
| `subject` | TEXT | NOT NULL |
| `user_id` | TEXT | NOT NULL REFERENCES users(id) |
| `email` | TEXT | NOT NULL |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| — | *table constraint* | `PRIMARY KEY (provider, subject)` |

**Khóa ngoại:** `user_id` → `users`

## `families`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `id` | TEXT | PRIMARY KEY |
| `name` | TEXT | — |
| `created_by` | TEXT | NOT NULL REFERENCES users(id) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| `source` | TEXT | NOT NULL DEFAULT 'self' — thêm ở 0078_family_workspace.sql |
| `status` | TEXT | NOT NULL DEFAULT 'active' — thêm ở 0078_family_workspace.sql |
| `updated_at` | TEXT |  — thêm ở 0078_family_workspace.sql |

**Khóa ngoại:** `created_by` → `users`

**Index:** `idx_families_status`(status, source)

## `family_members`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `family_id` | TEXT | NOT NULL REFERENCES families(id) |
| `user_id` | TEXT | NOT NULL REFERENCES users(id) |
| `role` | TEXT | NOT NULL CHECK (role IN ('owner','guardian','supporter','learner')) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| — | *table constraint* | `PRIMARY KEY (family_id, user_id)` |

**Khóa ngoại:** `family_id` → `families` · `user_id` → `users`

## `guardians`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `learner_id` | TEXT | NOT NULL REFERENCES learners(id) |
| `user_id` | TEXT | NOT NULL REFERENCES users(id) |
| `relationship` | TEXT | NOT NULL DEFAULT 'parent' CHECK (relationship IN ('parent','grandparent','guardian','other')) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| — | *table constraint* | `PRIMARY KEY (learner_id, user_id)` |

**Khóa ngoại:** `learner_id` → `learners` · `user_id` → `users`

## `invitations`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `id` | TEXT | PRIMARY KEY |
| `family_id` | TEXT | NOT NULL REFERENCES families(id) |
| `learner_id` | TEXT | REFERENCES learners(id) |
| `invited_by` | TEXT | NOT NULL REFERENCES users(id) |
| `email` | TEXT | — |
| `code` | TEXT | NOT NULL UNIQUE |
| `role` | TEXT | NOT NULL DEFAULT 'learner' CHECK (role IN ('learner','guardian','supporter')) |
| `status` | TEXT | NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','accepted','expired','revoked')) |
| `expires_at` | TEXT | NOT NULL |
| `accepted_by` | TEXT | REFERENCES users(id) |
| `accepted_at` | TEXT | — |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |

**Khóa ngoại:** `family_id` → `families` · `learner_id` → `learners` · `invited_by` → `users` · `accepted_by` → `users`

**Index:** `idx_invitations_family`(family_id)

## `learners`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `id` | TEXT | PRIMARY KEY |
| `user_id` | TEXT | UNIQUE REFERENCES users(id) |
| `family_id` | TEXT | NOT NULL REFERENCES families(id) |
| `display_name` | TEXT | NOT NULL |
| `birth_year` | INTEGER | — |
| `grade` | INTEGER | — |
| `status` | TEXT | NOT NULL DEFAULT 'invited' CHECK (status IN ('invited','active','paused','archived')) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| `updated_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| `birth_date` | TEXT |  — thêm ở 0003_onboarding_models.sql |
| `current_school` | TEXT |  — thêm ở 0003_onboarding_models.sql |
| `province` | TEXT |  — thêm ở 0003_onboarding_models.sql |
| `profile_completed_at` | TEXT |  — thêm ở 0003_onboarding_models.sql |
| `learning_priority` | TEXT |  — thêm ở 0045_parent_declared_profile.sql |
| `profile_source` | TEXT |  — thêm ở 0045_parent_declared_profile.sql |
| `daily_intensity` | TEXT | NOT NULL DEFAULT 'light'
CHECK (daily_intensity IN ('light','medium','high')) — thêm ở 0061_daily_intensity.sql |
| `photo_media_id` | TEXT |  — thêm ở 0083_family_member_photos_nickname.sql |
| `nickname` | TEXT |  — thêm ở 0083_family_member_photos_nickname.sql |
| `facebook_url` | TEXT |  — thêm ở 0161_person_profile_fields.sql |
| `links_json` | TEXT |  — thêm ở 0161_person_profile_fields.sql |
| `interests` | TEXT |  — thêm ở 0161_person_profile_fields.sql |
| `expertise` | TEXT |  — thêm ở 0161_person_profile_fields.sql |
| `province_code` | TEXT | REFERENCES provinces(code) — thêm ở 0279_learner_geography_country_and_province_codes.sql |

**Khóa ngoại:** `user_id` → `users` · `family_id` → `families`

**Index:** `idx_learners_family`(family_id) · `idx_learners_province_code`(province_code)

## `role_assignments`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `user_id` | TEXT | NOT NULL REFERENCES users(id) |
| `role` | TEXT | NOT NULL CHECK (role IN ('learner','guardian','mentor','staff','admin')) |
| `scope_type` | TEXT | NOT NULL DEFAULT 'global' CHECK (scope_type IN ('global','school','family','learner')) |
| `scope_id` | TEXT | NOT NULL DEFAULT '\*' |
| `granted_by` | TEXT | REFERENCES users(id) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| — | *table constraint* | `PRIMARY KEY (user_id, role, scope_type, scope_id)` |

**Khóa ngoại:** `user_id` → `users` · `granted_by` → `users`

## `sessions`

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `id` | TEXT | PRIMARY KEY |
| `user_id` | TEXT | NOT NULL REFERENCES users(id) |
| `token_hash` | TEXT | NOT NULL UNIQUE |
| `expires_at` | TEXT | NOT NULL |
| `rotated_from` | TEXT | — |
| `revoked_at` | TEXT | — |
| `last_seen_at` | TEXT | — |
| `client` | TEXT | NOT NULL DEFAULT 'web' CHECK (client IN ('web','mobile')) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |

**Khóa ngoại:** `user_id` → `users`

**Index:** `idx_sessions_user`(user_id)

## `users`

Migration 0001 — Identity, Family, RBAC core (SDD-001 §6–7). Quy tắc QG-004: một sequence duy nhất, idempotent, email KHÔNG là khóa dữ liệu (RISK-001).

migration: `0001_identity.sql`

| Cột | Kiểu | Ràng buộc / ghi chú |
| --- | --- | --- |
| `id` | TEXT | PRIMARY KEY |
| `email` | TEXT | NOT NULL UNIQUE |
| `display_name` | TEXT | — |
| `avatar_media_id` | TEXT | — |
| `status` | TEXT | NOT NULL DEFAULT 'active' CHECK (status IN ('active','suspended','deleted')) |
| `created_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| `updated_at` | TEXT | NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')) |
| `avatar_url` | TEXT |  — thêm ở 0022_user_avatar.sql |
| `birth_year` | INTEGER | CHECK (birth_year IS NULL OR (birth_year >= 1940 AND birth_year <= 2020)) — thêm ở 0221_parent_profile.sql |
| `province` | TEXT | CHECK (province IS NULL OR length(province) <= 100) — thêm ở 0221_parent_profile.sql |
| `profile_completed_at` | TEXT |  — thêm ở 0221_parent_profile.sql |
| `country_code` | TEXT | CHECK (country_code IS NULL OR (length(country_code) = 2 AND country_code = upper(country_code))) — thêm ở 0279_learner_geography_country_and_province_codes.sql |
| `country_source` | TEXT | CHECK (country_source IS NULL OR country_source IN ('geo', 'declared')) — thêm ở 0279_learner_geography_country_and_province_codes.sql |
| `country_seen_at` | TEXT |  — thêm ở 0279_learner_geography_country_and_province_codes.sql |
| `province_code` | TEXT | REFERENCES provinces(code) — thêm ở 0279_learner_geography_country_and_province_codes.sql |
