Skip to main content

Backup and Recovery Procedures

Product: Nquiry - Investigation Management Platform Last Updated: January 30, 2026 Version: 1.0 Owner: Joe Etherage (Founder)


1. Overview

This document describes backup and recovery procedures for Nquiry's data stores. It ensures business continuity and compliance with FedRAMP CP (Contingency Planning) controls.

Data Stores Covered

StoreTypeBackup MethodRPORTO
RDS PostgreSQLRelational databaseAutomated snapshots + PITR5 minutes< 1 hour
S3 EvidenceObject storageVersioning + replication0 (immediate)< 15 minutes
CognitoUser directoryAWS managedN/AN/A

RPO = Recovery Point Objective (max data loss) RTO = Recovery Time Objective (max downtime)


2. RDS PostgreSQL Backup

2.1 Automated Backups (Enabled)

Current Configuration:

SettingDevProduction (Planned)
Backup retention7 days35 days
Backup window03:00-04:00 UTC03:00-04:00 UTC
Multi-AZNoYes
Storage encryptedYes (KMS)Yes (KMS)

What's Backed Up:

  • Full database snapshot (daily)
  • Transaction logs (every 5 minutes) for Point-in-Time Recovery

2.2 Manual Snapshots

Create manual snapshots before major changes:

# Create manual snapshot
aws rds create-db-snapshot \
--db-instance-identifier invapp-dev-postgres \
--db-snapshot-identifier manual-$(date +%Y%m%d-%H%M%S) \
--region us-east-1

# List available snapshots
aws rds describe-db-snapshots \
--db-instance-identifier invapp-dev-postgres \
--query 'DBSnapshots[*].[DBSnapshotIdentifier,SnapshotCreateTime,Status]' \
--output table

# Delete old manual snapshot (automated ones are managed by retention)
aws rds delete-db-snapshot \
--db-snapshot-identifier <snapshot-id>

2.3 Verify Backups

Monthly verification checklist:

  • Automated backups completing successfully (CloudWatch → RDS Events)
  • At least one manual snapshot exists from current month
  • Snapshot retention matches policy
  • Test restore to temporary instance (quarterly)

3. RDS Recovery Procedures

3.1 Point-in-Time Recovery (PITR)

Use case: Recover from accidental data deletion or corruption

# Restore to a specific point in time
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier invapp-dev-postgres \
--target-db-instance-identifier invapp-dev-postgres-restored \
--restore-time "2026-01-30T15:30:00Z" \
--db-instance-class db.t3.micro \
--vpc-security-group-ids sg-XXXXXXXXX \
--db-subnet-group-name invapp-dev-db-subnet-group \
--no-multi-az \
--publicly-accessible

Post-restore steps:

  1. Wait for instance to become available (~10-20 minutes)
  2. Verify data integrity
  3. Update application to point to restored instance (or rename instances)
  4. Delete old instance if recovery is complete

3.2 Restore from Snapshot

Use case: Complete database recovery or environment cloning

# List available snapshots
aws rds describe-db-snapshots \
--db-instance-identifier invapp-dev-postgres \
--query 'DBSnapshots[*].[DBSnapshotIdentifier,SnapshotCreateTime]' \
--output table

# Restore from snapshot
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier invapp-dev-postgres-restored \
--db-snapshot-identifier <snapshot-id> \
--db-instance-class db.t3.micro \
--vpc-security-group-ids sg-XXXXXXXXX \
--db-subnet-group-name invapp-dev-db-subnet-group \
--no-multi-az \
--publicly-accessible

3.3 Instance Replacement (Rename Method)

Use case: Replace failed instance with restored version

# 1. Restore to temporary name
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier invapp-dev-postgres-new \
--db-snapshot-identifier <snapshot-id> \
...

# 2. Wait for new instance to be available
aws rds wait db-instance-available \
--db-instance-identifier invapp-dev-postgres-new

# 3. Rename old instance (if still exists)
aws rds modify-db-instance \
--db-instance-identifier invapp-dev-postgres \
--new-db-instance-identifier invapp-dev-postgres-old \
--apply-immediately

