Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 15 additions & 34 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,38 +1,19 @@
# Micopay Protocol — Environment Variables
# Copy to .env and fill in your values
# PostgreSQL
POSTGRES_PASSWORD=secure_password_here

# Stellar
STELLAR_NETWORK=testnet
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
# pgBackRest Encryption
PGBACKREST_CIPHER_PASS=32_byte_minimum_secret_key

# Contracts (deployed on testnet)
ATOMIC_SWAP_CONTRACT_ID=
ATOMIC_SWAP_CONTRACT_B_ID= # Second instance for demo (chain B simulation)
MICOPAY_ESCROW_CONTRACT_ID=
# S3/MinIO Storage
AWS_ACCESS_KEY_ID=minioadmin
AWS_SECRET_ACCESS_KEY=minioadmin
BACKUP_BUCKET=micopay-backups
BACKUP_ENDPOINT=http://minio:9000

# Platform wallet
PLATFORM_SECRET_KEY= # Stellar secret key (S...)
PLATFORM_STELLAR_ADDRESS= # Derived from above (G...)
# MinIO
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin

# Agent
ANTHROPIC_API_KEY= # sk-ant-...
AGENT_STELLAR_ADDRESS= # Agent's Stellar address for x402 payments

# API
PORT=3000
API_BASE_URL=http://localhost:3000
NODE_ENV=development

# Database (optional — currently using in-memory for demo)
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/micopay

# Etherfuse anchor (SPEI <-> CETES) — see apps/api/.env.example for full set.
# Each webhook subscription (POST /ramp/webhook) returns its own base64 signing
# secret, one per event type — there's no single shared WEBHOOK_SECRET anymore.
ETHERFUSE_API_KEY=
ETHERFUSE_API_URL=https://api.sand.etherfuse.com
ETHERFUSE_WEBHOOK_SECRET_ORDER=
ETHERFUSE_WEBHOOK_SECRET_KYC=

# Demo Mode — NEVER set to true in production; for app store review builds only
DEMO_MODE=false
# Alert Webhooks
BACKUP_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
RESTORE_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
51 changes: 51 additions & 0 deletions deploy/backup/pgbackrest.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# pgBackRest Configuration for Micopay Database
# =============================================

[global]
# Repository settings
repo1-path=/var/lib/pgbackrest
repo1-retention-full=30
repo1-retention-diff=7
repo1-retention-archive=12
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=${PGBACKREST_CIPHER_PASS}

# Archive settings
archive-timeout=60
archive-queue-max=32GB
compress-type=gz
compress-level=3

# Process settings
process-max=4
log-level-console=info
log-level-file=detail
log-level-stderr=error

# S3 repository (for production)
# repo1-s3-bucket=${S3_BUCKET}
# repo1-s3-endpoint=${S3_ENDPOINT}
# repo1-s3-key=${S3_KEY}
# repo1-s3-key-secret=${S3_SECRET}
# repo1-s3-region=${S3_REGION}

# MinIO repository (for development)
repo1-type=s3
repo1-s3-bucket=micopay-backups
repo1-s3-endpoint=http://minio:9000
repo1-s3-key=minioadmin
repo1-s3-key-secret=minioadmin
repo1-s3-region=us-east-1

# Stanza configuration
[micopay]
db-host=postgres
db-port=5432
db-name=micopay
db-user=micopay
db-socket-path=/var/run/postgresql
pg1-host=postgres
pg1-port=5432
pg1-database=micopay
pg1-user=micopay
pg1-path=/var/lib/postgresql/data
42 changes: 42 additions & 0 deletions deploy/backup/postgresql.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# PostgreSQL Configuration for PITR
# =================================

# Connection settings
listen_addresses = '*'
max_connections = 100

# Memory settings
shared_buffers = 256MB
work_mem = 16MB
maintenance_work_mem = 64MB
effective_cache_size = 1GB

# WAL settings (for PITR)
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
checkpoint_timeout = 15min
max_wal_size = 4GB
min_wal_size = 1GB

# Archive settings
archive_mode = on
archive_command = 'pgbackrest --stanza=micopay archive-push %p'
archive_timeout = 60

# Backup settings
restore_command = 'pgbackrest --stanza=micopay archive-get %f "%p"'
recovery_target_timeline = 'latest'

# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = '/var/log/postgresql'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 1d
log_rotation_size = 100MB

# Performance
synchronous_commit = off
fsync = on
full_page_writes = on
83 changes: 83 additions & 0 deletions deploy/backup/scripts/backup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/sh

# Backup script for Micopay database
# =================================

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

error() {
echo "${RED}[ERROR]${NC} $1" >&2
}

success() {
echo "${GREEN}[SUCCESS]${NC} $1"
}

warn() {
echo "${YELLOW}[WARNING]${NC} $1"
}

# Check if pgbackrest is running
if ! pgrep -f "pgbackrest" > /dev/null; then
error "pgBackRest is not running"
exit 1
fi

