{"skill":{"slug":"skill-evidenceops","displayName":"EvidenceOps - Forensic Evidence Management","summary":"Forensic media triage with chain of custody. Use when receiving images, videos, audio, PDFs, or documents that need evidence-grade handling, integrity verifi...","description":"---\nname: evidenceops\ndescription: Forensic media triage with chain of custody. Use when receiving images, videos, audio, PDFs, or documents that need evidence-grade handling, integrity verification, and audit trails.\nversion: 1.0.0\nauthor: OpenClaw Community\nmetadata:\n  openclaw:\n    emoji: \"🔬\"\n    tools:\n      - evidence.ingest\n      - evidence.verify\n      - evidence.manifest\n      - evidence.export\n      - evidence.access_log\n      - Read\n      - Write\n      - Bash\n    install:\n      npm: \"@openclaw/evidence-vault\"\n    os:\n      - darwin\n      - linux\n      - win32\n    categories:\n      - forensics\n      - security\n      - compliance\n    tags:\n      - evidence\n      - chain-of-custody\n      - forensic\n      - integrity\n      - audit\n---\n\n# EvidenceOps - Forensic Media Triage\n\n## What It Does\n\nEvidenceOps provides forensic-grade handling of media files with complete chain of custody:\n\n1. **Media Intake** - Accept images, videos, audio, PDFs, and documents from any channel\n2. **Immutable Storage** - Store originals in append-only vault with cryptographic hashes\n3. **Metadata Extraction** - Extract EXIF, file properties, and media information without altering originals\n4. **Derivative Generation** - Create thumbnails, transcripts, previews in separate folders\n5. **Chain of Custody** - Maintain tamper-evident audit trail with hash chain\n6. **Integrity Verification** - Verify evidence hasn't been modified post-ingest\n7. **Audit Trail** - Complete JSONL audit log for compliance\n\n## What It NEVER Does\n\n- **NEVER** modifies original evidence files after ingest\n- **NEVER** stores secrets, API keys, or credentials in manifests or logs\n- **NEVER** accepts unsanitized paths from user input\n- **NEVER** executes untrusted code or downloads remote scripts\n- **NEVER** exfiltrates data to external services without explicit configuration\n- **NEVER** bypasses channel allowlists or pairing requirements\n- **NEVER** stores real personal data in example files\n\n## Prerequisites\n\nBefore using this skill, ensure:\n\n1. Plugin `@openclaw/evidence-vault` is installed and initialized\n2. Vault storage directory is configured with appropriate permissions\n3. Channel allowlist is configured for trusted sources only\n4. Retention policies comply with your legal requirements\n\n## Workflow\n\n### Step 1: Receive Media\n\nWhen media is received via any channel:\n\n```\nUser sends: [image/video/document]\n```\n\n**Required Information:**\n- File content (from attachment)\n- Original filename\n- Source channel (whatsapp, telegram, email, etc.)\n- Sender identifier\n- Message ID (if available)\n\n### Step 2: Create or Select Case\n\n```\nIF user specifies existing caseId:\n  USE that caseId\nELSE IF user requests new case:\n  CREATE case with format: case-{YYYY}-{NNN}\n  EXAMPLE: case-2026-001\nELSE:\n  ASK user: \"Should I create a new case or add to existing case [case-2026-XXX]?\"\n```\n\n**Case ID Format:** `case-{year}-{sequence}`\n- Must match pattern: `^case-[a-zA-Z0-9_-]+$`\n- Examples: `case-2026-001`, `case-incident-alpha`, `case-legal-2026-q1`\n\n### Step 3: Stage Original (Read-Only)\n\nBefore ingest:\n\n1. **Save received file to temporary staging area**\n2. **Calculate SHA-256 hash immediately**\n3. **Record file size and MIME type**\n4. **DO NOT modify the file**\n\n```\n# Staging directory structure\n/tmp/evidence-staging/\n├── {caseId}/\n│   └── {timestamp}-{filename}\n```\n\n### Step 4: Extract Metadata\n\nExtract metadata WITHOUT modifying original:\n\n**For Images:**\n- EXIF data (camera, GPS, timestamps)\n- Dimensions\n- Color profile\n\n**For Videos:**\n- Duration\n- Codec information\n- Resolution\n\n**For Audio:**\n- Duration\n- Sample rate\n- Codec\n\n**For PDFs:**\n- Page count\n- Author (if embedded)\n- Creation date\n\n```\n# Use Read tool or appropriate extraction commands\n# NEVER write back to original file\n```\n\n### Step 5: Generate Derivatives (Optional)\n\nCreate derivative artifacts in SEPARATE folder:\n\n```\nderivatives/\n├── thumbnails/\n│   └── {evidenceId}-thumb.jpg\n├── transcripts/\n│   └── {evidenceId}-transcript.txt\n└── previews/\n    └── {evidenceId}-preview.pdf\n```\n\n**Derivative Types:**\n- `thumbnail` - Reduced resolution image/video preview\n- `transcript` - Speech-to-text for audio/video\n- `preview` - PDF or text representation\n- `ocr` - Extracted text from images\n\n### Step 6: Ingest to Vault\n\nCall the evidence.ingest tool:\n\n```json\n{\n  \"filePath\": \"/path/to/staged/file\",\n  \"filename\": \"original-filename.jpg\",\n  \"caseId\": \"case-2026-001\",\n  \"channel\": \"whatsapp\",\n  \"sender\": \"user@example.com\",\n  \"messageId\": \"msg-abc123\",\n  \"retentionDays\": 2555,\n  \"metadata\": {\n    \"exif\": { ... },\n    \"extracted\": { ... }\n  }\n}\n```\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"evidenceId\": \"ev-abc123...\",\n  \"sha256\": \"a1b2c3...\",\n  \"vaultUrl\": \"file:///vault/cases/case-2026-001/originals/ev-abc123.jpg\",\n  \"timestamp\": \"2026-02-17T10:30:00.000Z\"\n}\n```\n\n### Step 7: Update Manifest\n\nThe manifest is automatically updated by the ingest operation.\n\n**Manifest Location:** `{vault}/cases/{caseId}/manifest.json`\n\n**Manifest Contents:**\n- Case metadata\n- Evidence items with hashes\n- Derivatives\n- Chain of custody entries\n- Retention policy\n\n### Step 8: Return Receipt\n\nProvide user with evidence receipt:\n\n```\n📋 EVIDENCE RECEIPT\n\nCase ID: case-2026-001\nEvidence ID: ev-abc123...\nFile: original-filename.jpg\nSHA-256: a1b2c3d4e5f6...\nSize: 1.2 MB\nReceived: 2026-02-17 10:30:00 UTC\nVault: file:///vault/cases/case-2026-001/originals/ev-abc123.jpg\n\n✅ Chain of custody established\n✅ Original preserved immutably\n✅ Audit trail active\n```\n\n## Tool Reference\n\n### evidence.ingest\n\nIngest a file into the evidence vault.\n\n**Parameters:**\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| filePath | string | Yes | Path to the staged file |\n| filename | string | Yes | Original filename |\n| caseId | string | Yes | Case identifier |\n| channel | string | No | Source channel |\n| sender | string | No | Sender identifier |\n| messageId | string | No | Message ID from source |\n| retentionDays | number | No | Retention period |\n| metadata | object | No | Additional metadata |\n\n**Returns:** `{ evidenceId, sha256, vaultUrl, timestamp }`\n\n### evidence.verify\n\nVerify evidence integrity.\n\n**Parameters:**\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| evidenceId | string | Yes | Evidence to verify |\n| caseId | string | No | Limit search to case |\n\n**Returns:** `{ verified, details: { originalIntact, hashMatch, lastVerifiedAt } }`\n\n### evidence.manifest\n\nGet case manifest.\n\n**Parameters:**\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| caseId | string | Yes | Case identifier |\n\n**Returns:** Complete case manifest with all items\n\n### evidence.export\n\nExport case as archive.\n\n**Parameters:**\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| caseId | string | Yes | Case identifier |\n| format | string | No | 'zip' or 'tar' (default: zip) |\n\n**Returns:** `{ exportPath, sha256, size, itemCount }`\n\n### evidence.access_log\n\nGet audit trail.\n\n**Parameters:**\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| caseId | string | Yes | Case identifier |\n| limit | number | No | Max events (default: 100) |\n\n**Returns:** `{ events: [ ... ], count }`\n\n## SECURITY POSTURE\n\n### Channel Allowlist Configuration\n\nConfigure which channels can submit evidence:\n\n```yaml\n# ~/.openclaw/openclaw.json\n{\n  \"plugins\": {\n    \"evidence-vault\": {\n      \"channelAllowlist\": [\"whatsapp\", \"telegram\", \"email\"],\n      \"channelDenylist\": [\"public-discord\"],\n      \"requirePairing\": true\n    }\n  }\n}\n```\n\n### Pairing Requirements\n\nFor Direct Messages (DMs):\n- **REQUIRE** pairing before accepting evidence\n- **BLOCK** unpaired DMs by default\n- **LOG** rejected ingestion attempts\n\nFor Group Channels:\n- **VERIFY** channel is in allowlist\n- **REJECT** evidence from untrusted channels\n- **NEVER** auto-ingest from public channels\n\n### Path Sanitization Rules\n\nALL paths are validated:\n\n1. **No path traversal** - `../` sequences rejected\n2. **No null bytes** - `\\0` rejected\n3. **No home directory** - `~/` rejected\n4. **Canonicalization** - Paths resolved before validation\n5. **Base path check** - Must be within vault directory\n\n**Violations result in:** `E_PATH_TRAVERSAL` error + audit log entry\n\n### Destructive Action Confirmation\n\nBefore any potentially destructive operation:\n\n1. **EXPORT** - Confirm export destination\n2. **RETENTION DELETE** - Require explicit confirmation (if enabled)\n3. **LEGAL HOLD RELEASE** - Require explicit confirmation\n\n**Confirmation format:**\n```\n⚠️ DESTRUCTIVE ACTION\n\nYou are about to: [describe action]\nCase: [caseId]\nItems affected: [count]\n\nType \"CONFIRM\" to proceed:\n```\n\n### Secrets Handling\n\n**NEVER include in logs or manifests:**\n- API keys\n- Passwords\n- Tokens\n- Credit card numbers\n- Email addresses (redacted)\n- Phone numbers (redacted)\n- SSN (redacted)\n\n**Redaction is automatic** via regex patterns.\n\n## Troubleshooting\n\n### Error: E_PATH_TRAVERSAL\n\n**Cause:** Filename or path contains disallowed characters\n\n**Solution:**\n- Filenames are automatically sanitized\n- If error persists, check for unusual characters\n- Original filename is preserved in manifest metadata\n\n### Error: E_INVALID_MIME\n\n**Cause:** File type not in allowed list\n\n**Solution:**\n- Check `allowedMimeTypes` configuration\n- Add MIME type to allowlist if appropriate\n- Verify file is not corrupted\n\n### Error: E_SIZE_LIMIT\n\n**Cause:** File exceeds maximum size\n\n**Solution:**\n- Check `maxFileSizeBytes` configuration\n- Split large files if appropriate\n- Compress before ingest (note in metadata)\n\n### Error: E_CHANNEL_BLOCKED\n\n**Cause:** Evidence from blocked or non-allowlisted channel\n\n**Solution:**\n- Verify channel in allowlist\n- Check if pairing is required\n- Review security configuration\n\n### Error: E_HASH_MISMATCH\n\n**Cause:** Evidence file modified after ingest\n\n**Solution:**\n- This indicates potential tampering\n- Review access logs for case\n- Escalate per incident response procedures\n\n### Verification Fails\n\n**Symptoms:** `verified: false` in verify response\n\n**Diagnostic steps:**\n1. Check if original file exists\n2. Compare current hash with manifest hash\n3. Review audit log for modifications\n4. Check filesystem permissions\n\n## Configuration Example\n\n```yaml\n# openclaw.yaml\nplugins:\n  evidence-vault:\n    driver: filesystem  # or s3\n    basePath: /var/evidence-vault\n    maxFileSizeBytes: 524288000  # 500MB\n    defaultRetentionDays: 2555   # 7 years\n    allowedMimeTypes:\n      - image/jpeg\n      - image/png\n      - video/mp4\n      - audio/mpeg\n      - application/pdf\n    channelAllowlist:\n      - whatsapp\n      - telegram\n      - slack\n    requirePairing: true\n    \n    # S3 driver options (if driver: s3)\n    s3:\n      endpoint: https://s3.example.com\n      bucket: evidence-vault\n      region: us-east-1\n      objectLock: true\n```\n\n## Legal and Compliance Notes\n\n### LGPD/GDPR Considerations\n\n- Tag sensitive data with `sensitivityTag`\n- Configure appropriate retention periods\n- Never store real personal data in examples\n- Implement data subject access request procedures\n\n### Retention Policies\n\n| Sensitivity | Default Retention |\n|-------------|-------------------|\n| public | 365 days |\n| internal | 1825 days (5 years) |\n| confidential | 2555 days (7 years) |\n| restricted | Legal hold |\n\n### Audit Requirements\n\nAll operations logged to JSONL audit file:\n- Case creation/deletion\n- Evidence ingest\n- Verification attempts\n- Export operations\n- Access requests\n\n**Audit log retention:** Same as case retention\n","tags":{"latest":"1.0.0"},"stats":{"comments":0,"downloads":968,"installsAllTime":0,"installsCurrent":0,"stars":1,"versions":1},"createdAt":1771349502788,"updatedAt":1778491567625},"latestVersion":{"version":"1.0.0","createdAt":1771349502788,"changelog":"This is the initial release of EvidenceOps, a forensic-grade evidence management system for OpenClaw. It provides:\n\n1. **Complete Skill for ClawHub** - Ready-to-publish skill with comprehensive documentation\n2. **Dual Storage Drivers** - Filesystem (local) and S3/MinIO (cloud) backends\n3. **Chain of Custody** - Cryptographic hash chain for evidence integrity\n4. **Security-First Design** - Path sanitization, secret redaction, channel controls\n5. **Full Test Coverage** - Unit and integration tests for core functionality\n\nThe skill teaches OpenClaw agents how to:\n- Accept media from any channel\n- Create/manage cases with proper IDs\n- Stage originals without modification\n- Extract metadata (EXIF, duration, pages)\n- Generate derivatives in separate folders\n- Maintain tamper-evident audit trails\n- Return evidence receipts to users\n\nThe plugin provides the backend implementation with:\n- Pluggable storage architecture\n- Deterministic manifest generation\n- Channel-based access control\n- Comprehensive error handling","license":null},"metadata":{"setup":[],"os":["darwin","linux","win32"],"systems":null},"owner":{"handle":"msrovani","userId":"s173y9g719yvqbbdgmxhjaaf218844by","displayName":"msrovani","image":"https://avatars.githubusercontent.com/u/40367525?v=4"},"moderation":{"isSuspicious":false,"isMalwareBlocked":false,"verdict":"clean","reasonCodes":["review.llm_review"],"summary":"Review: review.llm_review","engineVersion":"v2.4.24","updatedAt":1779972863285}}