# 4. Wait for rename
sleep 60

# 5. Rename new instance to production name
aws rds modify-db-instance \
--db-instance-identifier invapp-dev-postgres-new \
--new-db-instance-identifier invapp-dev-postgres \
--apply-immediately

# 6. Update DNS/connection strings if needed
# (Amplify env vars point to instance identifier, may need update)

# 7. Delete old instance after verification
aws rds delete-db-instance \
--db-instance-identifier invapp-dev-postgres-old \
--skip-final-snapshot

4. S3 Evidence Backup

4.1 Versioning (Enabled)

All evidence buckets have versioning enabled:

# Verify versioning status
aws s3api get-bucket-versioning \
--bucket investigation-app-dev-760007728097

Recovery from accidental deletion:

# List object versions (including delete markers)
aws s3api list-object-versions \
--bucket investigation-app-dev-760007728097 \
--prefix "org-id/investigation-id/evidence-id/"

# Restore deleted object (remove delete marker)
aws s3api delete-object \
--bucket investigation-app-dev-760007728097 \
--key "path/to/file.pdf" \
--version-id "delete-marker-version-id"

# Or restore specific version
aws s3api copy-object \
--bucket investigation-app-dev-760007728097 \
--copy-source "investigation-app-dev-760007728097/path/to/file.pdf?versionId=old-version-id" \
--key "path/to/file.pdf"

4.2 Cross-Region Replication (Production - Planned)

For production, enable replication to us-west-2:

# Terraform configuration (future)
resource "aws_s3_bucket_replication_configuration" "evidence" {
bucket = aws_s3_bucket.main.id
role = aws_iam_role.replication.arn

rule {
id = "disaster-recovery"
status = "Enabled"

destination {
bucket = "arn:aws:s3:::investigation-app-prod-dr"
storage_class = "STANDARD_IA"
}
}
}

4.3 Lifecycle Rules (Current)

- Transition to IA: 90 days (cost optimization)
- Delete incomplete multipart uploads: 7 days
- Delete old versions: 90 days (configurable)

5. Cognito Backup

Cognito user data is managed by AWS and cannot be directly backed up. However:

5.1 User Export

Export user list for offline reference:

# Export users to CSV
aws cognito-idp list-users \
--user-pool-id us-east-1_C8qcVKKp5 \
--output json > cognito-users-$(date +%Y%m%d).json

# Parse to CSV
cat cognito-users-$(date +%Y%m%d).json | \
jq -r '.Users[] | [.Username, .UserStatus, (.Attributes[] | select(.Name=="email") | .Value)] | @csv' \
> cognito-users-$(date +%Y%m%d).csv

5.2 Recovery Options

ScenarioSolution
Deleted user poolContact AWS Support (no self-service restore)
Deleted userRe-create via admin API, user re-verifies email
Compromised userDisable user, reset password, revoke tokens

6. Application State Recovery

6.1 Infrastructure (Terraform)

All infrastructure is defined in code:

# Restore infrastructure to known state
cd infrastructure/terraform/environments/dev
terraform init
terraform plan
terraform apply

6.2 Application Code

# Clone from GitHub
git clone git@github.com:AlligatorC0der/investigation-app.git

# Install dependencies
npm install

# Set environment variables (from secure storage)
cp .env.example .env.local
# Edit with production values

# Deploy
git push origin main # Triggers Amplify deployment

7. Disaster Recovery Scenarios

Scenario A: Accidental Data Deletion

Symptoms: User reports missing investigation/evidence

Response:

  1. Check audit log for deletion event

    SELECT * FROM audit_log
    WHERE resource_type = 'investigation'
    AND action = 'delete'
    ORDER BY created_at DESC LIMIT 10;
  2. Determine scope (single record vs. bulk)

  3. For database records:

    • PITR restore to before deletion
    • Extract needed records
    • Import to production
  4. For S3 files:

    • Restore from version history
    • Or restore from cross-region replica

