{"id":82,"date":"2025-09-01T13:55:45","date_gmt":"2025-09-01T13:55:45","guid":{"rendered":"https:\/\/scootercam.net\/notes\/?p=82"},"modified":"2025-09-01T15:19:09","modified_gmt":"2025-09-01T15:19:09","slug":"server-side-timelapse-processing","status":"publish","type":"post","link":"https:\/\/scootercam.net\/blog\/server-side-timelapse-processing\/","title":{"rendered":"Server-side timelapse processing"},"content":{"rendered":"\n<p>Every five minutes, the Raspberry Pi system at the lake sends a short timelapse segment to Scootercam&#8217;s webserver. Here&#8217;s what happens next.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Security-Hardened Timelapse System<\/h1>\n\n\n\n<p>A PHP-based timelapse video management system that receives video segments from cameras and combines them into daily period videos with automatic 8-hour rotation and comprehensive security controls.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n\n\n\n<p>This system manages timelapse videos from multiple cameras with enterprise-grade security by:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Receiving authenticated video segments via HTTP uploads<\/li>\n\n\n\n<li>Appending segments to period-based daily videos<\/li>\n\n\n\n<li>Auto-rotating videos every 8 hours via cron<\/li>\n\n\n\n<li>Archiving completed videos for long-term storage<\/li>\n\n\n\n<li>Comprehensive security logging and monitoring<\/li>\n\n\n\n<li>Rate limiting and access controls<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">System Architecture<\/h2>\n\n\n\n<p><strong>Cameras<\/strong>: 2 (Amcrest, Reolink)<br><strong>Periods<\/strong>: 3 per day (Night: 00:00-08:00, Day: 08:00-16:00, Evening: 16:00-24:00)<br><strong>Total Videos<\/strong>: Maximum 6 active videos at any time<br><strong>Rotation<\/strong>: Every 8 hours via cron (00:00, 08:00, 16:00)<br><strong>Security<\/strong>: Two-tier API key authentication, rate limiting, comprehensive validation<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Directory Structure<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/home\/scootercam\/public_html\/timelapse\/\n\u251c\u2500\u2500 upload.php              # Main secure script\n\u251c\u2500\u2500 videos\/                 # Active videos (max 6 files)\n\u2502   \u251c\u2500\u2500 amcrest_day_20250901.mp4\n\u2502   \u251c\u2500\u2500 amcrest_evening_20250901.mp4\n\u2502   \u251c\u2500\u2500 amcrest_night_20250902.mp4\n\u2502   \u251c\u2500\u2500 reolink_day_20250901.mp4\n\u2502   \u251c\u2500\u2500 reolink_evening_20250901.mp4\n\u2502   \u2514\u2500\u2500 reolink_night_20250902.mp4\n\u251c\u2500\u2500 videos\/archive\/         # Archived videos\n\u2502   \u251c\u2500\u2500 amcrest_day_20250831_235959.mp4\n\u2502   \u2514\u2500\u2500 reolink_evening_20250831_235959.mp4\n\u251c\u2500\u2500 temp\/                   # Temporary processing files\n\u251c\u2500\u2500 security.log            # Security audit trail\n\u2514\u2500\u2500 rate_limit.json         # Rate limiting data\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Security Features<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd10 <strong>Two-Tier API Authentication<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Upload Key<\/strong>: Cameras can upload segments and basic operations<\/li>\n\n\n\n<li><strong>Admin Key<\/strong>: Full system control including rotation and status<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee1\ufe0f <strong>Comprehensive File Validation<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>File size limits (50MB maximum)<\/li>\n\n\n\n<li>MIME type verification (video\/mp4, video\/quicktime)<\/li>\n\n\n\n<li>File header validation (MP4 format check)<\/li>\n\n\n\n<li>Upload method verification<\/li>\n\n\n\n<li>Path traversal protection<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u26a1 <strong>Rate Limiting<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Maximum 10 uploads per minute per IP address<\/li>\n\n\n\n<li>Automatic blocking of excessive requests<\/li>\n\n\n\n<li>Rate limit data logging and monitoring<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcca <strong>Security Monitoring<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>All access attempts logged with IP tracking<\/li>\n\n\n\n<li>Suspicious activity alerts<\/li>\n\n\n\n<li>Failed authentication tracking<\/li>\n\n\n\n<li>File operation audit trail<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd12 <strong>Input Sanitization<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Strict camera name validation (whitelist only)<\/li>\n\n\n\n<li>Command injection prevention<\/li>\n\n\n\n<li>Path traversal protection<\/li>\n\n\n\n<li>Error information hiding<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Setup<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Generate API Keys<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Generate secure upload key (64 characters)\nopenssl rand -hex 32\n\n# Generate secure admin key (64 characters) \nopenssl rand -hex 32\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Configure Script<\/h3>\n\n\n\n<p>Edit <code>upload.php<\/code> and replace the placeholder API keys:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private $api_keys = &#91;\n    'upload' =&gt; 'your_64_char_upload_key_here',\n    'admin' =&gt; 'your_64_char_admin_key_here'\n];\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Set File Permissions<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>chmod 755 \/home\/scootercam\/public_html\/timelapse\/upload.php\nchmod 750 \/home\/scootercam\/public_html\/timelapse\/videos\nchmod 750 \/home\/scootercam\/public_html\/timelapse\/temp\nchmod 640 \/home\/scootercam\/public_html\/timelapse\/security.log\nchown -R scootercam:scootercam \/home\/scootercam\/public_html\/timelapse\/\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4. Update Cron Job<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Replace with your actual admin API key\n0 0,8,16 * * * \/usr\/bin\/curl --silent \"http:\/\/scootercam.net\/timelapse\/upload.php?action=rotate&amp;api_key=YOUR_ADMIN_KEY_HERE\" &gt; \/dev\/null 2&gt;&amp;1\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">5. Configure Web Server Security<\/h3>\n\n\n\n<p>Add to <code>.htaccess<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Deny access to sensitive files\n&lt;Files \"security.log\"&gt;\n    Deny from all\n&lt;\/Files&gt;\n\n&lt;Files \"rate_limit.json\"&gt;\n    Deny from all\n&lt;\/Files&gt;\n\n# Optional: Basic DDoS protection\n&lt;IfModule mod_evasive24.c&gt;\n    DOSPageCount        5\n    DOSPageInterval     1\n    DOSBlockingPeriod   300\n&lt;\/IfModule&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6. Verify FFmpeg<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>which ffmpeg\n# Should return: \/bin\/ffmpeg\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">API Endpoints<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Upload Video Segment<\/h3>\n\n\n\n<p><strong>POST<\/strong> <code>upload.php?action=receive<\/code> (default)<\/p>\n\n\n\n<p><strong>Authentication<\/strong>: Upload or Admin API key required<\/p>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>camera<\/code> (POST): <code>amcrest<\/code> or <code>reolink<\/code><\/li>\n\n\n\n<li><code>api_key<\/code> (POST): Valid upload or admin API key<\/li>\n\n\n\n<li><code>video<\/code> (FILE): MP4 video segment (max 50MB)<\/li>\n<\/ul>\n\n\n\n<p><strong>Response:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"status\": \"success\",\n  \"message\": \"Segment appended\",\n  \"period\": \"day\",\n  \"camera\": \"amcrest\"\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -X POST \\\n  -F \"camera=amcrest\" \\\n  -F \"api_key=your_upload_key_here\" \\\n  -F \"video=@segment.mp4\" \\\n  http:&#47;&#47;scootercam.net\/timelapse\/upload.php\n<\/code><\/pre>\n\n\n\n<p><strong>Security Validations:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>API key authentication<\/li>\n\n\n\n<li>Rate limiting (10 requests\/minute\/IP)<\/li>\n\n\n\n<li>File type validation (MP4\/MOV only)<\/li>\n\n\n\n<li>File size validation (\u226450MB)<\/li>\n\n\n\n<li>MIME type verification<\/li>\n\n\n\n<li>File header validation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Manual Rotation<\/h3>\n\n\n\n<p><strong>GET<\/strong> <code>upload.php?action=rotate&amp;api_key=ADMIN_KEY<\/code><\/p>\n\n\n\n<p><strong>Authentication<\/strong>: Admin API key required<\/p>\n\n\n\n<p>Moves all active videos to archive directory.<\/p>\n\n\n\n<p><strong>Response:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"status\": \"success\",\n  \"message\": \"Rotated 6 videos\"\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"http:\/\/scootercam.net\/timelapse\/upload.php?action=rotate&amp;api_key=your_admin_key_here\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">System Status<\/h3>\n\n\n\n<p><strong>GET<\/strong> <code>upload.php?action=status&amp;api_key=ADMIN_KEY<\/code><\/p>\n\n\n\n<p><strong>Authentication<\/strong>: Admin API key required<\/p>\n\n\n\n<p><strong>Response:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"status\": \"success\",\n  \"current_period\": \"day\",\n  \"videos\": &#91;\n    {\n      \"camera\": \"amcrest\",\n      \"period\": \"day\",\n      \"exists\": true,\n      \"size\": 15728640,\n      \"modified\": \"2025-09-01 14:30:15\"\n    }\n  ]\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Archive Cleanup<\/h3>\n\n\n\n<p><strong>GET<\/strong> <code>upload.php?action=cleanup&amp;days=7&amp;api_key=ADMIN_KEY<\/code><\/p>\n\n\n\n<p><strong>Authentication<\/strong>: Admin API key required<\/p>\n\n\n\n<p>Deletes archive files older than specified days (1-30 day range enforced).<\/p>\n\n\n\n<p><strong>Response:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"status\": \"success\",\n  \"message\": \"Deleted 12 old archive files\"\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"http:\/\/scootercam.net\/timelapse\/upload.php?action=cleanup&amp;days=5&amp;api_key=your_admin_key_here\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How It Works<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Secure Upload Process<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Rate Limiting<\/strong>: Check IP hasn&#8217;t exceeded 10 uploads\/minute<\/li>\n\n\n\n<li><strong>Authentication<\/strong>: Validate API key (upload or admin level)<\/li>\n\n\n\n<li><strong>File Validation<\/strong>: Comprehensive security checks on uploaded file<\/li>\n\n\n\n<li><strong>Camera Validation<\/strong>: Ensure camera name is in allowed whitelist<\/li>\n\n\n\n<li><strong>Period Detection<\/strong>: Determine current time period (night\/day\/evening)<\/li>\n\n\n\n<li><strong>Secure Processing<\/strong>: Append to appropriate daily video using validated paths<\/li>\n\n\n\n<li><strong>Cleanup<\/strong>: Remove temporary files and log successful operation<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">2. Video Concatenation<\/h3>\n\n\n\n<p>Uses FFmpeg&#8217;s concat demuxer with security controls:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ffmpeg -f concat -safe 0 -i concat_list.txt -c copy -avoid_negative_ts make_zero output.mp4\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Automatic Rotation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cron triggers rotation every 8 hours with admin API key<\/li>\n\n\n\n<li>All videos in <code>\/videos\/<\/code> moved to <code>\/videos\/archive\/<\/code> with timestamp<\/li>\n\n\n\n<li>Security logging of rotation operations<\/li>\n\n\n\n<li>Fresh videos created as new segments arrive<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4. Security Monitoring<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>All operations logged to <code>security.log<\/code> with timestamps and IP addresses<\/li>\n\n\n\n<li>Rate limiting data tracked in <code>rate_limit.json<\/code><\/li>\n\n\n\n<li>Failed authentication attempts logged as warnings<\/li>\n\n\n\n<li>Suspicious activity (invalid files, path traversal attempts) logged as errors<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5. File Naming Convention<\/h3>\n\n\n\n<p><strong>Active Videos:<\/strong> <code>{camera}_{period}_{date}.mp4<\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>amcrest_day_20250901.mp4<\/code><\/li>\n<\/ul>\n\n\n\n<p><strong>Archived Videos:<\/strong> <code>{camera}_{period}_{date}_{time}.mp4<\/code><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>amcrest_day_20250901_160002.mp4<\/code><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Camera Configuration<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Update Camera Upload Scripts<\/h3>\n\n\n\n<p>Cameras must now include API keys in their upload requests:<\/p>\n\n\n\n<p><strong>Amcrest Camera:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\nAPI_KEY=\"your_upload_key_here\"\nCAMERA=\"amcrest\"\nSEGMENT=\"\/path\/to\/segment.mp4\"\n\ncurl -X POST \\\n  -F \"camera=${CAMERA}\" \\\n  -F \"api_key=${API_KEY}\" \\\n  -F \"video=@${SEGMENT}\" \\\n  http:&#47;&#47;scootercam.net\/timelapse\/upload.php\n<\/code><\/pre>\n\n\n\n<p><strong>Reolink Camera:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\nAPI_KEY=\"your_upload_key_here\" \nCAMERA=\"reolink\"\nSEGMENT=\"\/path\/to\/segment.mp4\"\n\ncurl -X POST \\\n  -F \"camera=${CAMERA}\" \\\n  -F \"api_key=${API_KEY}\" \\\n  -F \"video=@${SEGMENT}\" \\\n  http:&#47;&#47;scootercam.net\/timelapse\/upload.php\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Monitoring<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Security Log Monitoring<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Real-time security monitoring\ntail -f \/home\/scootercam\/public_html\/timelapse\/security.log\n\n# Check for suspicious activity\ngrep \"WARNING\\|ERROR\" \/home\/scootercam\/public_html\/timelapse\/security.log\n\n# Monitor rate limiting\ngrep \"Rate limit\" \/home\/scootercam\/public_html\/timelapse\/security.log\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">System Status Monitoring<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Check current system status (requires admin key)\ncurl \"http:\/\/scootercam.net\/timelapse\/upload.php?action=status&amp;api_key=your_admin_key_here\"\n\n# Verify active video count (should be \u22646)\nls -la \/home\/scootercam\/public_html\/timelapse\/videos\/*.mp4 | wc -l\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Disk Usage Monitoring<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Check active videos disk usage\ndu -sh \/home\/scootercam\/public_html\/timelapse\/videos\/\n\n# Check archive disk usage\ndu -sh \/home\/scootercam\/public_html\/timelapse\/videos\/archive\/\n\n# Overall system disk usage\ndf -h\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Monitoring<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># Check recent uploads (last 24 hours)\nfind \/home\/scootercam\/public_html\/timelapse\/videos\/ -name \"*.mp4\" -mtime -1 -ls\n\n# Monitor temp directory (should be empty most of the time)\nls -la \/home\/scootercam\/public_html\/timelapse\/temp\/\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Problem: Upload Rejected (401 Unauthorized)<\/h3>\n\n\n\n<p><strong>Cause:<\/strong> Invalid or missing API key<br><strong>Solution:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Verify API key in camera configuration<\/li>\n\n\n\n<li>Check <code>security.log<\/code> for failed authentication attempts<\/li>\n\n\n\n<li>Ensure API key matches configuration in <code>upload.php<\/code><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Problem: Upload Rejected (429 Rate Limited)<\/h3>\n\n\n\n<p><strong>Cause:<\/strong> Too many requests from same IP<br><strong>Solution:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Check rate limiting in <code>security.log<\/code><\/li>\n\n\n\n<li>Wait for rate limit reset (1 minute)<\/li>\n\n\n\n<li>Investigate if camera is sending duplicate requests<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Problem: Upload Rejected (400 Bad Request)<\/h3>\n\n\n\n<p><strong>Cause:<\/strong> File validation failure<br><strong>Solution:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Check file size (must be \u226450MB)<\/li>\n\n\n\n<li>Verify file format (MP4\/MOV only)<\/li>\n\n\n\n<li>Ensure camera name is valid (<code>amcrest<\/code> or <code>reolink<\/code>)<\/li>\n\n\n\n<li>Check <code>security.log<\/code> for specific validation errors<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Problem: FFmpeg Concatenation Failures<\/h3>\n\n\n\n<p><strong>Check:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>FFmpeg availability: <code>which ffmpeg<\/code><\/li>\n\n\n\n<li>Temporary directory permissions: <code>ls -la temp\/<\/code><\/li>\n\n\n\n<li>Video codec compatibility<\/li>\n\n\n\n<li>Security log for FFmpeg errors<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Problem: Too Many Active Videos (&gt;6)<\/h3>\n\n\n\n<p><strong>Solution:<\/strong> Force rotation<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"http:\/\/scootercam.net\/timelapse\/upload.php?action=rotate&amp;api_key=your_admin_key_here\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Problem: Large Archive Directory<\/h3>\n\n\n\n<p><strong>Solution:<\/strong> Clean old archives<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"http:\/\/scootercam.net\/timelapse\/upload.php?action=cleanup&amp;days=5&amp;api_key=your_admin_key_here\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Security Incident Response<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Suspicious Activity Detection<\/h3>\n\n\n\n<p>Monitor <code>security.log<\/code> for these indicators:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Multiple failed authentication attempts from same IP<\/li>\n\n\n\n<li>Invalid file upload attempts<\/li>\n\n\n\n<li>Path traversal attempts<\/li>\n\n\n\n<li>Unusual upload patterns or timing<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Immediate Response Actions<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Block suspicious IPs at firewall level<\/strong><\/li>\n\n\n\n<li><strong>Rotate API keys if compromised<\/strong><\/li>\n\n\n\n<li><strong>Review and analyze security logs<\/strong><\/li>\n\n\n\n<li><strong>Verify system integrity<\/strong><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">API Key Compromise Response<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. Generate new API keys\nopenssl rand -hex 32  # New upload key\nopenssl rand -hex 32  # New admin key\n\n# 2. Update upload.php with new keys\n# 3. Update camera configurations\n# 4. Update cron job with new admin key\n# 5. Clear rate limiting data\nrm -f \/home\/scootercam\/public_html\/timelapse\/rate_limit.json\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Maintenance<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Daily Tasks<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Monitor security log for suspicious activity<\/li>\n\n\n\n<li>Verify 6 or fewer active videos exist<\/li>\n\n\n\n<li>Check cron rotation is working (00:00, 08:00, 16:00)<\/li>\n\n\n\n<li>Monitor disk usage<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Weekly Tasks<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Review security log trends<\/li>\n\n\n\n<li>Clean old archives (automated with API call)<\/li>\n\n\n\n<li>Verify video quality and completeness<\/li>\n\n\n\n<li>Update API keys if security policy requires<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Monthly Tasks<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Security audit and log analysis<\/li>\n\n\n\n<li>Performance optimization review<\/li>\n\n\n\n<li>Archive storage management<\/li>\n\n\n\n<li>System backup verification<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Emergency Procedures<\/h3>\n\n\n\n<p><strong>Complete System Reset:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># 1. Backup all data\nmkdir \/home\/scootercam\/timelapse_emergency_backup\ncp -r \/home\/scootercam\/public_html\/timelapse\/* \/home\/scootercam\/timelapse_emergency_backup\/\n\n# 2. Stop all uploads temporarily (block at firewall)\n# 3. Force rotation with admin key\ncurl \"http:\/\/scootercam.net\/timelapse\/upload.php?action=rotate&amp;api_key=your_admin_key_here\"\n\n# 4. Clear rate limiting\nrm -f \/home\/scootercam\/public_html\/timelapse\/rate_limit.json\n\n# 5. Restart system monitoring\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Expected Performance<\/h2>\n\n\n\n<p><strong>File Sizes (approximate):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>8-hour period video: 15-25 MB<\/li>\n\n\n\n<li>Daily total per camera: 45-75 MB<\/li>\n\n\n\n<li>Archive growth: ~100-150 MB per day<\/li>\n<\/ul>\n\n\n\n<p><strong>Processing Performance:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Segment append: &lt;2 seconds<\/li>\n\n\n\n<li>Authentication check: &lt;0.1 seconds<\/li>\n\n\n\n<li>File validation: &lt;0.5 seconds<\/li>\n\n\n\n<li>Rotation: &lt;10 seconds for all videos<\/li>\n\n\n\n<li>Rate limiting check: &lt;0.1 seconds<\/li>\n<\/ul>\n\n\n\n<p><strong>Security Overhead:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Minimal performance impact (&lt;5% processing overhead)<\/li>\n\n\n\n<li>Comprehensive logging with efficient file I\/O<\/li>\n\n\n\n<li>Rate limiting with memory-efficient tracking<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Compliance &amp; Auditing<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Security Audit Trail<\/h3>\n\n\n\n<p>All operations are logged with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Timestamp (Y-m-d H:i:s format)<\/li>\n\n\n\n<li>Client IP address (with proxy detection)<\/li>\n\n\n\n<li>Action performed<\/li>\n\n\n\n<li>Success\/failure status<\/li>\n\n\n\n<li>Error details (for failures)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Log Retention<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Security logs: Recommend 90-day retention minimum<\/li>\n\n\n\n<li>Rate limit data: Automatic cleanup (5 minutes retention)<\/li>\n\n\n\n<li>Archive videos: Configurable cleanup (default 7 days)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Access Control Summary<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Upload Operations<\/strong>: Upload or Admin API key required<\/li>\n\n\n\n<li><strong>System Status<\/strong>: Admin API key required<\/li>\n\n\n\n<li><strong>Manual Rotation<\/strong>: Admin API key required<\/li>\n\n\n\n<li><strong>Archive Cleanup<\/strong>: Admin API key required<\/li>\n\n\n\n<li><strong>Log Access<\/strong>: File system level permissions<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Version History<\/h2>\n\n\n\n<p><strong>v3.0<\/strong> &#8211; Security-Hardened System<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Added two-tier API key authentication<\/li>\n\n\n\n<li>Implemented comprehensive file validation<\/li>\n\n\n\n<li>Added rate limiting per IP address<\/li>\n\n\n\n<li>Security logging and audit trail<\/li>\n\n\n\n<li>Path traversal protection<\/li>\n\n\n\n<li>Command injection prevention<\/li>\n\n\n\n<li>Input sanitization and validation<\/li>\n\n\n\n<li>Error information hiding<\/li>\n\n\n\n<li>File size and type restrictions<\/li>\n<\/ul>\n\n\n\n<p><strong>v2.0<\/strong> &#8211; Simplified System<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Removed sunset\/golden hour logic<\/li>\n\n\n\n<li>Fixed multiple file creation issue<\/li>\n\n\n\n<li>Improved cron-based rotation<\/li>\n\n\n\n<li>Added status monitoring endpoint<\/li>\n<\/ul>\n\n\n\n<p><strong>v1.0<\/strong> &#8211; Original System<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Basic file upload and concatenation<\/li>\n\n\n\n<li>Manual rotation support<\/li>\n\n\n\n<li>Archive functionality<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Every five minutes, the Raspberry Pi system at the lake sends a short timelapse segment to Scootercam&#8217;s webserver. Here&#8217;s what happens next. Security-Hardened Timelapse System A PHP-based timelapse video management system that receives video segments from cameras and combines them into daily period videos with automatic 8-hour rotation and comprehensive security controls. Overview This system &#8230; <a title=\"Server-side timelapse processing\" class=\"read-more\" href=\"https:\/\/scootercam.net\/blog\/server-side-timelapse-processing\/\" aria-label=\"Read more about Server-side timelapse processing\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-82","post","type-post","status-publish","format-standard","hentry","category-timelapse"],"_links":{"self":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts\/82","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/comments?post=82"}],"version-history":[{"count":2,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts\/82\/revisions"}],"predecessor-version":[{"id":84,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts\/82\/revisions\/84"}],"wp:attachment":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/media?parent=82"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/categories?post=82"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/tags?post=82"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}