Database Schema¶
This document describes the SQLite schema used by Open Codelabs.
ERD (Entity Relationship Diagram)¶
erDiagram
CODELABS ||--o{ STEPS : "contains"
CODELABS ||--o{ ATTENDEES : "enrolled_by"
CODELABS ||--o{ HELP_REQUESTS : "receives"
CODELABS ||--o{ CHAT_MESSAGES : "stores"
CODELABS ||--o{ INLINE_COMMENT_THREADS : "annotates"
CODELABS ||--o{ INLINE_COMMENT_MESSAGES : "stores"
CODELABS ||--o{ FEEDBACK : "collects"
CODELABS ||--o{ MATERIALS : "provides"
CODELABS ||--o{ QUIZZES : "includes"
CODELABS ||--o{ QUIZ_SUBMISSIONS : "aggregates"
CODELABS ||--o{ SUBMISSIONS : "collects"
CODELABS ||--o{ AI_CONVERSATIONS : "stores"
CODELABS ||--|| CODESERVER_WORKSPACES : "workspace"
CODELABS ||--o{ AUDIT_LOGS : "audits"
ATTENDEES ||--o{ HELP_REQUESTS : "makes"
ATTENDEES ||--o{ INLINE_COMMENT_THREADS : "creates"
INLINE_COMMENT_THREADS ||--o{ INLINE_COMMENT_MESSAGES : "contains"
ATTENDEES ||--o{ FEEDBACK : "submits"
ATTENDEES ||--o{ QUIZ_SUBMISSIONS : "answers"
ATTENDEES ||--o{ SUBMISSIONS : "uploads"
ATTENDEES ||--o{ AI_CONVERSATIONS : "asks"
QUIZZES ||--o{ QUIZ_SUBMISSIONS : "has"
Table reference¶
codelabs¶
Stores codelab metadata and configuration.
CREATE TABLE IF NOT EXISTS codelabs (
id VARCHAR(255) PRIMARY KEY NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
author VARCHAR(255) NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
is_public INTEGER NOT NULL DEFAULT 1,
quiz_enabled INTEGER DEFAULT 0,
require_quiz INTEGER DEFAULT 0,
require_feedback INTEGER DEFAULT 0,
guide_markdown TEXT
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
title |
VARCHAR | Codelab title | NOT NULL |
description |
TEXT | Codelab description | NOT NULL |
author |
VARCHAR | Author name | NOT NULL |
created_at |
TEXT | Created time | DEFAULT CURRENT_TIMESTAMP |
is_public |
INTEGER | Visibility (1: public, 0: private) | DEFAULT 1 |
quiz_enabled |
INTEGER | Quiz enabled | DEFAULT 0 |
require_quiz |
INTEGER | Require quiz to complete | DEFAULT 0 |
require_feedback |
INTEGER | Require feedback to complete | DEFAULT 0 |
guide_markdown |
TEXT | Prep guide Markdown | - |
steps¶
Stores individual steps in a codelab.
CREATE TABLE IF NOT EXISTS steps (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
step_number INTEGER NOT NULL,
title VARCHAR(255) NOT NULL,
content_markdown TEXT NOT NULL,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
step_number |
INTEGER | Step order (starts at 1) | NOT NULL |
title |
VARCHAR | Step title | NOT NULL |
content_markdown |
TEXT | Markdown content | NOT NULL |
attendees¶
Stores attendee information.
CREATE TABLE IF NOT EXISTS attendees (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
code VARCHAR(255) NOT NULL,
email VARCHAR(255),
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
current_step INTEGER DEFAULT 1,
is_completed INTEGER DEFAULT 0,
completed_at TEXT,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
name |
VARCHAR | Attendee name | NOT NULL |
code |
VARCHAR | Attendee code | NOT NULL |
email |
VARCHAR | Email (optional) | - |
current_step |
INTEGER | Current step number | DEFAULT 1 |
is_completed |
INTEGER | Completion status (1: completed, 0: in progress) | DEFAULT 0 |
completed_at |
TEXT | Completion time | - |
created_at |
TEXT | Registration time | DEFAULT CURRENT_TIMESTAMP |
help_requests¶
Stores attendee help requests.
CREATE TABLE IF NOT EXISTS help_requests (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
attendee_id VARCHAR(255) NOT NULL,
step_number INTEGER NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE,
FOREIGN KEY (attendee_id) REFERENCES attendees(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
attendee_id |
VARCHAR | Requester ID | FOREIGN KEY |
step_number |
INTEGER | Blocked step number | NOT NULL |
status |
VARCHAR | Status (pending/resolved) | DEFAULT 'pending' |
created_at |
TEXT | Request time | DEFAULT CURRENT_TIMESTAMP |
chat_messages¶
Stores real-time chat and DM messages.
CREATE TABLE IF NOT EXISTS chat_messages (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
sender_name VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
msg_type VARCHAR(50) DEFAULT 'chat',
target_id VARCHAR(255),
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
sender_name |
VARCHAR | Sender name | NOT NULL |
message |
TEXT | Message | NOT NULL |
msg_type |
VARCHAR | Type (chat/dm) | DEFAULT 'chat' |
target_id |
VARCHAR | DM target ID (attendee ID) | - |
created_at |
TEXT | Sent time | DEFAULT CURRENT_TIMESTAMP |
inline_comment_threads¶
Stores highlight anchors and thread metadata for guide/step inline comments.
CREATE TABLE IF NOT EXISTS inline_comment_threads (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
anchor_key VARCHAR(512) NOT NULL,
target_type VARCHAR(16) NOT NULL,
target_step_id VARCHAR(255),
start_offset INTEGER NOT NULL,
end_offset INTEGER NOT NULL,
selected_text TEXT NOT NULL,
content_hash VARCHAR(128) NOT NULL,
created_by_attendee_id VARCHAR(255) NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE,
UNIQUE (codelab_id, anchor_key)
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
anchor_key |
VARCHAR | Unique highlight anchor key | UNIQUE (with codelab_id) |
target_type |
VARCHAR | Target type (step/guide) |
NOT NULL |
target_step_id |
VARCHAR | Step ID when target_type=step |
- |
start_offset |
INTEGER | Start offset | NOT NULL |
end_offset |
INTEGER | End offset | NOT NULL |
selected_text |
TEXT | Selected source text | NOT NULL |
content_hash |
VARCHAR | Content version hash | NOT NULL |
created_by_attendee_id |
VARCHAR | Creator attendee ID | NOT NULL |
created_at |
TEXT | Created time | DEFAULT CURRENT_TIMESTAMP |
inline_comment_messages¶
Stores messages under inline comment threads.
CREATE TABLE IF NOT EXISTS inline_comment_messages (
id VARCHAR(255) PRIMARY KEY NOT NULL,
thread_id VARCHAR(255) NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
author_role VARCHAR(16) NOT NULL,
author_id VARCHAR(255) NOT NULL,
author_name VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (thread_id) REFERENCES inline_comment_threads(id) ON DELETE CASCADE,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
thread_id |
VARCHAR | Thread ID | FOREIGN KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
author_role |
VARCHAR | Author role (attendee/admin) |
NOT NULL |
author_id |
VARCHAR | Author identifier | NOT NULL |
author_name |
VARCHAR | Display author name | NOT NULL |
message |
TEXT | Message body | NOT NULL |
created_at |
TEXT | Created time | DEFAULT CURRENT_TIMESTAMP |
feedback¶
Stores attendee feedback.
CREATE TABLE IF NOT EXISTS feedback (
id VARCHAR(255) PRIMARY KEY,
codelab_id VARCHAR(255) NOT NULL,
attendee_id VARCHAR(255),
difficulty VARCHAR(50) NOT NULL,
satisfaction VARCHAR(50) NOT NULL,
comment TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
attendee_id |
VARCHAR | Submitter ID | UNIQUE (with codelab_id) |
difficulty |
VARCHAR | Difficulty (1-5) | NOT NULL |
satisfaction |
VARCHAR | Satisfaction (1-5) | NOT NULL |
comment |
TEXT | Comment (optional) | - |
created_at |
TEXT | Submitted time | DEFAULT CURRENT_TIMESTAMP |
materials¶
Stores materials attached to a codelab (links or files).
CREATE TABLE IF NOT EXISTS materials (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
material_type VARCHAR(50) NOT NULL,
link_url TEXT,
file_path TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
title |
VARCHAR | Material title | NOT NULL |
material_type |
VARCHAR | Type (link/file) | NOT NULL |
link_url |
TEXT | Link URL | - |
file_path |
TEXT | File path | - |
created_at |
TEXT | Registered time | DEFAULT CURRENT_TIMESTAMP |
quizzes¶
Stores quiz questions included in a codelab.
CREATE TABLE IF NOT EXISTS quizzes (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
question TEXT NOT NULL,
options TEXT NOT NULL,
correct_answer INTEGER NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
quiz_type TEXT DEFAULT 'multiple_choice'
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
question |
TEXT | Question | NOT NULL |
options |
TEXT | Options (JSON array) | NOT NULL |
correct_answer |
INTEGER | Correct index or value | NOT NULL |
quiz_type |
TEXT | Type (multiple_choice/descriptive) | DEFAULT 'multiple_choice' |
created_at |
TEXT | Created time | DEFAULT CURRENT_TIMESTAMP |
quiz_submissions¶
Stores attendee quiz submissions and grading results.
CREATE TABLE IF NOT EXISTS quiz_submissions (
id VARCHAR(255) PRIMARY KEY NOT NULL,
codelab_id VARCHAR(255) NOT NULL,
attendee_id VARCHAR(255) NOT NULL,
quiz_id VARCHAR(255) NOT NULL,
answer TEXT NOT NULL,
is_correct INTEGER NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE,
FOREIGN KEY (attendee_id) REFERENCES attendees(id) ON DELETE CASCADE,
FOREIGN KEY (quiz_id) REFERENCES quizzes(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
codelab_id |
VARCHAR | Codelab ID | FOREIGN KEY |
attendee_id |
VARCHAR | Attendee ID | FOREIGN KEY |
quiz_id |
VARCHAR | Quiz ID | FOREIGN KEY |
answer |
TEXT | Submitted answer | NOT NULL |
is_correct |
INTEGER | Correct (1) / incorrect (0) | NOT NULL |
created_at |
TEXT | Submitted time | DEFAULT CURRENT_TIMESTAMP |
submissions¶
Stores attendee submission files (assignments).
CREATE TABLE IF NOT EXISTS submissions (
id TEXT PRIMARY KEY,
codelab_id TEXT NOT NULL,
attendee_id TEXT NOT NULL,
file_path TEXT NOT NULL,
file_name TEXT NOT NULL,
file_size INTEGER NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs (id),
FOREIGN KEY (attendee_id) REFERENCES attendees (id)
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
TEXT | UUID | PRIMARY KEY |
codelab_id |
TEXT | Codelab ID | FOREIGN KEY |
attendee_id |
TEXT | Attendee ID | FOREIGN KEY |
file_path |
TEXT | Storage path | NOT NULL |
file_name |
TEXT | Original filename | NOT NULL |
file_size |
INTEGER | File size | NOT NULL |
created_at |
DATETIME | Submitted time | DEFAULT CURRENT_TIMESTAMP |
audit_logs¶
Stores admin/attendee event logs.
CREATE TABLE IF NOT EXISTS audit_logs (
id VARCHAR(255) PRIMARY KEY NOT NULL,
action VARCHAR(255) NOT NULL,
actor_type VARCHAR(50) NOT NULL,
actor_id VARCHAR(255),
target_id VARCHAR(255),
codelab_id VARCHAR(255),
ip VARCHAR(64),
user_agent TEXT,
metadata TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
VARCHAR | UUID | PRIMARY KEY |
action |
VARCHAR | Event key | NOT NULL |
actor_type |
VARCHAR | Actor type (admin/attendee) | NOT NULL |
actor_id |
VARCHAR | Actor ID | - |
target_id |
VARCHAR | Target ID | - |
codelab_id |
VARCHAR | Codelab ID | - |
ip |
VARCHAR | Request IP | - |
user_agent |
TEXT | User-Agent | - |
metadata |
TEXT | Extra metadata (JSON) | - |
created_at |
TEXT | Created time | DEFAULT CURRENT_TIMESTAMP |
codeserver_workspaces¶
Stores code-server workspaces.
CREATE TABLE IF NOT EXISTS codeserver_workspaces (
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
codelab_id TEXT NOT NULL UNIQUE,
url TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
structure_type TEXT NOT NULL DEFAULT 'branch',
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
TEXT | UUID | PRIMARY KEY |
codelab_id |
TEXT | Codelab ID | UNIQUE, FOREIGN KEY |
url |
TEXT | Workspace URL | NOT NULL |
structure_type |
TEXT | Structure type (branch/folder) | DEFAULT 'branch' |
created_at |
TEXT | Created time | DEFAULT datetime('now') |
ai_conversations¶
Stores AI Q&A history.
CREATE TABLE IF NOT EXISTS ai_conversations (
id TEXT PRIMARY KEY,
codelab_id TEXT NOT NULL,
user_id TEXT NOT NULL,
user_type TEXT NOT NULL,
user_name TEXT NOT NULL,
step_number INTEGER,
question TEXT NOT NULL,
answer TEXT NOT NULL,
model TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (codelab_id) REFERENCES codelabs(id) ON DELETE CASCADE
);
| Column | Type | Description | Constraints |
|---|---|---|---|
id |
TEXT | UUID | PRIMARY KEY |
codelab_id |
TEXT | Codelab ID | FOREIGN KEY |
user_id |
TEXT | User ID | NOT NULL |
user_type |
TEXT | User type (admin/attendee) | NOT NULL |
user_name |
TEXT | User name | NOT NULL |
step_number |
INTEGER | Step number | - |
question |
TEXT | Question | NOT NULL |
answer |
TEXT | Answer | NOT NULL |
model |
TEXT | Model name | - |
created_at |
TIMESTAMP | Created time | DEFAULT CURRENT_TIMESTAMP |
Migration list¶
The system uses sqlx to manage schema migrations. Files in backend/migrations/ are applied in order:
20251226161500_init.sql: create initial tables (codelabs, steps)20251226161600_attendees.sql: create attendees, help requests, chat tables20251226161700_chat_enhancements.sql: add chat type and target fields20251227001500_attendee_progress.sql: add attendee progress field20251227160000_create_feedback.sql: create feedback table20251227161000_add_attendee_feedback.sql: add submitter info and constraints20251227162000_add_is_public_to_codelabs.sql: add public flag to codelabs20251229150000_add_materials.sql: create materials table20251229160000_add_completion_to_attendees.sql: add completion status/time20251229161000_quizzes.sql: add quiz settings and tables20251230113000_add_quiz_type.sql: add quiz type field20251230120000_quiz_submissions.sql: create quiz submissions table20251231120000_add_guide_to_codelabs.sql: add guide field to codelabs20251231130000_submissions.sql: create submissions table20260102090000_audit_logs.sql: create audit logs table20260129000000_codeserver_containers.sql: create code-server workspaces table20260129100000_add_workspace_structure_type.sql: add workspace structure type20260130000000_add_email_to_attendees.sql: add attendee email field20260130010000_ai_conversations.sql: create AI conversations table20260213200000_inline_comments.sql: create inline comment thread/message tables