# Determine backup type based on time
HOUR=$(date +%H)
DAY=$(date +%u)

if [ "$HOUR" -eq 2 ]; then
# Full backup (daily at 02:00)
BACKUP_TYPE="full"
log "Starting daily full backup..."
elif [ $((HOUR % 6)) -eq 0 ]; then
# Incremental backup (every 6 hours)
BACKUP_TYPE="incr"
log "Starting incremental backup..."
else
# WAL archiving (continuous)
log "WAL archiving is continuous, no backup needed"
exit 0
fi

# Run the backup
if [ "$BACKUP_TYPE" = "full" ]; then
if pgbackrest --stanza=micopay backup --type=full; then
success "Full backup completed successfully"
else
error "Full backup failed"
exit 1
fi
else
if pgbackrest --stanza=micopay backup --type=incr; then
success "Incremental backup completed successfully"
else
error "Incremental backup failed"
exit 1
fi
fi

# Check backup info
log "Backup info:"
pgbackrest --stanza=micopay info

# Send success notification (if webhook configured)
if [ -n "$BACKUP_WEBHOOK_URL" ]; then
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"status\":\"success\",\"type\":\"$BACKUP_TYPE\",\"timestamp\":\"$(date -Iseconds)\"}" \
"$BACKUP_WEBHOOK_URL" 2>/dev/null || warn "Failed to send webhook notification"
fi

exit 0
131 changes: 131 additions & 0 deletions deploy/backup/scripts/restore.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/bin/bash

# Restore script for Micopay database
# ===================================

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

error() {
echo "${RED}[ERROR]${NC} $1" >&2
}

success() {
echo "${GREEN}[SUCCESS]${NC} $1"
}

warn() {
echo "${YELLOW}[WARNING]${NC} $1"
}

# Configuration
RESTORE_TYPE="${1:-latest}" # latest, time, or specific backup
RESTORE_TIME="${2:-}" # ISO timestamp for PITR
RESTORE_TARGET="${3:-/tmp/pg_restore}"

# Validate arguments
if [ "$RESTORE_TYPE" = "time" ] && [ -z "$RESTORE_TIME" ]; then
error "RESTORE_TIME is required for PITR"
echo "Usage: $0 time '2024-01-15 10:30:00' [/tmp/pg_restore]"
exit 1
fi

log "Starting restore process..."
log "Restore type: $RESTORE_TYPE"
[ -n "$RESTORE_TIME" ] && log "Restore time: $RESTORE_TIME"
log "Restore target: $RESTORE_TARGET"

# Stop PostgreSQL
log "Stopping PostgreSQL..."
docker-compose stop postgres
docker-compose rm -f postgres

# Remove existing data
log "Removing existing data directory..."
rm -rf $RESTORE_TARGET/postgres_data
mkdir -p $RESTORE_TARGET/postgres_data

# Start empty PostgreSQL
log "Starting empty PostgreSQL container..."
docker-compose up -d postgres
sleep 10

# Run restore based on type
case $RESTORE_TYPE in
latest)
log "Restoring latest backup..."
docker-compose exec -T pgbackrest pgbackrest \
--stanza=micopay \
--delta \
--process-max=4 \
restore
;;
time)
log "Performing Point-in-Time Recovery to: $RESTORE_TIME"
docker-compose exec -T pgbackrest pgbackrest \
--stanza=micopay \
--delta \
--process-max=4 \
--type=time \
--target="$RESTORE_TIME" \
--target-action=promote \
restore
;;
backup)
# Restore specific backup (not implemented)
error "Specific backup restore not implemented yet"
exit 1
;;
*)
error "Unknown restore type: $RESTORE_TYPE"
echo "Valid types: latest, time, backup"
exit 1
;;
esac

# Start PostgreSQL with restored data
log "Starting restored PostgreSQL..."
docker-compose stop postgres
docker-compose rm -f postgres
docker-compose up -d postgres

# Wait for PostgreSQL to be ready
log "Waiting for PostgreSQL to be ready..."
sleep 10

# Run validation queries
log "Running validation checks..."
docker-compose exec -T postgres psql -U micopay -d micopay -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" > /dev/null 2>&1

if [ $? -eq 0 ]; then
success "Restore completed successfully"
success "Database is ready"
else
error "Restore validation failed"
exit 1
fi

# Print summary
log "Restore Summary:"
log " Type: $RESTORE_TYPE"
[ -n "$RESTORE_TIME" ] && log " PITR Target: $RESTORE_TIME"
log " Target Directory: $RESTORE_TARGET"

# Send success notification
if [ -n "$RESTORE_WEBHOOK_URL" ]; then
curl -X POST \
-H "Content-Type: application/json" \
-d "{\"status\":\"success\",\"type\":\"$RESTORE_TYPE\",\"timestamp\":\"$(date -Iseconds)\"}" \
"$RESTORE_WEBHOOK_URL" 2>/dev/null || warn "Failed to send webhook notification"
fi

exit 0
Loading
Loading