Skip to content

πŸ“Ž Evidence Collection MVP

Scope: Simple evidence storage and retrieval for compliance Status: Basic file upload with metadata tagging Target Users: IT managers uploading policies, screenshots, certificates Not: Complex document management system

Customer Need

Auditors ask: "Show me evidence of [specific control]" Boards ask: "How do we know our controls are working?" Managers need: "Where did I put that firewall config screenshot?"

MVP Answer: Tagged file storage with easy retrieval

Core MVP Features

1. Smart Upload Interface

interface Evidence {
  id: string
  fileName: string
  fileType: 'pdf' | 'image' | 'document' | 'spreadsheet'
  fileSize: number
  uploadedBy: User
  uploadedAt: Date

  // Smart tagging
  tags: {
    frameworks: string[] // ['E8', 'ACSC', 'S180', 'Privacy']
    controls: string[] // ['MFA', 'Patching']
    evidenceType: string // 'Policy', 'Screenshot', 'Report'
    period?: string // '2024-Q1'
  }

  // Metadata
  description?: string
  validFrom?: Date
  validUntil?: Date

  // Relationships
  linkedTo: {
    tasks?: string[]
    assessments?: string[]
    vendors?: string[]
  }
}

2. Drag & Drop Upload

<DropZone
  on:drop={handleFiles}
  accept=".pdf,.png,.jpg,.docx,.xlsx"
  maxSize="50MB"
>
  <div class="upload-prompt">
    <Upload size={48} />
    <p>Drop evidence files here</p>
    <p class="hint">PDFs, images, documents up to 50MB</p>
  </div>
</DropZone>

<!-- Auto-tag based on filename -->
<script>
  function autoTag(filename) {
    // "MFA-Policy-2024.pdf" β†’ tags: ['MFA', 'Policy', '2024']
    // "essential-eight-assessment.xlsx" β†’ tags: ['E8', 'Assessment']
    return smartParse(filename);
  }
</script>

3. Quick Retrieval

Find Evidence By:
  - Framework: 'Show all E8 evidence'
  - Control: 'Show MFA evidence'
  - Date: 'Evidence from last audit'
  - Type: 'All policies'
  - Expiry: 'Expiring in 30 days'

One-Click Actions:
  - Preview (in-browser)
  - Download
  - Share link (time-limited)
  - Add to report

Storage Architecture

File Organization

{tenant-id}/
  /evidence/
    /2024/
      /01/
        /{uuid}-MFA-Policy.pdf
        /{uuid}-Firewall-Config.png
      /02/
        /{uuid}-Pen-Test-Report.pdf

Metadata Storage

CREATE TABLE evidence (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  file_name TEXT NOT NULL,
  file_path TEXT NOT NULL,
  file_type TEXT NOT NULL,
  file_size BIGINT NOT NULL,

  -- Searchable tags
  framework_tags TEXT[],
  control_tags TEXT[],
  evidence_type TEXT,
  period TEXT,

  -- Metadata
  description TEXT,
  valid_from DATE,
  valid_until DATE,

  -- Audit trail
  uploaded_by UUID REFERENCES users(id),
  uploaded_at TIMESTAMPTZ DEFAULT NOW(),
  last_accessed_at TIMESTAMPTZ,
  access_count INTEGER DEFAULT 0,

  tenant_id UUID NOT NULL,

  -- Full text search
  search_vector tsvector GENERATED ALWAYS AS (
    to_tsvector('english',
      coalesce(file_name, '') || ' ' ||
      coalesce(description, '') || ' ' ||
      array_to_string(framework_tags, ' ') || ' ' ||
      array_to_string(control_tags, ' ')
    )
  ) STORED
);

CREATE INDEX idx_evidence_search ON evidence USING GIN(search_vector);
CREATE INDEX idx_evidence_tags ON evidence USING GIN(framework_tags, control_tags);
CREATE INDEX idx_evidence_expiry ON evidence(valid_until) WHERE valid_until IS NOT NULL;

Smart Features

1. Auto-Tagging

