Assignment
Data Entity
Description
A sensitive, encrypted dispatching record used by Blindeforbundet coordinators to securely send personal information (name, address, medical excerpts) to peer mentors. Assignment payloads are encrypted end-to-end using X25519 + AES-GCM and include tracking for dispatch, read confirmation, and completion status.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Globally unique identifier for the assignment record, generated server-side on dispatch. | PKrequiredunique |
coordinator_id |
uuid |
Foreign key referencing the users table — the coordinator who created and dispatched the assignment. Must have Coordinator role or higher in the associated local association. | required |
peer_mentor_id |
uuid |
Foreign key referencing the users table — the peer mentor who is the intended recipient of the encrypted assignment payload. | required |
contact_id |
uuid |
Optional foreign key referencing the contacts table — the person who is the subject of the assignment (e.g., the individual whose personal data is being shared). Nullable for assignments that do not relate to a specific contact record. | - |
local_association_id |
uuid |
Foreign key referencing the local_associations table. Scopes the assignment to a specific local association for honorarium threshold evaluation and coordinator visibility queries. | required |
encrypted_payload |
text |
Base64-encoded AES-GCM ciphertext containing the sensitive assignment content (name, address, medical excerpts, special needs). Encrypted client-side using the recipient peer mentor's X25519 public key via ECDH key agreement. Never stored in plaintext. | required |
ephemeral_public_key |
string |
Base64-encoded X25519 ephemeral public key generated by the sender for this specific assignment. Required by the recipient to derive the shared secret for AES-GCM decryption. Stored alongside the ciphertext. | required |
nonce |
string |
Base64-encoded 12-byte AES-GCM nonce (IV) used during encryption of this assignment payload. Must be unique per encrypted_payload; generated using a cryptographically secure random source. | requiredunique |
public_key_fingerprint |
string |
SHA-256 fingerprint (hex-encoded) of the recipient peer mentor's X25519 public key at the time of encryption. Used by the recipient to verify that their current private key corresponds to the key used during dispatch, and to detect key rotation issues. | required |
status |
enum |
Current lifecycle status of the assignment. Transitions are strictly ordered and validated server-side. 'dispatched' is the initial state set at creation; 'delivered' is set when the push notification is confirmed received; 'read' is set when the peer mentor opens the assignment; 'acknowledged' is set when the peer mentor submits explicit read confirmation; 'completed' is set when the assignment work is done; 'cancelled' can be set by the coordinator at any pre-completion stage. | required |
title |
string |
A non-sensitive reference label for the assignment visible to the coordinator in list views (e.g., 'Assignment #42' or a date-based reference). Must not contain personal data. Shown in coordinator dashboards without decryption. | required |
honorarium_relevant |
boolean |
Indicates whether this assignment counts toward the peer mentor's honorarium threshold counter. Set to false for test, cancelled, or administrative assignments. When true and status transitions to 'completed', the threshold-tracking-service increments the peer mentor's assignment counter. | required |
dispatched_at |
datetime |
UTC timestamp when the assignment was dispatched by the coordinator. Set server-side at record creation and immutable thereafter. | required |
read_at |
datetime |
UTC timestamp when the peer mentor first opened and viewed the decrypted assignment content. Set server-side when status transitions to 'read'. Null until the peer mentor opens the assignment. | - |
acknowledged_at |
datetime |
UTC timestamp when the peer mentor submitted explicit read confirmation via the ReadConfirmationWidget. Set server-side when status transitions to 'acknowledged'. Null until explicit confirmation is submitted. | - |
completed_at |
datetime |
UTC timestamp when the assignment was marked completed. Set server-side when status transitions to 'completed'. Triggers honorarium threshold evaluation if honorarium_relevant is true. | - |
cancelled_at |
datetime |
UTC timestamp when the assignment was cancelled by the coordinator. Set server-side when status transitions to 'cancelled'. Null for non-cancelled assignments. | - |
expires_at |
datetime |
Optional UTC timestamp after which the assignment is considered expired and the encrypted payload should no longer be accessible. Supports GDPR-aligned data minimisation for time-limited sensitive data sharing. | - |
created_at |
datetime |
UTC timestamp of record creation. Set server-side and immutable. | required |
updated_at |
datetime |
UTC timestamp of the last record modification. Updated automatically on any field change. | required |
Database Indexes
idx_assignment_coordinator_id
Columns: coordinator_id
idx_assignment_peer_mentor_id
Columns: peer_mentor_id
idx_assignment_contact_id
Columns: contact_id
idx_assignment_local_association_id
Columns: local_association_id
idx_assignment_status
Columns: status
idx_assignment_coordinator_status
Columns: coordinator_id, status
idx_assignment_peer_mentor_status
Columns: peer_mentor_id, status
idx_assignment_dispatched_at
Columns: dispatched_at
idx_assignment_nonce
Columns: nonce
idx_assignment_honorarium_relevant_status
Columns: peer_mentor_id, honorarium_relevant, status
Validation Rules
coordinator_id_is_valid_coordinator
error
Validation failed
peer_mentor_id_references_valid_peer_mentor
error
Validation failed
encrypted_payload_non_empty
error
Validation failed
ephemeral_public_key_valid_format
error
Validation failed
nonce_uniqueness
error
Validation failed
public_key_fingerprint_matches_registered_key
error
Validation failed
contact_id_coordinator_scope
error
Validation failed
title_no_personal_data
warning
Validation failed
expires_at_after_dispatched_at
error
Validation failed
status_update_actor_authorization
error
Validation failed
Business Rules
coordinator_role_required_for_dispatch
Only users with the Coordinator role or higher (Organization Administrator, Global Administrator) within the same local_association_id may create and dispatch assignments. Peer mentors cannot create assignments.
payload_must_be_encrypted_before_dispatch
The encrypted_payload field must contain a valid AES-GCM ciphertext encrypted with the recipient's X25519 public key. Plaintext payloads are rejected. Encryption is performed client-side in the Flutter app before the record is submitted to the backend.
status_transition_must_be_forward_only
Assignment status transitions must follow the defined lifecycle order: dispatched → delivered → read → acknowledged → completed. The 'cancelled' terminal state may be applied from any pre-completion status by the original coordinator. Backward transitions (e.g., read → dispatched) are rejected.
honorarium_counter_increment_on_completion
When an assignment transitions to 'completed' status and honorarium_relevant is true, the peer mentor's assignment counter is atomically incremented. The threshold-tracking-service evaluates whether a honorarium tier boundary has been crossed and emits a threshold-crossing notification event if applicable.
honorarium_counter_decrement_on_cancellation
When an assignment that was previously 'completed' is not applicable (edge case: administrative correction), or when a completed assignment count is adjusted, the assignment counter is decremented. This prevents erroneous honorarium tier escalation.
read_confirmation_requires_explicit_acknowledgement
The status transition from 'read' to 'acknowledged' requires the peer mentor to submit an explicit confirmation action through the ReadConfirmationWidget — passive scrolling or time-based triggers are not sufficient. This ensures legal certainty for Blindeforbundet's sensitive data delivery requirements.
encrypted_payload_inaccessible_after_expiry
If expires_at is set and the current timestamp exceeds it, the encrypted_payload field must not be returned in API responses. The backend returns a placeholder indicating the assignment payload has expired. This supports GDPR data minimisation for sensitive personal data.
only_recipient_can_decrypt
The encrypted_payload can only be decrypted by the peer mentor whose X25519 public key was used during encryption (identified by public_key_fingerprint). The coordinator can view assignment metadata (title, status, timestamps) but not the decrypted payload content.
peer_mentor_must_be_active
Assignments may only be dispatched to peer mentors whose status is 'active' (not paused, suspended, or deactivated). Attempting to dispatch to a paused or deactivated peer mentor returns a validation error and prompts the coordinator to check the mentor's status.
push_notification_on_dispatch
A push notification is sent to the recipient peer mentor immediately upon successful assignment dispatch, informing them that a new sensitive assignment awaits. The notification payload does not include sensitive content — only the assignment reference and a prompt to open the app.
calendar_feed_privacy_mask
When assignment records are included in iCal feed generation, the title, description, and contact information in the VEvent must be replaced with a generic placeholder (e.g., 'Encrypted Assignment') to prevent sensitive data leakage via third-party calendar integrations.
CRUD Operations
Storage Configuration
Entity Relationships
An assignment has an immutable log of all status transitions from dispatch through completion
A contact may be the subject of multiple encrypted assignments dispatched to different peer mentors
A peer mentor is the recipient of many encrypted assignments dispatched by coordinators
A coordinator (user) dispatches many encrypted assignments to peer mentors