Skip to content

Evidence Management

Status: Final Version: 1.2


Purpose

Define file handling, storage, retention, chain-of-custody, and immutability controls for evidence files supporting ESG metric submissions.


Evidence Types & File Formats

Category Evidence Type Code Allowed MIME Types Max Size Required For
Environmental UTILITY_BILL application/pdf, image/jpeg, image/png 10 MB Energy, water metrics
METER_READING image/*, application/pdf 5 MB Energy, water
LAB_REPORT application/pdf 10 MB Emissions, effluents, water quality
WASTE_MANIFEST application/pdf 10 MB Waste disposal
FUEL_LOG application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv 15 MB Diesel, petrol consumption
PRODUCTION_LOG application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/csv 15 MB Crushed ore, milled ore, gold production
WATER_METER_READING image/*, application/pdf 5 MB Water abstraction, consumption
EMISSIONS_MONITORING_REPORT application/pdf 10 MB Air quality monitoring (PM10, SO2, NO2, CO)
TSF_INSPECTION_REPORT application/pdf 10 MB TSF management (slurry density, freeboard, surface area, rate of rise)
REHAB_COMPLETION_RECORD application/pdf, image/* 10 MB Rehabilitation activities (MANDATORY for completed rehabilitation)
ENVIRONMENTAL_INCIDENT_REPORT application/pdf 10 MB Environmental incidents (MANDATORY for High/Critical severity incidents)
Social HR_REGISTER application/pdf, application/vnd.ms-excel, text/csv 25 MB Employment, diversity
TRAINING_RECORD application/pdf, image/* 10 MB Training hours
INCIDENT_REPORT application/pdf 10 MB General incidents
Social (OHS) ACCIDENT_REPORT application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document 10 MB Incident investigation, LTI, medical treatment, restricted work
INCIDENT_REGISTER application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 15 MB Near miss, high potential incidents, property damage
CLINIC_REGISTER application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 15 MB Other clinic visits, first aid incidents
FIRST_AID_REGISTER application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 15 MB First aid incidents
MEDICAL_TREATMENT_RECORD application/pdf 10 MB Medical treatment incidents, LTI days
LTI_REGISTER application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 15 MB Lost time injuries, LTI days, LTIFR
FATALITY_REPORT application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document 10 MB Work-related fatalities (MANDATORY)
INVESTIGATION_REPORT application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document 10 MB Fatality investigations, high potential incidents
PROPERTY_DAMAGE_REPORT application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document 10 MB Property damage incidents
OCCUPATIONAL_DISEASE_REGISTER application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 15 MB Silicosis, pneumoconiosis cases
HSE_REPORT application/pdf 10 MB Health, safety, and environmental reports
TIME_SHEET application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 15 MB Hours worked for LTIFR calculation
PAYROLL_REPORT application/pdf, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 25 MB Hours worked verification
Governance BOARD_MINUTES application/pdf 10 MB Board diversity
POLICY_DOCUMENT application/pdf 10 MB Governance disclosures
AUDIT_REPORT application/pdf 25 MB Assurance
Community Investment DONATION_RECEIPT application/pdf, image/* 5 MB Cash donations (MANDATORY if > threshold)
CSR_INVOICE application/pdf 10 MB Direct spend on goods/services
FUEL_DONATION_LOG application/pdf, application/vnd.ms-excel, text/csv 10 MB In-kind fuel support
AGREEMENT_MOU application/pdf 10 MB Long-term partnerships, sponsorships
BENEFICIARY_LETTER application/pdf, image/* 5 MB Acknowledgement of receipt (in-kind)
ACTIVITY_PHOTO image/* 10 MB Evidence of event/handover (RECOMMENDED)

File Upload Workflow

1. Client Upload

POST /api/v1/collector/submissions/{uuid}/evidence
Content-Type: multipart/form-data

file: <binary>
evidence_type: UTILITY_BILL
description: March 2025 electricity bill

2. Backend Processing

@ApplicationScoped
class EvidenceService(
    private val evidenceRepository: EvidenceRepository,
    private val submissionRepository: MetricSubmissionRepository,
    private val storageService: StorageService,
    private val virusScanService: VirusScanService,
    private val evidenceConfig: EvidenceConfig
) {
    @Transactional
    fun upload(
        submission: MetricSubmission,
        file: MultipartFile,
        evidenceType: String,
        description: String?,
        currentUser: User,
        clientIp: String
    ): Evidence {
        // 1. Validate file
        validateFile(file, evidenceType)

        // 2. Virus scan
        if (!virusScanService.scan(file)) {
            throw SecurityException("File failed virus scan")
        }

        // 3. Generate content hash
        val contentHash = MessageDigest.getInstance("SHA-256")
            .digest(file.bytes)
            .joinToString("") { "%02x".format(it) }

        // 4. Store immutably
        val extension = file.originalFilename?.substringAfterLast('.') ?: ""
        val filename = "${UUID.randomUUID()}.$extension"
        val path = "evidence/${submission.tenantId}/${submission.id}/$filename"

        storageService.store(path, file.bytes)

        // 5. Create evidence record
        val evidence = Evidence(
            id = UUID.randomUUID(),
            tenantId = submission.tenantId,
            filename = file.originalFilename ?: filename,
            filepath = path,
            fileSize = file.size,
            mimeType = file.contentType ?: "application/octet-stream",
            contentHash = contentHash,
            evidenceType = evidenceType,
            description = description,
            uploadedByUserId = currentUser.id,
            uploadedFromIp = clientIp,
            uploadedAt = Instant.now()
        )
        evidenceRepository.persist(evidence)

        // 6. Link to submission
        submission.evidence.add(evidence)
        submissionRepository.persist(submission)

        return evidence
    }

    private fun validateFile(file: MultipartFile, evidenceType: String) {
        val rules = evidenceConfig.types[evidenceType]
            ?: throw ValidationException("Unknown evidence type: $evidenceType")

        if (file.contentType !in rules.allowedMimeTypes) {
            throw ValidationException("File type not allowed for $evidenceType")
        }

        if (file.size > rules.maxSizeBytes) {
            throw ValidationException("File exceeds max size (${rules.maxSizeMb} MB)")
        }
    }
}

Virus Scanning

Integration: ClamAV

@ApplicationScoped
class VirusScanService(
    private val clamAvHost: String,
    private val clamAvPort: Int,
    private val logger: Logger = LoggerFactory.getLogger(VirusScanService::class.java)
) {
    fun scan(file: MultipartFile): Boolean {
        Socket(clamAvHost, clamAvPort).use { socket ->
            val outputStream = socket.getOutputStream()
            val inputStream = socket.getInputStream()

            // Send INSTREAM command to ClamAV
            outputStream.write("zINSTREAM\u0000".toByteArray())

            // Stream file in chunks
            file.bytes.chunked(2048).forEach { chunk ->
                val size = ByteBuffer.allocate(4).putInt(chunk.size).array()
                outputStream.write(size)
                outputStream.write(chunk.toByteArray())
            }

            // Send zero-length chunk to signal end
            outputStream.write(ByteArray(4))
            outputStream.flush()

            // Read result
            val result = inputStream.bufferedReader().readLine()

            if (result.contains("FOUND")) {
                logger.warn(
                    "Virus detected in file: ${file.originalFilename}, result: $result",
                    mapOf(
                        "file" to file.originalFilename,
                        "scanResult" to result
                    )
                )
                return false
            }

            return true
        }
    }
}

Storage Architecture

Quarkus S3 Configuration

# application.properties

# Quarkus Amazon S3 Configuration (quarkus-amazon-s3 extension)
quarkus.s3.aws.region=${AWS_EVIDENCE_REGION:us-east-1}
quarkus.s3.aws.credentials.type=static
quarkus.s3.aws.credentials.static-provider.access-key-id=${AWS_EVIDENCE_KEY}
quarkus.s3.aws.credentials.static-provider.secret-access-key=${AWS_EVIDENCE_SECRET}

# S3 endpoint URL (optional, for LocalStack or custom endpoints)
quarkus.s3.endpoint-override=${AWS_EVIDENCE_URL:}

# Custom application properties
storage.evidence.bucket=${AWS_EVIDENCE_BUCKET}
storage.evidence.type=s3
storage.evidence.encryption=AES256
storage.evidence.visibility=private

# Profile-specific configuration
%dev.storage.evidence.type=local
%dev.quarkus.s3.endpoint-override=http://localhost:4566
%test.storage.evidence.type=local

Quarkus S3 Client Injection:

@ApplicationScoped
class S3StorageService(
    private val s3Client: S3Client,
    @ConfigProperty(name = "storage.evidence.bucket")
    private val bucketName: String
) {
    fun store(path: String, content: ByteArray) {
        s3Client.putObject(
            PutObjectRequest.builder()
                .bucket(bucketName)
                .key(path)
                .serverSideEncryption(ServerSideEncryption.AES256)
                .build(),
            RequestBody.fromBytes(content)
        )
    }

    fun generatePresignedUrl(path: String, duration: Duration): String {
        val presigner = S3Presigner.builder()
            .region(Region.of(System.getenv("AWS_EVIDENCE_REGION") ?: "us-east-1"))
            .build()

        val presignRequest = GetObjectPresignRequest.builder()
            .signatureDuration(duration)
            .getObjectRequest { it.bucket(bucketName).key(path) }
            .build()

        return presigner.presignGetObject(presignRequest).url().toString()
    }
}

Signed URLs for Access

@Path("/api/v1/evidence")
@ApplicationScoped
class EvidenceResource(
    private val storageService: StorageService,
    private val evidenceRepository: EvidenceRepository,
    private val evidenceAuthorizationService: EvidenceAuthorizationService,
    private val auditLogRepository: AuditLogRepository,
    private val securityIdentity: SecurityIdentity
) {
    @GET
    @Path("/{evidenceId}/download")
    @RolesAllowed("EVIDENCE_VIEW")
    @Produces(MediaType.TEXT_PLAIN)
    fun download(
        @PathParam("evidenceId") evidenceId: UUID
    ): Response {
        val evidence = evidenceRepository.findByIdOptional(evidenceId)
            .orElseThrow { NotFoundException("Evidence not found") }

        // Get current user from SecurityIdentity
        val userId = securityIdentity.principal.name

        // Authorization check
        if (!evidenceAuthorizationService.canView(userId, evidence)) {
            throw ForbiddenException("Not authorized to view this evidence")
        }

        // Log access for audit
        auditLogRepository.persist(
            AuditLog(
                action = "evidence.accessed",
                entityType = "Evidence",
                entityId = evidence.id,
                userId = UUID.fromString(userId),
                timestamp = Instant.now()
            )
        )

        // Generate temporary signed URL (1 hour expiry)
        val signedUrl = storageService.generatePresignedUrl(
            evidence.filepath,
            Duration.ofHours(1)
        )

        return Response.ok(signedUrl).build()
    }
}

Retention Policy

Evidence Type Retention Period Legal Hold Support Auto-Delete After
All Evidence 7 years minimum ✅ Yes Never (manual only)
PII-Containing Subject to GDPR Right to Erasure ✅ Yes On request (anonymize)

Retention Policy Implementation

@Entity
@Table(name = "evidence")
data class Evidence(
    @Id
    val id: UUID,

    val tenantId: UUID,
    val filename: String,
    val filepath: String,
    val fileSize: Long,
    val mimeType: String,
    val contentHash: String,
    val evidenceType: String,
    val description: String?,

    val uploadedByUserId: UUID,
    val uploadedFromIp: String,
    val uploadedAt: Instant,

    val legalHold: Boolean = false,
    val retentionUntil: LocalDate? = null,
    val markedForDeletionAt: Instant? = null
) {
    fun canDelete(): Boolean {
        if (legalHold) {
            return false // Cannot delete under legal hold
        }

        if (retentionUntil != null && retentionUntil.isAfter(LocalDate.now())) {
            return false // Retention period not expired
        }

        return true
    }
}

// Scheduled task: Mark evidence eligible for deletion
@ApplicationScoped
class EvidenceRetentionScheduler(
    private val evidenceRepository: EvidenceRepository
) {
    @Scheduled(cron = "0 0 2 * * *") // Daily at 2 AM
    fun markEvidenceForDeletion() {
        val now = Instant.now()
        val today = LocalDate.now()

        val eligibleEvidence = evidenceRepository.findAll()
            .filter { it.retentionUntil != null && it.retentionUntil!!.isBefore(today) }
            .filter { !it.legalHold }
            .filter { it.markedForDeletionAt == null }

        eligibleEvidence.forEach { evidence ->
            evidenceRepository.persist(evidence.copy(markedForDeletionAt = now))
        }

        // Admin reviews and manually deletes
    }
}

OHS Evidence Requirements & Confidentiality

Overview

Occupational Health and Safety (OHS) evidence requires special handling due to the sensitive nature of incident data and potential inclusion of Personal Health Information (PHI). All OHS evidence must comply with confidentiality requirements and access controls defined in Security & Compliance.


Evidence Requirements by Incident Severity

OHS metrics follow a three-tier evidence requirement model based on incident severity and regulatory obligations:

Incident Type Severity Tier Evidence Requirement Minimum Evidence Types
Fatality MANDATORY Hard failure if missing (submission blocked) FATALITY_REPORT + INVESTIGATION_REPORT (minimum 2 items)
Lost Time Injury (LTI) MANDATORY Hard failure if missing LTI_REGISTER or ACCIDENT_REPORT (minimum 1 item)
High Potential Incident MANDATORY Hard failure if missing INCIDENT_REGISTER or INVESTIGATION_REPORT (minimum 1 item)
Silicosis/Pneumoconiosis MANDATORY Hard failure if missing OCCUPATIONAL_DISEASE_REGISTER or MEDICAL_TREATMENT_RECORD (minimum 1 item)
Medical Treatment RECOMMENDED Soft warning if missing (submission allowed) MEDICAL_TREATMENT_RECORD or ACCIDENT_REPORT
Restricted Work RECOMMENDED Soft warning if missing ACCIDENT_REPORT or HSE_REPORT
Property Damage RECOMMENDED Soft warning if missing PROPERTY_DAMAGE_REPORT or INCIDENT_REGISTER
First Aid OPTIONAL No validation check FIRST_AID_REGISTER or CLINIC_REGISTER
Near Miss OPTIONAL No validation check INCIDENT_REGISTER or HSE_REPORT
Other Clinic Visits OPTIONAL No validation check CLINIC_REGISTER

Rationale: - MANDATORY tier: Incidents with severe consequences (fatalities, LTIs, high potential incidents, occupational diseases) require regulatory notification, investigation, and audit trail. Evidence is non-negotiable. - RECOMMENDED tier: Incidents requiring medical intervention or work restrictions should have supporting evidence for audit purposes, but lack of evidence does not block submission (allows data capture during emergencies). - OPTIONAL tier: Minor incidents and near misses are tracked for trending but do not require formal evidence (reduces collector burden).


Confidentiality Classification

All OHS evidence is classified as CONFIDENTIAL and must comply with the following requirements:

  1. Data Sensitivity:
  2. All incident data (aggregated counts, evidence files) is CONFIDENTIAL
  3. Medical treatment records containing diagnosis or Personal Health Information (PHI) require additional protection beyond standard confidential classification

  4. Encryption Requirements:

  5. At Rest: All OHS evidence files stored with S3 Server-Side Encryption (SSE-AES256)
  6. In Transit: TLS 1.2+ required for all uploads and downloads
  7. Medical Records with PHI: Additional field-level encryption required (see Security & Compliance)

  8. Anonymization Requirements:

  9. Aggregated Incident Counts: No individual employee names or identifiable information in metric submissions
  10. Evidence Files: Redact employee names, ID numbers, and medical diagnoses before upload
  11. Fatality Reports: May contain victim information for regulatory notification, but must be restricted to authorized personnel only

  12. Retention Requirements:

  13. Standard Retention: 7 years minimum (aligns with regulatory and audit requirements)
  14. Fatality Evidence: 10 years minimum (extended retention for legal and regulatory compliance)
  15. Medical Records with PHI: Subject to GDPR Right to Erasure (anonymize on request, keep aggregated counts)

Access Controls for OHS Evidence

OHS evidence access is restricted by role and follows the principle of least privilege:

Role Access Permissions Use Case
Collector Can upload OHS evidence for own site submissions only Submit quarterly incident statistics and LTI performance data
Reviewer Can view OHS evidence for assigned sites (site or business unit scope) Validate evidence completeness and quality
Approver Can view and approve OHS submissions and evidence for entire organisation Formal sign-off on quarterly OHS reports
Admin Full access to all OHS evidence (with mandatory access logging and audit justification) System administration, troubleshooting, evidence retention management
Auditor Read-only access to all OHS evidence for assurance purposes External or internal audit, GRI 403 assurance
HSE Manager Can view and export OHS evidence across organisation (read-only) Analyse safety trends, prepare management reports

Additional Controls: - Evidence Access Logging: All views and downloads of OHS evidence are logged to audit_logs table with user ID, timestamp, IP address, and evidence ID - Medical Records: Access restricted to HSE Manager, Approver, Admin, and Auditor roles only (Collectors and Reviewers cannot view medical records after upload) - Fatality Evidence: Access restricted to Approver, Admin, Auditor, and HSE Manager only (requires additional audit justification) - Regulatory Notification: Fatality reports may require external sharing with regulatory authorities (redact non-essential PHI before sharing)


Evidence Validation Rules

OHS evidence validation follows the tiered model with conditional checks based on incident counts:

// Example validation for OHS incident evidence
@ApplicationScoped
class OHSEvidenceValidator(
    private val evidenceRepository: EvidenceRepository,
    private val validationConfig: ValidationConfig
) {
    fun validateIncidentEvidence(submission: MetricSubmission): List<ValidationError> {
        val errors = mutableListOf<ValidationError>()
        val rawData = submission.rawData as Map<String, Any>
        val evidence = evidenceRepository.findBySubmissionId(submission.id)
        val evidenceTypes = evidence.map { it.evidenceType }

        // MANDATORY: Fatality evidence (minimum 2 items)
        val fatalityCount = getIncidentCount(rawData, "fatality")
        if (fatalityCount > 0) {
            val hasFatalityReport = evidenceTypes.contains("FATALITY_REPORT")
            val hasInvestigationReport = evidenceTypes.contains("INVESTIGATION_REPORT")
            if (!hasFatalityReport || !hasInvestigationReport) {
                errors.add(ValidationError(
                    "Fatality incidents require both FATALITY_REPORT and INVESTIGATION_REPORT (minimum 2 attachments)"
                ))
            }
        }

        // MANDATORY: LTI evidence (minimum 1 item)
        val ltiCount = getIncidentCount(rawData, "lti")
        if (ltiCount > 0) {
            val hasLtiEvidence = evidenceTypes.any { it in listOf("LTI_REGISTER", "ACCIDENT_REPORT") }
            if (!hasLtiEvidence) {
                errors.add(ValidationError(
                    "LTI incidents require at least one of: LTI_REGISTER, ACCIDENT_REPORT"
                ))
            }
        }

        // RECOMMENDED: Medical treatment evidence (soft warning)
        val medicalTreatmentCount = getIncidentCount(rawData, "medical_treatment")
        if (medicalTreatmentCount > 0) {
            val hasMedicalEvidence = evidenceTypes.any { it in listOf("MEDICAL_TREATMENT_RECORD", "ACCIDENT_REPORT") }
            if (!hasMedicalEvidence) {
                warnings.add(ValidationWarning(
                    "Medical treatment incidents should include MEDICAL_TREATMENT_RECORD or ACCIDENT_REPORT"
                ))
            }
        }

        return errors
    }

    private fun getIncidentCount(rawData: Map<String, Any>, incidentType: String): Int {
        // Extract mine + contractor count for incident type
        val incidentData = rawData[incidentType] as? Map<String, Any> ?: return 0
        val mine = incidentData["mine"] as? Int ?: 0
        val contractors = incidentData["contractors"] as? Int ?: 0
        return mine + contractors
    }
}

PHI Redaction Guidance

Evidence files uploaded for OHS metrics must be redacted to protect Personal Health Information (PHI) and employee privacy:

Files Requiring Redaction: - Medical treatment records (diagnosis, prescription, treatment plan) - Fatality reports (victim name, next of kin contact information) - Occupational disease registers (employee name, medical history) - Accident reports (individual employee names, witness statements with names)

Acceptable Evidence Content: - Incident summary (date, time, location, type) - Root cause analysis and corrective actions - Aggregated statistics (total incidents, total LTI days) - Investigation findings and recommendations - Regulatory notification reference (case number, not individual details)

Redaction Process: 1. Use PDF redaction tools to permanently remove PHI (black boxes, not just highlights) 2. Export summary-only reports from HSE systems (exclude individual employee records) 3. Manually remove employee names and ID numbers from registers before upload 4. Add redaction note in evidence description: "Individual identifiers redacted for privacy"

Platform Feature (vNext): Automated PHI detection in uploaded files with warning prompts before upload.


Cross-References


Environmental Evidence Requirements & Confidentiality

Overview

Environmental evidence supports the collection and reporting of environmental metrics across production, materials consumption, energy usage, water consumption, air emissions, waste management, TSF management, rehabilitation activities, and environmental incidents. Environmental evidence requirements vary based on metric sensitivity and regulatory obligations.


Evidence Requirements by Metric Category

Environmental metrics follow a three-tier evidence requirement model based on regulatory requirements and data criticality:

Metric Category Evidence Tier Evidence Requirement Minimum Evidence Types
Environmental Incidents (High/Critical) MANDATORY Hard failure if missing (submission blocked) ENVIRONMENTAL_INCIDENT_REPORT (minimum 1 item)
Rehabilitation Activities (Completed) MANDATORY Hard failure if missing REHAB_COMPLETION_RECORD (minimum 1 item)
Water Quality Monitoring RECOMMENDED Soft warning if missing (submission allowed) LAB_REPORT (water quality test results)
Air Emissions Monitoring RECOMMENDED Soft warning if missing EMISSIONS_MONITORING_REPORT
Waste Disposal RECOMMENDED Soft warning if missing WASTE_MANIFEST
TSF Management RECOMMENDED Soft warning if missing TSF_INSPECTION_REPORT
Production Metrics OPTIONAL No validation check PRODUCTION_LOG
Energy Metrics OPTIONAL No validation check UTILITY_BILL, METER_READING, FUEL_LOG
Water Abstraction/Consumption OPTIONAL No validation check WATER_METER_READING, UTILITY_BILL
Materials Consumption OPTIONAL No validation check Invoices, stock cards, delivery notes

Rationale: - MANDATORY tier: Environmental incidents with severe consequences (High/Critical severity) and completed rehabilitation activities require regulatory notification, documentation, and audit trail. Evidence is non-negotiable for compliance and assurance. - RECOMMENDED tier: Monitoring data (water quality, air emissions, waste, TSF) should have supporting evidence for audit purposes, but lack of evidence does not block submission (allows data capture when lab reports or inspection records are delayed). - OPTIONAL tier: Operational metrics (production, energy, water volumes, materials) are tracked for performance monitoring but do not require formal evidence (reduces collector burden for routine metrics).


Confidentiality Classification

Environmental evidence is classified based on sensitivity and regulatory/commercial considerations:

  1. Environmental Incidents (G.10):
  2. Classification: CONFIDENTIAL
  3. Rationale: High/Critical severity incidents may involve regulatory enforcement, reputational risk, and community impact. Incident details (descriptions, actions, root causes) are sensitive.
  4. Access Restrictions: Evidence restricted to Approver, Admin, Auditor, and Environmental Manager roles only

  5. Rehabilitation Activities (G.9):

  6. Classification: INTERNAL
  7. Rationale: Rehabilitation costs and completion status may be commercially sensitive but are generally disclosed in sustainability reports
  8. Access Restrictions: Standard RBAC permissions (Collector, Reviewer, Approver, Admin, Auditor)

  9. General Environmental Data (G.1-G.8):

  10. Classification: INTERNAL
  11. Rationale: Production, energy, water, emissions, waste, and TSF data are typically disclosed in sustainability reports (GRI 301-306) with organizational-level aggregation
  12. Access Restrictions: Standard RBAC permissions

Encryption Requirements

All environmental evidence files must comply with standard encryption requirements:

  • At Rest: All evidence files stored with S3 Server-Side Encryption (SSE-AES256)
  • In Transit: TLS 1.2+ required for all uploads and downloads
  • High/Critical Incidents: Additional field-level encryption RECOMMENDED (SSE-KMS with customer-managed keys) for reputational risk management

Retention Requirements

Evidence Category Retention Period Legal Hold Support Auto-Delete After
Environmental Incidents (High/Critical) 10 years minimum ✅ Yes Never (manual only)
Rehabilitation Completion Records 10 years minimum ✅ Yes Never (manual only)
Water Quality Lab Reports 7 years minimum ✅ Yes Never (manual only)
Emissions Monitoring Reports 7 years minimum ✅ Yes Never (manual only)
TSF Inspection Reports 7 years minimum ✅ Yes Never (manual only)
General Environmental Evidence 7 years minimum ✅ Yes Never (manual only)

Rationale: - Environmental incidents and rehabilitation records have extended retention (10 years) due to potential long-term regulatory and legal implications - Monitoring data (water quality, air emissions, TSF inspections) retained for 7 years to support regulatory compliance and audit requirements - All environmental evidence subject to legal hold (mining operations may face retrospective regulatory investigations)


Access Controls for Environmental Evidence

Environmental evidence access is restricted by role and follows the principle of least privilege:

Role Access Permissions Use Case
Collector Can upload environmental evidence for own site submissions only Submit monthly production, materials, energy, water, waste, TSF data; quarterly water quality, air emissions, rehabilitation, and incident logs
Reviewer Can view environmental evidence for assigned sites (site or business unit scope) Validate evidence completeness and quality for assigned sites
Approver Can view and approve environmental submissions and evidence for entire organisation Formal sign-off on quarterly/annual environmental reports
Admin Full access to all environmental evidence (with mandatory access logging and audit justification) System administration, troubleshooting, evidence retention management
Auditor Read-only access to all environmental evidence for assurance purposes External or internal audit, GRI 301-307 assurance
Environmental Manager Can view and export environmental evidence across organisation (read-only) Analyse environmental trends, prepare management reports, regulatory compliance

Additional Controls: - Evidence Access Logging: All views and downloads of environmental evidence are logged to audit_logs table with user ID, timestamp, IP address, and evidence ID - Incident Evidence (High/Critical): Access restricted to Approver, Admin, Auditor, and Environmental Manager roles only (Collectors and Reviewers cannot view high/critical incident evidence after upload) - Regulatory Notification: Environmental incident reports may require external sharing with regulatory authorities (redact non-essential operational details before sharing)


Evidence Validation Rules

Environmental evidence validation follows the tiered model with conditional checks based on incident severity and rehabilitation status:

// Example validation for environmental incident evidence
@ApplicationScoped
class EnvironmentalEvidenceValidator(
    private val evidenceRepository: EvidenceRepository,
    private val validationConfig: ValidationConfig
) {
    fun validateEnvironmentalEvidence(submission: MetricSubmission): List<ValidationError> {
        val errors = mutableListOf<ValidationError>()
        val rawData = submission.rawData as Map<String, Any>
        val evidence = evidenceRepository.findBySubmissionId(submission.id)
        val evidenceTypes = evidence.map { it.evidenceType }

        // MANDATORY: High/Critical environmental incident evidence
        val incidents = rawData["environmental_incidents"] as? List<Map<String, Any>> ?: emptyList()
        val highCriticalIncidents = incidents.filter {
            val severity = it["severity"] as? String ?: ""
            severity in listOf("High", "Critical")
        }

        if (highCriticalIncidents.isNotEmpty()) {
            val hasIncidentReport = evidenceTypes.contains("ENVIRONMENTAL_INCIDENT_REPORT")
            if (!hasIncidentReport) {
                errors.add(ValidationError(
                    "High/Critical environmental incidents require ENVIRONMENTAL_INCIDENT_REPORT"
                ))
            }
        }

        // MANDATORY: Completed rehabilitation activity evidence
        val rehabActivities = rawData["rehabilitation_activities"] as? List<Map<String, Any>> ?: emptyList()
        val completedRehab = rehabActivities.filter {
            val status = it["closure_status"] as? String ?: ""
            status == "Closed"
        }

        if (completedRehab.isNotEmpty()) {
            val hasCompletionRecord = evidenceTypes.contains("REHAB_COMPLETION_RECORD")
            if (!hasCompletionRecord) {
                errors.add(ValidationError(
                    "Completed rehabilitation activities require REHAB_COMPLETION_RECORD"
                ))
            }
        }

        // RECOMMENDED: Water quality lab reports (soft warning)
        val waterQuality = rawData["water_quality_monitoring"] as? List<Map<String, Any>> ?: emptyList()
        if (waterQuality.isNotEmpty()) {
            val hasLabReport = evidenceTypes.contains("LAB_REPORT")
            if (!hasLabReport) {
                warnings.add(ValidationWarning(
                    "Water quality monitoring should include LAB_REPORT with test results"
                ))
            }
        }

        return errors
    }
}

Cross-References



Community Investment Evidence Requirements

Overview

Community Investment (CSR/CSIR) evidence validates financial and in-kind contributions to community development, education, health, and sports. Evidence requirements balance the need for financial auditability with the practical realities of community-based activities (where formal receipts may sometimes be replaced by acknowledgement letters).


Evidence Requirements by Contribution Type

Community Investment evidence requirements are determined by the nature of the contribution (Cash vs. In-Kind) and the value involved:

Contribution Type Evidence Requirement Minimum Evidence Types
Cash Donation > Threshold MANDATORY Hard failure if missing
Cash Donation < Threshold RECOMMENDED Soft warning if missing
In-Kind (Goods/Services) MANDATORY Hard failure if missing
In-Kind (Fuel) MANDATORY Hard failure if missing
Sponsorship MANDATORY Hard failure if missing
Community Event RECOMMENDED Soft warning if missing

Rationale: - Financial Auditability: Direct cash spend and purchasing of goods for donation require strict financial evidence (invoices, receipts) to prevent fraud and ensuring tax compliance. - In-Kind Verification: Fuel and goods handed over to beneficiaries require acknowledgement (Beneficiary Letter or Log) to prove the aid reached the intended recipient. - Visual Evidence: Photos are recommended to build a "portfolio of evidence" for sustainability reports but are not sufficient on their own for financial audit.


Confidentiality Classification

Community Investment evidence is generally classified as INTERNAL, with specific privacy considerations:

  1. Beneficiary Privacy:
  2. Classification: INTERNAL (Restricted)
  3. Requirement: Avoid capturing PII of individual beneficiaries (e.g., students, patients) in photos or lists unless consent is obtained.
  4. Prefer: Aggregate beneficiary letters (e.g., from a School Principal) over lists of individual student names.

  5. Financial Records:

  6. Classification: INTERNAL
  7. Requirement: Invoices and receipts typically contain supplier and pricing details which are internal business records.

  8. Strategic Partnerships:

  9. Classification: INTERNAL or CONFIDENTIAL (depending on MOU terms)
  10. Requirement: MOUs may contain confidentiality clauses regarding sponsorship values before public announcement.

Access Controls

Role Access Permissions Use Case
Collector Uploads evidence for site/org activities Submitting quarterly CSR log entries
Reviewer View access for validation Verifying that spend matches evidence
Approver View access for sign-off Ensuring compliance with CSR policy
Auditor Read-only access verifying financial spend vs reported impact
External Stakeholders No direct access (Data is aggregated for public reports)

Evidence Validation Rules

// Example validation for Community Investment evidence
@ApplicationScoped
class CommunityInvestmentEvidenceValidator(
    private val evidenceRepository: EvidenceRepository
) {
    fun validate(submission: CommunityInvestmentLog): List<ValidationError> {
        val errors = mutableListOf<ValidationError>()
        val evidence = evidenceRepository.findByEntityId(submission.id)
        val evidenceTypes = evidence.map { it.evidenceType }

        // Rule: Cash spend > $1000 requires Receipt or Invoice
        if (submission.actualAmount > 1000.0 && submission.currency == "USD") {
             val hasFinancialProof = evidenceTypes.any { 
                 it in listOf("DONATION_RECEIPT", "CSR_INVOICE", "AGREEMENT_MOU") 
             }
             if (!hasFinancialProof) {
                 errors.add(ValidationError("Cash spend over $1000 requires DONATION_RECEIPT, CSR_INVOICE, or AGREEMENT_MOU"))
             }
        }

        // Rule: Fuel donations require Fuel Log
        if (submission.description.contains("Fuel", ignoreCase = true)) {
            if (!evidenceTypes.contains("FUEL_DONATION_LOG")) {
                errors.add(ValidationError("Fuel donations require a FUEL_DONATION_LOG attachment"))
            }
        }

        return errors
    }
}

Chain of Custody

Tracked Fields: - uploaded_by_user_id: Who uploaded - uploaded_from_ip: Source IP address - uploaded_at: Timestamp - content_hash: SHA-256 hash (immutability verification) - Access logs: Every download/view logged to audit_logs


Acceptance Criteria

  • File type whitelist enforced (MIME type check)
  • Max file size validated (per evidence type)
  • Virus scanning blocks infected files
  • Files stored with encryption at rest (S3 SSE)
  • Content hash generated and stored
  • Signed URLs expire after 1 hour
  • All evidence access logged to audit trail
  • Retention policy prevents premature deletion

Cross-References


Change Log

Version Date Author Changes
1.3 2026-01-21 Ralph Agent Added Community Investment Evidence section (types: DONATION_RECEIPT, CSR_INVOICE, FUEL_DONATION_LOG, AGREEMENT_MOU, BENEFICIARY_LETTER, ACTIVITY_PHOTO), defined mandatory/recommended tiers based on spend thresholds and in-kind nature, and documented confidentiality/privacy rules for beneficiary data.
1.2 2026-01-19 Ralph Agent Added Environmental Evidence Requirements & Confidentiality section covering: 8 environmental evidence types (FUEL_LOG, PRODUCTION_LOG, WATER_METER_READING, EMISSIONS_MONITORING_REPORT, TSF_INSPECTION_REPORT, REHAB_COMPLETION_RECORD, ENVIRONMENTAL_INCIDENT_REPORT), evidence requirements by metric category (three-tier model: MANDATORY for High/Critical incidents and completed rehabilitation, RECOMMENDED for monitoring data, OPTIONAL for operational metrics), confidentiality classification (CONFIDENTIAL for incidents, INTERNAL for general environmental data), access controls by role (Collector, Reviewer, Approver, Admin, Auditor, Environmental Manager), retention requirements (10 years for incidents/rehabilitation, 7 years for monitoring data), evidence validation rules implementation, and cross-references to environmental sections. Extended Evidence Types table with 8 environmental evidence types. Updated LAB_REPORT description to include water quality.
1.1 2026-01-18 Ralph Agent Added OHS Evidence Requirements & Confidentiality section covering: 14 OHS evidence types (ACCIDENT_REPORT, INCIDENT_REGISTER, CLINIC_REGISTER, FIRST_AID_REGISTER, MEDICAL_TREATMENT_RECORD, LTI_REGISTER, FATALITY_REPORT, INVESTIGATION_REPORT, PROPERTY_DAMAGE_REPORT, OCCUPATIONAL_DISEASE_REGISTER, HSE_REPORT, TIME_SHEET, PAYROLL_REPORT), evidence requirements by incident severity (three-tier model: MANDATORY, RECOMMENDED, OPTIONAL), confidentiality classification (all OHS data is CONFIDENTIAL, medical records with PHI require additional encryption), access controls by role (Collector, Reviewer, Approver, Admin, Auditor, HSE Manager), evidence validation rules implementation, PHI redaction guidance, and cross-references to OHS sections. Extended Evidence Types table with 14 OHS evidence types including allowed MIME types and max file sizes.
1.0 2026-01-03 Senior Product Architect Initial evidence management specification