Activity Attachment
Data Entity
Description
Files attached to activity records by peer mentors — photos, PDF invitations, Facebook screenshots, and other supporting documents uploaded to substantiate activities for Bufdir audit trails and coordinator review.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key | PKrequiredunique |
activity_id |
uuid |
Foreign key to the activity this attachment belongs to | required |
uploaded_by_user_id |
uuid |
User who uploaded the attachment (peer mentor or coordinator acting as proxy) | required |
file_name |
string |
Original filename as provided by the client at upload time | required |
file_size_bytes |
integer |
File size in bytes, recorded at upload for quota and display purposes | required |
mime_type |
string |
MIME type of the uploaded file (e.g. image/jpeg, application/pdf) | required |
attachment_type |
enum |
Semantic classification of the attachment for display and filtering | required |
storage_key |
string |
Opaque key used to retrieve the file from cloud object storage (e.g. S3/GCS path). Never exposed directly to clients. | requiredunique |
storage_bucket |
string |
Name of the cloud storage bucket holding this file, allowing bucket-per-tenant isolation | required |
cdn_url |
string |
Signed CDN URL for direct client download. Nullable — generated on demand and not persisted long-term. | - |
checksum_sha256 |
string |
SHA-256 hex digest of file contents for integrity verification and deduplication detection | required |
status |
enum |
Lifecycle state of the attachment record | required |
virus_scan_result |
enum |
Result from async virus/malware scan after upload | - |
virus_scanned_at |
datetime |
Timestamp when the virus scan completed | - |
organization_id |
uuid |
Denormalized organization scope for tenant isolation and multi-tenancy enforcement at the row level | required |
sort_order |
integer |
Display order of attachments within the activity, user-adjustable via the attachment thumbnail strip | - |
description |
text |
Optional free-text description added by the uploader to explain what the attachment shows | - |
offline_temp_id |
string |
Temporary client-side ID assigned when the attachment was queued offline. Used by the ID mapping service to correlate after sync. | - |
uploaded_at |
datetime |
Timestamp when the file was successfully received and stored | - |
deleted_at |
datetime |
Soft-delete timestamp. Null means the record is active. | - |
created_at |
datetime |
Record creation timestamp | required |
updated_at |
datetime |
Last modification timestamp | required |
Database Indexes
idx_activity_attachments_activity_id
Columns: activity_id, deleted_at
idx_activity_attachments_organization_id
Columns: organization_id, status
idx_activity_attachments_storage_key
Columns: storage_key
idx_activity_attachments_uploaded_by
Columns: uploaded_by_user_id, created_at
idx_activity_attachments_virus_scan
Columns: virus_scan_result, status
Validation Rules
allowed_mime_types
error
Validation failed
max_file_size_50mb
error
Validation failed
file_name_sanitization
error
Validation failed
checksum_integrity_on_store
error
Validation failed
storage_key_uniqueness
error
Validation failed
status_transition_forward_only
error
Validation failed
activity_must_exist_and_be_active
error
Validation failed
Business Rules
attachment_requires_completed_virus_scan
An attachment may not be displayed to any client until virus_scan_result is 'clean'. Attachments in 'pending' or 'virus_flagged' state must not be served via CDN URL.
max_attachments_per_activity
A single activity may have at most 10 active (non-deleted) attachments to prevent abuse and manage storage costs.
tenant_isolation
organization_id on the attachment must match organization_id of the parent activity. Cross-organization attachment access is forbidden at the API layer.
soft_delete_only
Attachments are never hard-deleted from the database. Setting deleted_at performs a logical delete. The backing file in object storage is purged asynchronously after a 30-day grace period to support recovery.
offline_upload_queued_to_outbox
When the device is offline, attachment upload is serialized into the mutation outbox with an offline_temp_id. The ID mapping service resolves the final server-assigned ID upon sync and updates all local references.
bufdir_audit_retention
Attachments linked to activities used in Bufdir reports must be retained for a minimum of 5 years from the report submission date to satisfy Norwegian grant audit requirements.
uploader_must_own_or_proxy_activity
Only the peer mentor who owns the activity or a coordinator with proxy rights over that peer mentor may upload or delete attachments.