Scenario B: Database Corruption

Symptoms: Application errors, inconsistent data

Response:

  1. Assess scope of corruption
  2. Take immediate snapshot of current state (forensics)
  3. Identify last known good backup
  4. Perform PITR to before corruption
  5. Validate data integrity
  6. Switch to restored instance
  7. Investigate root cause

Scenario C: Complete Region Failure

Symptoms: All AWS us-east-1 services unavailable

Response (Production):

  1. Activate DR plan
  2. Promote cross-region S3 replica to primary
  3. Restore RDS from cross-region snapshot
  4. Update DNS to DR region
  5. Communicate status to customers

Note: Currently dev-only in single region. Production will have multi-region DR.

Scenario D: Ransomware/Compromise

Symptoms: Encrypted files, unauthorized changes, ransom demand

Response:

  1. Isolate immediately - Disable all access (see incident response plan)
  2. Do NOT pay ransom
  3. Determine attack timeline via CloudTrail/audit logs
  4. Identify last clean backup (before compromise)
  5. Restore from clean backup to isolated environment
  6. Validate no persistence mechanisms
  7. Restore service with enhanced security
  8. Report to authorities (FBI IC3, US-CERT)

8. Recovery Testing

Quarterly Test: Database Restore

StepActionExpected Result
1Create test snapshotSnapshot created in < 10 min
2Restore to new instanceInstance available in < 30 min
3Connect and verify dataAll tables present, recent data exists
4Run application health checkAPI responds correctly
5Delete test instanceClean up complete

Test Schedule:

  • Q1: January
  • Q2: April
  • Q3: July
  • Q4: October

Last Tested: TBD (schedule first test for Q2 2026)

Annual Test: Full DR Drill

  • Restore all data stores
  • Deploy application to restored infrastructure
  • Validate end-to-end functionality
  • Document recovery time achieved
  • Update procedures based on lessons learned

9. Monitoring & Alerts

# Create alarm for backup failures
aws cloudwatch put-metric-alarm \
--alarm-name "rds-backup-failed" \
--alarm-description "RDS automated backup failed" \
--metric-name "BackupRetentionPeriodStorageUsed" \
--namespace "AWS/RDS" \
--statistic "Average" \
--period 86400 \
--threshold 0 \
--comparison-operator "LessThanThreshold" \
--evaluation-periods 1 \
--alarm-actions "arn:aws:sns:us-east-1:ACCOUNT:alerts"

Events to Monitor

EventSourceAction
Backup completedRDS EventsInformational
Backup failedRDS EventsAlert + investigate
Snapshot deletedCloudTrailAudit (verify authorized)
Restore initiatedRDS EventsAlert (verify authorized)

10. Quick Reference

Key Commands

# === RDS ===

# List snapshots
aws rds describe-db-snapshots --db-instance-identifier invapp-dev-postgres

# Create manual snapshot
aws rds create-db-snapshot --db-instance-identifier invapp-dev-postgres --db-snapshot-identifier manual-YYYYMMDD

# Point-in-time restore
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier invapp-dev-postgres \
--target-db-instance-identifier invapp-restored \
--restore-time "2026-01-30T15:00:00Z"

# Restore from snapshot
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier invapp-restored \
--db-snapshot-identifier <snapshot-id>

# === S3 ===

# List object versions
aws s3api list-object-versions --bucket investigation-app-dev-760007728097 --prefix "path/"

# Restore deleted object
aws s3api delete-object --bucket BUCKET --key KEY --version-id DELETE_MARKER_ID

# === Cognito ===

# Export users
aws cognito-idp list-users --user-pool-id us-east-1_C8qcVKKp5 > users.json

Instance Identifiers

EnvironmentRDS InstanceS3 BucketCognito Pool
Devinvapp-dev-postgresinvestigation-app-dev-760007728097us-east-1_C8qcVKKp5
StagingTBDTBDTBD
ProductionTBDTBDTBD

Revision History

VersionDateAuthorChanges
1.02026-01-30Joe EtherageInitial version

References