const TAG_PATTERNS = {
  frameworks: {
    'E8|essential.?eight': ['E8'],
    ACSC: ['ACSC'],
    'S180|section.?180': ['S180'],
    'Privacy|Privacy.?Act': ['Privacy'],
  },
  controls: {
    'MFA|multi.?factor': ['MFA'],
    patch: ['Patching'],
    backup: ['Backup'],
    firewall: ['Network Security'],
  },
  types: {
    'policy|policies': 'Policy',
    'screenshot|screen': 'Screenshot',
    report: 'Report',
    'certificate|cert': 'Certificate',
  },
}

function autoTagEvidence(filename: string, content?: string) {
  const tags = {
    frameworks: [],
    controls: [],
    evidenceType: 'Document',
  }

  // Check filename and content against patterns
  for (const [pattern, tag] of Object.entries(TAG_PATTERNS.frameworks)) {
    if (new RegExp(pattern, 'i').test(filename + ' ' + (content || ''))) {
      tags.frameworks.push(...tag)
    }
  }

  return tags
}

2. Evidence Dashboard

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Evidence Library                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Total Files: 127                        β”‚
β”‚ This Month: 12 ↑                        β”‚
β”‚ Expiring Soon: 3 ⚠️                      β”‚
β”‚                                         β”‚
β”‚ By Framework:                           β”‚
β”‚ [E8: 45] [ACSC: 23] [S180: 15] [Privacy: 10] β”‚
β”‚                                         β”‚
β”‚ Recent Uploads:                         β”‚
β”‚ β€’ MFA-Config-Screenshot.png (2 min ago) β”‚
β”‚ β€’ Patch-Schedule-2024.xlsx (1 hour ago) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3. Quick Evidence for Auditors

// Generate evidence package for specific request
export async function createAuditPackage(request: AuditRequest) {
  const evidence = await db.evidence.findMany({
    where: {
      framework_tags: { contains: request.framework },
      control_tags: { overlap: request.controls },
      valid_until: { gte: new Date() },
    },
    orderBy: { uploaded_at: 'desc' },
  })

  // Create ZIP with organized folders
  const zip = new JSZip()
  for (const item of evidence) {
    const folder = `${item.framework_tags[0]}/${item.control_tags[0]}/`
    zip.file(folder + item.file_name, await getFile(item.file_path))
  }

  return zip.generateAsync({ type: 'blob' })
}

Integration Points

WhatsApp Upload

// Handle evidence via WhatsApp
Bot: "Please send the MFA policy document"
User: [Sends PDF]
Bot: "Received 'MFA-Policy-Jan.pdf' (2.3MB)
      Tagged: E8, MFA, Policy
      Stored in Evidence Library βœ“"

Task Linking

// When completing a task
interface TaskCompletion {
  taskId: string
  evidence: string[] // Evidence IDs
  notes: string
}

// Auto-prompt for evidence
if (task.requires_evidence && !completion.evidence.length) {
  prompt('This task requires evidence. Upload or select existing.')
}

Report Generation

// Include evidence links in reports
function generateBoardReport(period: string) {
  const sections = {
    compliance: getComplianceScores(period),
    evidence: {
      total: getEvidenceCount(period),
      byFramework: getEvidenceByFramework(period),
      expiring: getExpiringEvidence(30),
      recentUploads: getRecentEvidence(10),
    },
  }

  // Include direct links to evidence
  return renderReport(sections)
}

What We're NOT Building (MVP)

  • Document versioning
  • OCR or content extraction
  • Advanced metadata schemas
  • Workflow approvals
  • Document editing
  • Complex folder hierarchies
  • Integration with DMS systems
  • Automated evidence gathering

Success Metrics

Usage Metrics

  • Files uploaded per customer: 50-200
  • Average upload time: < 30 seconds
  • Retrieval time: < 2 seconds
  • Evidence used in reports: > 80%

Quality Metrics

  • Auto-tag accuracy: > 70%
  • Evidence found on first search: > 90%
  • Expired evidence caught: 100%

Implementation Timeline

  1. Week 1: File upload + basic storage
  2. Week 2: Tagging system + search
  3. Week 3: Evidence dashboard
  4. Week 4: Audit package generation
  5. Week 5: Integrations (WhatsApp, tasks)

Future Enhancements (Post-MVP)

  • AI-powered content extraction
  • Evidence quality scoring
  • Automated evidence requests
  • Version control
  • Digital signatures
  • Chain of custody tracking

Related Documents: