# SMOA database schema (server + device) **Version:** 2.0 **Last updated:** 2026-03-23 This document aligns **SMOA backend** (Flyway `V1__baseline.sql`, H2 or PostgreSQL) with **on-device Room** entities in this repository. Older rows that described only a generic local `credentials` table **without** a matching entity are removed. --- ## 1. Server database (SMOA backend) Source of truth: `backend/src/main/resources/db/migration/V1__baseline.sql`. JPA entities: `backend/src/main/kotlin/com/smoa/backend/domain/*.kt`. ### 1.1 `directory_entries` | Column | SQL type | Description | |--------|-----------|-------------| | id | VARCHAR(255) PK | Directory entry id | | name | VARCHAR(255) | Display name | | title | VARCHAR(255) | Job title | | unit | VARCHAR(255) | Unit / org | | phone_number | VARCHAR(255) | Phone | | extension | VARCHAR(255) | Extension | | email | VARCHAR(255) | Email | | secure_routing_id | VARCHAR(255) | Secure routing | | role | VARCHAR(255) | Role | | clearance_level | VARCHAR(255) | Clearance | | last_updated | BIGINT | Epoch ms | ### 1.2 `orders` | Column | SQL type | Description | |--------|-----------|-------------| | order_id | VARCHAR(255) PK | Order id | | order_type | VARCHAR(255) | Enum string (see backend validation) | | title | VARCHAR(255) | Title | | content | CLOB | Body | | issued_by | VARCHAR(255) | Issuer | | issued_to | VARCHAR(255) | Recipient | | issue_date | TIMESTAMP | Issued | | effective_date | TIMESTAMP | Effective | | expiration_date | TIMESTAMP | Expiry | | status | VARCHAR(255) | Status enum | | classification | VARCHAR(255) | Classification | | jurisdiction | VARCHAR(255) | Jurisdiction | | case_number | VARCHAR(255) | Case | | updated_at | BIGINT | Epoch ms | ### 1.3 `evidence` | Column | SQL type | Description | |--------|-----------|-------------| | evidence_id | VARCHAR(255) PK | Evidence id | | case_number | VARCHAR(255) | Case | | description | CLOB | Description | | evidence_type | VARCHAR(255) | Type enum | | collection_date | TIMESTAMP | Collected | | collection_location | VARCHAR(255) | Location | | collection_method | VARCHAR(255) | Method | | collected_by | VARCHAR(255) | Collector | | current_custodian | VARCHAR(255) | Custodian | | storage_location | VARCHAR(255) | Storage | | updated_at | BIGINT | Epoch ms | ### 1.4 `credentials` (server) | Column | SQL type | Description | |--------|-----------|-------------| | credential_id | VARCHAR(255) PK | Credential id | | holder_id | VARCHAR(255) | Subject / holder | | credential_type | VARCHAR(255) | Canonical type (`SmoaCredentialType` + legacy; see `docs/reference/IDENTITY-TEMPLATE-ALIGNMENT.md`) | | issuer | VARCHAR(255) | Issuing authority label | | issued_at | BIGINT | Epoch ms (optional) | | expires_at | BIGINT | Epoch ms (optional) | | payload_json | CLOB | JSON object: format-specific fields, optional `issuingAuthority`, `completeCredentialDomain`, `extensions` | | updated_at | BIGINT | Epoch ms | **Issuance validation:** Parse `payload_json` as JSON, then validate with `docs/schemas/credential-payload-*.schema.json` for the given `credential_type` on the sync **request body** (not duplicated inside payload). ### 1.5 `reports` | Column | SQL type | Description | |--------|-----------|-------------| | report_id | VARCHAR(255) PK | Report id | | report_type | VARCHAR(255) | Type enum | | title | VARCHAR(255) | Title | | format | VARCHAR(255) | PDF, XML, JSON, CSV, EXCEL | | generated_date | BIGINT | Epoch ms | | generated_by | VARCHAR(255) | Generator | | content | BLOB | Optional binary | | metadata_json | CLOB | Optional JSON | | updated_at | BIGINT | Epoch ms | ### 1.6 `sync_audit_log` | Column | SQL type | Description | |--------|-----------|-------------| | id | BIGINT IDENTITY PK | Surrogate key | | resource_type | VARCHAR(255) | Resource | | resource_id | VARCHAR(255) | Id | | operation | VARCHAR(255) | Operation | | success | BOOLEAN | Outcome | | principal | VARCHAR(255) | Optional actor | | timestamp | TIMESTAMP | Event time | --- ## 2. On-device Room (implemented modules) Room uses SQLite; module-specific databases. Below match Kotlin `@Entity` types. ### 2.1 Directory — `directory_entries` `modules/directory/.../DirectoryEntity.kt` | Column | Type | Notes | |--------|------|--------| | id | TEXT PK | | | name | TEXT | | | title | TEXT? | | | unit | TEXT | | | phoneNumber | TEXT? | camelCase in Kotlin → snake in DB per Room | | extension | TEXT? | | | email | TEXT? | | | secureRoutingId | TEXT? | | | role | TEXT? | | | clearanceLevel | TEXT? | | | lastUpdated | INTEGER | Epoch ms | ### 2.2 Orders — `orders` `modules/orders/.../OrderEntity.kt` | Column | Type | Notes | |--------|------|--------| | orderId | TEXT PK | | | orderType | TEXT | Enum persisted via converter | | title | TEXT | | | content | TEXT | | | issuedBy | TEXT | | | issuedTo | TEXT? | | | issueDate | INTEGER / Date | Type converters | | effectiveDate | | | | expirationDate | | | | status | | Enum | | classification | TEXT? | | | jurisdiction | TEXT | | | caseNumber | TEXT? | | | createdAt | | | | updatedAt | | | ### 2.3 Evidence — `evidence` `modules/evidence/.../EvidenceEntity.kt` | Column | Type | Notes | |--------|------|--------| | evidenceId | TEXT PK | | | caseNumber | TEXT | | | description | TEXT | | | evidenceType | | Enum | | collectionDate | | | | collectionLocation | TEXT | | | collectionMethod | TEXT | | | collectedBy | TEXT | | | currentCustodian | TEXT | | | storageLocation | TEXT? | | | createdAt | | | | updatedAt | | | ### 2.4 Custody — `custody_transfers` `modules/evidence/.../CustodyTransferEntity.kt` | Column | Type | Notes | |--------|------|--------| | transferId | TEXT PK | | | evidenceId | TEXT FK → evidence | | | timestamp | Date | | | fromCustodian | TEXT | | | toCustodian | TEXT | | | reason | TEXT | | | evidenceCondition | TEXT | | | signatureData | BLOB? | | | notes | TEXT? | | ### 2.5 Credentials cache — `credential_cache` `modules/credentials/.../CredentialCacheEntity.kt` (encrypted Room DB `credential_cache_database`) | Column | Type | Notes | |--------|------|--------| | credentialId | TEXT PK | | | holderId | TEXT | | | credentialType | TEXT | | | issuer | TEXT? | | | issuedAt | INTEGER? | Epoch ms | | expiresAt | INTEGER? | Epoch ms | | payloadJson | TEXT? | JSON string | | updatedAt | INTEGER | Epoch ms | Populate from pull/sync merge logic in application code when wired; schema matches server `credentials` for offline display. ### 2.6 Reports (device) No Room `ReportEntity` in this repo; reports remain server-backed unless a local entity is added later. --- ## 3. Indexes (server) - `idx_sync_audit_timestamp` on `sync_audit_log(timestamp)` --- ## 4. References - Backend Flyway: `backend/src/main/resources/db/migration/V1__baseline.sql` - Identity / payload schemas: `docs/reference/IDENTITY-TEMPLATE-ALIGNMENT.md`, `docs/schemas/` - Tenant model: `docs/security/TENANT-THREAT-MODEL.md`