diff --git a/assets/linkFortyLogo.png b/assets/linkFortyLogo.png
new file mode 100644
index 0000000..bee8254
Binary files /dev/null and b/assets/linkFortyLogo.png differ
diff --git a/assets/logo.png b/assets/logo.png
new file mode 100644
index 0000000..ad6ac92
Binary files /dev/null and b/assets/logo.png differ
diff --git a/examples/Dockerfile b/examples/Dockerfile
new file mode 100644
index 0000000..0c65710
--- /dev/null
+++ b/examples/Dockerfile
@@ -0,0 +1,45 @@
+FROM node:22-alpine
+
+WORKDIR /app
+
+# Install curl and dumb-init for health checks and signal handling
+# Split to avoid busybox trigger issues in ARM64 QEMU builds
+RUN apk add --no-cache --no-scripts curl dumb-init && \
+ /bin/busybox --install -s || true
+
+# Create non-root user for security
+RUN addgroup -g 1001 -S linkforty && \
+ adduser -S linkforty -u 1001 -G linkforty
+
+# Copy package files
+COPY package*.json ./
+
+# Install dependencies (skip prepare script since we already built in CI)
+RUN npm ci --only=production --ignore-scripts && \
+ npm cache clean --force
+
+# Copy source files
+COPY dist ./dist
+COPY examples/basic-server.ts ./
+
+# Install tsx for running TypeScript
+RUN npm install -g tsx
+
+# Change ownership to non-root user
+RUN chown -R linkforty:linkforty /app
+
+# Switch to non-root user
+USER linkforty
+
+# Expose port
+EXPOSE 3000
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
+ CMD curl -f http://localhost:3000/api/sdk/v1/health || exit 1
+
+# Use dumb-init for proper signal handling
+ENTRYPOINT ["dumb-init", "--"]
+
+# Run migrations on startup and then start server
+CMD ["sh", "-c", "tsx dist/scripts/migrate.js && tsx basic-server.ts"]
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..f673e5f
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,329 @@
+
+

+
+ # @linkforty/core Examples
+
+ This directory contains example implementations for deploying LinkForty Core.
+
+
+## Quick Start with Docker Compose
+
+The easiest way to get started is using Docker Compose, which will set up PostgreSQL, Redis, and the LinkForty server.
+
+### 1. Start All Services
+
+```bash
+cd examples
+docker compose up -d
+```
+
+This will start:
+- PostgreSQL database (port 5432)
+- Redis cache (port 6379)
+- LinkForty server (port 3000)
+
+### 2. Access the Server
+
+The API will be available at `http://localhost:3000`
+
+**Test it:**
+```bash
+# Health check
+curl http://localhost:3000/health
+
+# Create a test link (you'll need a userId first)
+curl -X POST http://localhost:3000/api/links \
+ -H "Content-Type: application/json" \
+ -d '{
+ "userId": "test-user",
+ "originalUrl": "https://example.com",
+ "title": "My First Link"
+ }'
+```
+
+### 3. View Logs
+
+```bash
+docker compose logs -f linkforty
+```
+
+### 4. Stop Services
+
+```bash
+docker compose down
+```
+
+To remove volumes (data will be lost):
+```bash
+docker compose down -v
+```
+
+## Basic Server (Node.js)
+
+If you prefer to run the server directly with Node.js:
+
+### Prerequisites
+
+- Node.js 18+
+- PostgreSQL 14+
+- Redis 6+
+
+### 1. Install Dependencies
+
+```bash
+npm install @linkforty/core
+```
+
+### 2. Start PostgreSQL and Redis
+
+Using Docker:
+```bash
+docker run -d --name postgres -p 5432:5432 \
+ -e POSTGRES_DB=linkforty \
+ -e POSTGRES_USER=linkforty \
+ -e POSTGRES_PASSWORD=changeme \
+ postgres:15-alpine
+
+docker run -d --name redis -p 6379:6379 \
+ redis:7-alpine
+```
+
+Or install locally using your package manager.
+
+### 3. Run the Example Server
+
+```bash
+# Set environment variables
+export DATABASE_URL=postgresql://linkforty:changeme@localhost:5432/linkforty
+export REDIS_URL=redis://localhost:6379
+export PORT=3000
+
+# Run the server
+npx tsx examples/basic-server.ts
+```
+
+## Custom Implementation
+
+You can also create your own server implementation:
+
+### TypeScript Example
+
+```typescript
+import { createServer } from '@linkforty/core';
+
+async function start() {
+ const server = await createServer({
+ database: {
+ url: process.env.DATABASE_URL,
+ pool: {
+ min: 2,
+ max: 10,
+ },
+ },
+ redis: {
+ url: process.env.REDIS_URL,
+ },
+ cors: {
+ origin: ['https://yourdomain.com'],
+ },
+ logger: true,
+ });
+
+ // Add custom routes
+ server.get('/custom', async (request, reply) => {
+ return { message: 'Custom endpoint' };
+ });
+
+ await server.listen({
+ port: 3000,
+ host: '0.0.0.0',
+ });
+
+ console.log('Server running!');
+}
+
+start();
+```
+
+### JavaScript Example
+
+```javascript
+const { createServer } = require('@linkforty/core');
+
+async function start() {
+ const server = await createServer({
+ database: {
+ url: 'postgresql://linkforty:changeme@localhost:5432/linkforty'
+ },
+ redis: {
+ url: 'redis://localhost:6379'
+ }
+ });
+
+ await server.listen({ port: 3000, host: '0.0.0.0' });
+ console.log('Server running on http://localhost:3000');
+}
+
+start().catch(console.error);
+```
+
+## Environment Variables
+
+Create a `.env` file:
+
+```bash
+DATABASE_URL=postgresql://linkforty:changeme@localhost:5432/linkforty
+REDIS_URL=redis://localhost:6379
+PORT=3000
+NODE_ENV=development
+CORS_ORIGIN=*
+```
+
+## Database Migrations
+
+The database schema is automatically initialized on first startup. If you need to run migrations manually:
+
+```bash
+npx tsx node_modules/@linkforty/core/dist/scripts/migrate.js
+```
+
+## Production Deployment
+
+For production deployments:
+
+1. **Use environment variables** for configuration
+2. **Enable Redis** for caching
+3. **Set up PostgreSQL replication** for high availability
+4. **Use a process manager** (PM2, systemd)
+5. **Set NODE_ENV=production**
+6. **Configure CORS** to allow only your domains
+7. **Use HTTPS** with a reverse proxy (nginx, Caddy)
+
+### Example with PM2
+
+```bash
+# Install PM2
+npm install -g pm2
+
+# Start server
+pm2 start examples/basic-server.ts --name linkforty
+
+# View logs
+pm2 logs linkforty
+
+# Monitor
+pm2 monit
+
+# Restart
+pm2 restart linkforty
+
+# Set to start on boot
+pm2 startup
+pm2 save
+```
+
+### Example nginx config
+
+```nginx
+server {
+ listen 80;
+ server_name links.yourdomain.com;
+
+ location / {
+ proxy_pass http://localhost:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_cache_bypass $http_upgrade;
+ }
+}
+```
+
+## API Usage Examples
+
+### Create a Link
+
+```bash
+curl -X POST http://localhost:3000/api/links \
+ -H "Content-Type: application/json" \
+ -d '{
+ "userId": "user-123",
+ "originalUrl": "https://example.com/product",
+ "title": "Product Page",
+ "iosUrl": "myapp://product/123",
+ "androidUrl": "myapp://product/123",
+ "utmParameters": {
+ "source": "twitter",
+ "medium": "social",
+ "campaign": "launch"
+ },
+ "customCode": "product-launch"
+ }'
+```
+
+### Get All Links
+
+```bash
+curl "http://localhost:3000/api/links?userId=user-123"
+```
+
+### Update a Link
+
+```bash
+curl -X PUT "http://localhost:3000/api/links/link-id?userId=user-123" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "title": "Updated Title",
+ "isActive": false
+ }'
+```
+
+### Get Analytics
+
+```bash
+# Overview
+curl "http://localhost:3000/api/analytics/overview?userId=user-123&days=30"
+
+# Link-specific
+curl "http://localhost:3000/api/analytics/links/link-id?userId=user-123&days=7"
+```
+
+### Test Redirect
+
+```bash
+curl -I http://localhost:3000/product-launch
+```
+
+## Troubleshooting
+
+### Database Connection Failed
+
+- Verify PostgreSQL is running: `docker ps | grep postgres`
+- Check connection string in DATABASE_URL
+- Ensure database exists: `psql -U linkforty -d linkforty -c "SELECT 1;"`
+
+### Redis Connection Failed
+
+- Verify Redis is running: `docker ps | grep redis`
+- Test connection: `redis-cli ping`
+- Make sure REDIS_URL is correct
+
+### Port Already in Use
+
+- Check what's using the port: `lsof -i :3000`
+- Change the PORT environment variable
+
+### Migrations Not Running
+
+- Run manually: `npx tsx node_modules/@linkforty/core/dist/scripts/migrate.js`
+- Check database permissions
+
+## Support
+
+- Documentation: https://github.com/linkforty/core
+- Issues: https://github.com/linkforty/core/issues
+- Discussions: https://github.com/linkforty/core/discussions
diff --git a/examples/basic-server.ts b/examples/basic-server.ts
new file mode 100644
index 0000000..cb435c9
--- /dev/null
+++ b/examples/basic-server.ts
@@ -0,0 +1,46 @@
+import { createServer } from '@linkforty/core';
+
+function getTrustProxy(): boolean | number | undefined {
+ const v = process.env.TRUST_PROXY;
+ if (v === undefined || v === '') return undefined;
+ if (v === '1' || v.toLowerCase() === 'true') return true;
+ const n = Number(v);
+ if (!Number.isNaN(n) && n >= 0) return n;
+ return undefined;
+}
+
+async function start() {
+ const server = await createServer({
+ database: {
+ url: process.env.DATABASE_URL || 'postgresql://postgres:password@localhost:5432/linkforty',
+ },
+ redis: {
+ url: process.env.REDIS_URL || 'redis://localhost:6379',
+ },
+ cors: {
+ origin: process.env.CORS_ORIGIN || '*',
+ },
+ trustProxy: getTrustProxy(),
+ });
+
+ await server.listen({
+ port: Number(process.env.PORT) || 3000,
+ host: '0.0.0.0',
+ });
+
+ console.log('LinkForty server running on http://localhost:3000');
+ console.log('');
+ console.log('API Endpoints:');
+ console.log(' POST /api/links - Create a new link');
+ console.log(' GET /api/links - List all links (requires ?userId=xxx)');
+ console.log(' GET /api/links/:id - Get a specific link (requires ?userId=xxx)');
+ console.log(' PUT /api/links/:id - Update a link (requires ?userId=xxx)');
+ console.log(' DELETE /api/links/:id - Delete a link (requires ?userId=xxx)');
+ console.log(' GET /api/analytics/overview - Get analytics overview (requires ?userId=xxx)');
+ console.log(' GET /api/analytics/links/:linkId - Get link analytics (requires ?userId=xxx)');
+ console.log('');
+ console.log('Public Endpoint:');
+ console.log(' GET /:shortCode - Redirect to target URL');
+}
+
+start().catch(console.error);
diff --git a/examples/docker-compose.yml b/examples/docker-compose.yml
new file mode 100644
index 0000000..23cfed7
--- /dev/null
+++ b/examples/docker-compose.yml
@@ -0,0 +1,56 @@
+services:
+ postgres:
+ image: postgres:15-alpine
+ environment:
+ POSTGRES_DB: linkforty
+ POSTGRES_USER: linkforty
+ POSTGRES_PASSWORD: changeme
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ ports:
+ - "5432:5432"
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U linkforty"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
+
+ redis:
+ image: redis:7-alpine
+ ports:
+ - "6379:6379"
+ volumes:
+ - redis_data:/data
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
+
+ linkforty:
+ build:
+ context: ..
+ dockerfile: examples/Dockerfile
+ depends_on:
+ postgres:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ environment:
+ DATABASE_URL: postgresql://linkforty:changeme@postgres:5432/linkforty?sslmode=disable
+ REDIS_URL: redis://redis:6379
+ NODE_ENV: production
+ PORT: 3000
+ ports:
+ - "3000:3000"
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:3000/api/sdk/v1/health"]
+ interval: 5s
+ timeout: 5s
+ retries: 10
+ start_period: 10s
+ restart: unless-stopped
+
+volumes:
+ postgres_data:
+ redis_data:
diff --git a/infra/CONTRIBUTING.md b/infra/CONTRIBUTING.md
new file mode 100644
index 0000000..a097ee7
--- /dev/null
+++ b/infra/CONTRIBUTING.md
@@ -0,0 +1,425 @@
+
+

+
+ # Contributing Infrastructure Templates
+
+ Thank you for your interest in contributing infrastructure templates to LinkForty! This guide will help you add support for new cloud platforms and deployment methods.
+
+
+## Overview
+
+We welcome community contributions for additional infrastructure providers. This allows LinkForty users to deploy on their preferred platforms with minimal setup.
+
+## Official vs Community Support
+
+### Official Support (Maintained by Core Team)
+- Docker & Docker Compose (`examples/`)
+- Fly.io (`infra/fly.io/`)
+
+### Community Support (Maintained by Contributors)
+- All other platforms
+- We validate they work but may not provide detailed troubleshooting
+
+## Contribution Guidelines
+
+### Before You Start
+
+1. **Check existing issues/PRs** to avoid duplicate work
+2. **Open a discussion** on GitHub to gauge interest
+3. **Choose a popular platform** that benefits many users
+4. **Test thoroughly** before submitting
+
+### Requested Providers
+
+We're especially interested in templates for:
+- **AWS** (ECS/Fargate, Elastic Beanstalk)
+- **Google Cloud** (Cloud Run, GKE)
+- **Azure** (Container Instances, App Service)
+- **Railway**
+- **Render**
+- **DigitalOcean App Platform**
+- **Kubernetes** (generic manifests)
+- **Terraform** (multi-cloud IaC)
+
+## Template Requirements
+
+### Must-Have Components
+
+Your infrastructure template **must** include:
+
+1. **Configuration Files**
+ - Platform-specific deployment config (e.g., `app.yaml`, `terraform.tf`)
+ - Environment variable template
+ - Build/deployment instructions
+
+2. **Documentation**
+ - `DEPLOYMENT.md` - Step-by-step deployment guide
+ - Database setup instructions
+ - Redis setup instructions (optional but recommended)
+ - Cost estimates for different tiers
+
+3. **Security Guidelines**
+ - SSL/TLS configuration
+ - Secret management best practices
+ - Network security recommendations
+ - Backup strategy
+
+4. **Testing**
+ - Proof of successful deployment
+ - Screenshots or deployment logs
+ - Performance benchmarks (optional)
+
+### Directory Structure
+
+Add your template under `infra/[provider]/`:
+
+```
+infra/
+├── your-provider/
+│ ├── README.md # Quick overview
+│ ├── DEPLOYMENT.md # Detailed deployment guide
+│ ├── config.[ext] # Platform-specific config
+│ ├── .env.production.example # Environment variables
+│ └── [additional files] # Provider-specific resources
+```
+
+### Example Structure: AWS ECS
+
+```
+infra/
+├── aws-ecs/
+│ ├── README.md # Overview of AWS ECS deployment
+│ ├── DEPLOYMENT.md # Step-by-step guide
+│ ├── task-definition.json # ECS task definition
+│ ├── service.json # ECS service config
+│ ├── cloudformation.yaml # Infrastructure template (optional)
+│ ├── .env.production.example # Environment variables
+│ └── scripts/
+│ ├── setup.sh # Automated setup script (optional)
+│ └── deploy.sh # Deployment script (optional)
+```
+
+## Documentation Standards
+
+### DEPLOYMENT.md Template
+
+Your `DEPLOYMENT.md` should include:
+
+```markdown
+# Deploying LinkForty to [Provider Name]
+
+## Prerequisites
+- Account setup
+- CLI tools needed
+- Required permissions
+
+## Step 1: Database Setup
+- PostgreSQL 13+ configuration
+- Connection string format
+- Security settings
+
+## Step 2: Redis Setup (Optional)
+- Redis configuration
+- Connection string format
+
+## Step 3: Application Deployment
+- Configuration steps
+- Secret management
+- Build and deploy process
+
+## Step 4: Verification
+- Health check verification
+- Test redirect creation
+- Troubleshooting common issues
+
+## Scaling
+- Vertical scaling
+- Horizontal scaling
+- Multi-region deployment
+
+## Monitoring
+- Logs access
+- Metrics dashboard
+- Alerts setup
+
+## Cost Estimation
+- Free tier (if available)
+- Small production setup
+- Large production setup
+
+## Troubleshooting
+- Common issues and solutions
+
+## Resources
+- Platform documentation links
+```
+
+### README.md Template
+
+```markdown
+# LinkForty on [Provider Name]
+
+Quick overview of deploying LinkForty to [Provider].
+
+## Quick Start
+
+[One-click deploy button if available]
+
+## Features
+- What makes this platform good for LinkForty
+- Key benefits
+
+## Cost Estimate
+- Starting at $X/month
+
+## Documentation
+- [Full deployment guide](./DEPLOYMENT.md)
+
+## Support
+- Community-maintained
+- Link to platform's support
+```
+
+## Code Quality Standards
+
+### Configuration Files
+
+- **Use comments** to explain non-obvious settings
+- **Include defaults** that work for most users
+- **Parameterize** where possible (don't hardcode values)
+- **Follow platform conventions** (naming, structure)
+
+### Environment Variables
+
+- **Match LinkForty's standards** (see `examples/.env.example`)
+- **Include all required variables**
+- **Document optional variables**
+- **Show example values** (with placeholders for secrets)
+
+### Security
+
+- **Never include actual secrets** in example files
+- **Use secure defaults** (SSL/TLS, authentication)
+- **Document security best practices**
+- **Include secret rotation instructions**
+
+## Testing Your Template
+
+Before submitting, test your template:
+
+### 1. Fresh Deployment Test
+
+- [ ] Start from scratch (new account or clean environment)
+- [ ] Follow your DEPLOYMENT.md step-by-step
+- [ ] Verify all commands work as documented
+- [ ] Note any issues or unclear steps
+
+### 2. Functional Test
+
+- [ ] Application starts successfully
+- [ ] Health check endpoint responds
+- [ ] Create a test link via API
+- [ ] Verify redirect works
+- [ ] Check logs are accessible
+- [ ] Test database connection
+- [ ] Test Redis connection (if used)
+
+### 3. Security Test
+
+- [ ] HTTPS is enforced
+- [ ] Secrets are not exposed in logs
+- [ ] Database uses SSL
+- [ ] CORS is configurable
+- [ ] No public exposure of internal services
+
+### 4. Documentation Test
+
+- [ ] Another person can follow your guide successfully
+- [ ] All prerequisites are listed
+- [ ] Cost estimates are accurate
+- [ ] Troubleshooting section is helpful
+
+## Pull Request Process
+
+### 1. Prepare Your PR
+
+Create a branch:
+```bash
+git checkout -b infra/add-[provider-name]
+```
+
+Add your files:
+```bash
+git add infra/[provider-name]/
+```
+
+Commit with clear message:
+```bash
+git commit -m "Add infrastructure template for [Provider Name]"
+```
+
+### 2. Update Main README
+
+Add your provider to `infra/README.md`:
+
+```markdown
+### [Provider Name] (Community)
+**Location:** [`infra/[provider-name]/`](./[provider-name]/)
+
+**Best for:**
+- [Use case 1]
+- [Use case 2]
+
+[View deployment guide →](./[provider-name]/DEPLOYMENT.md)
+```
+
+### 3. PR Description Template
+
+```markdown
+## Summary
+Adds infrastructure template for deploying LinkForty to [Provider Name].
+
+## What's Included
+- [ ] Configuration files
+- [ ] Deployment guide
+- [ ] Environment variable template
+- [ ] Security documentation
+- [ ] Cost estimates
+
+## Testing
+- [ ] Successfully deployed to [Provider]
+- [ ] Verified all features work
+- [ ] Tested by at least one other person
+
+## Screenshots/Logs
+[Include proof of successful deployment]
+
+## Cost Estimate
+- Free tier: [Yes/No]
+- Small production: ~$X/month
+- Medium production: ~$Y/month
+
+## Additional Notes
+[Any platform-specific quirks or considerations]
+```
+
+### 4. Review Process
+
+Your PR will be reviewed for:
+- **Completeness** - All required components present
+- **Accuracy** - Instructions work as documented
+- **Security** - Follows best practices
+- **Quality** - Clear, well-documented
+- **Maintenance** - Reasonable to maintain
+
+We may request changes or improvements.
+
+### 5. After Merge
+
+- Your template will be listed in the main README
+- You'll be credited as the maintainer
+- Users may open issues specific to your platform
+- You'll be tagged for platform-specific questions
+
+## Maintenance Expectations
+
+### As a Contributor
+
+You're expected to:
+- **Respond to issues** related to your platform (within reason)
+- **Update templates** when platform changes significantly
+- **Test updates** before merging changes
+- **Notify maintainers** if you can no longer maintain
+
+### If You Can't Maintain
+
+If you can't continue maintaining:
+1. Open an issue titled "Seeking maintainer for [Provider]"
+2. Tag current maintainers
+3. We'll mark it as "community-seeking-maintainer"
+
+### What Core Maintainers Will Do
+
+- Review and merge PRs for your platform
+- Help with general LinkForty questions
+- Validate deployments still work (periodically)
+- Archive unmaintained platforms (if necessary)
+
+### What Core Maintainers Won't Do
+
+- Debug provider-specific issues (beyond basic validation)
+- Maintain deep expertise in all platforms
+- Provide 24/7 support for community platforms
+
+## Support Boundaries
+
+### In-Scope Support (You Provide)
+
+- Platform-specific deployment questions
+- Configuration issues unique to the platform
+- Cost optimization for that platform
+- Platform CLI/API usage
+
+### Out-of-Scope Support (User's Responsibility)
+
+- Cloud account setup and billing
+- Platform account permissions/access issues
+- General cloud computing questions
+- Non-Link-Forty application issues
+
+### Core LinkForty Support (Maintainers Provide)
+
+- Application bugs
+- Database schema issues
+- API behavior questions
+- General architecture questions
+
+## Examples to Learn From
+
+### Good Example: Fly.io Template
+
+Located at `infra/fly.io/`, this template demonstrates:
+- ✅ Clear, step-by-step instructions
+- ✅ Complete configuration files with comments
+- ✅ Security checklist
+- ✅ Cost estimates at multiple tiers
+- ✅ Troubleshooting section
+- ✅ Links to platform documentation
+
+### What Not to Do
+
+- ❌ One-liner deployment without explanation
+- ❌ Missing environment variables
+- ❌ No security guidance
+- ❌ Untested instructions
+- ❌ Hardcoded values (like app names)
+- ❌ No cost information
+
+## Getting Help
+
+### Questions About Contributing
+
+- Open a discussion on GitHub
+- Tag maintainers: @[maintainer-username]
+- Join our community chat (if available)
+
+### Technical Questions
+
+- Review existing templates (especially `infra/fly.io/`)
+- Check LinkForty's main documentation
+- Ask in GitHub Discussions
+
+## Recognition
+
+Contributors will be:
+- Listed in the template's README as maintainer
+- Mentioned in release notes
+- Credited in the project's contributors list
+
+Thank you for helping make LinkForty accessible on more platforms!
+
+## License
+
+All infrastructure templates are licensed under the same MIT license as LinkForty Core.
+
+By contributing, you agree to license your contribution under this license.
diff --git a/infra/README.md b/infra/README.md
new file mode 100644
index 0000000..ff98cf7
--- /dev/null
+++ b/infra/README.md
@@ -0,0 +1,121 @@
+
+

+
+ # LinkForty Infrastructure
+
+ This directory contains infrastructure-as-code templates and deployment guides for running LinkForty in production environments.
+
+
+## Available Deployment Options
+
+### Docker & Docker Compose (Self-Hosted)
+For local development or self-managed infrastructure.
+
+**Location:** [`examples/`](../examples/)
+
+**Best for:**
+- Local development
+- Self-managed VPS/bare metal servers
+- Custom infrastructure requirements
+- Full control over all components
+
+**What's included:**
+- Multi-stage Dockerfile for production builds
+- Docker Compose orchestration with PostgreSQL and Redis
+- Environment configuration examples
+
+[View Docker deployment guide →](../examples/README.md)
+
+---
+
+### Fly.io (Managed Platform)
+Recommended for production deployments with minimal DevOps overhead.
+
+**Location:** [`infra/fly.io/`](./fly.io/)
+
+**Best for:**
+- Production deployments
+- Global edge distribution
+- Auto-scaling applications
+- Managed PostgreSQL and Redis
+- Teams without dedicated DevOps
+
+**What's included:**
+- `fly.toml` - Application configuration
+- Deployment guide with step-by-step instructions
+- PostgreSQL and Redis setup guides
+- Production security checklist
+- Environment configuration templates
+
+[View Fly.io deployment guide →](./fly.io/DEPLOYMENT.md)
+
+**Estimated costs:** Starting at ~$10-15/month for small production deployments
+
+---
+
+## Choosing a Deployment Option
+
+| Feature | Docker Compose | Fly.io |
+|-------------------------------|----------------------------|-----------------------------|
+| **Setup complexity** | Medium | Low |
+| **Infrastructure management** | You manage | Fully managed |
+| **Scaling** | Manual | Automatic |
+| **Global distribution** | Manual setup | Built-in edge regions |
+| **Database backups** | You configure | Automated |
+| **SSL/TLS certificates** | You configure | Automatic |
+| **Cost** | Infrastructure only | ~$10-15+/month |
+| **Best for** | Custom needs, self-hosting | Production, fast deployment |
+
+## Community-Contributed Providers
+
+We welcome community contributions for additional infrastructure providers! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
+
+**Requested providers:**
+- AWS (ECS/Fargate)
+- Google Cloud Run
+- Azure Container Instances
+- Railway
+- Render
+- DigitalOcean App Platform
+
+## Support
+
+### Official Support
+We provide official support and maintenance for:
+- Docker & Docker Compose templates (in `examples/`)
+- Fly.io templates (in `infra/fly.io/`)
+
+### Community Support
+Additional provider templates are community-maintained. We validate they work but may not provide detailed troubleshooting for provider-specific issues.
+
+### Getting Help
+
+- **Application issues:** [GitHub Issues](https://github.com/yourusername/linkforty-core/issues)
+- **Infrastructure questions:** Check provider-specific documentation first
+- **Security concerns:** See [SECURITY.md](../SECURITY.md)
+
+## Security Notice
+
+**Production Deployment Responsibility**
+
+While we provide infrastructure templates and security checklists, **you are responsible for**:
+- Securing your cloud provider accounts
+- Managing secrets and API keys
+- Configuring firewalls and network policies
+- Compliance with relevant regulations (GDPR, HIPAA, etc.)
+- Monitoring and incident response
+- Cost management and billing
+
+Always review the security checklist for your chosen platform before deploying to production.
+
+## Quick Start
+
+1. Choose your deployment platform
+2. Follow the deployment guide in the respective directory
+3. Review the security checklist
+4. Deploy and test
+5. Set up monitoring and backups
+
+## License
+
+All infrastructure templates are provided under the same MIT license as LinkForty Core.
diff --git a/infra/fly.io/.env.production.example b/infra/fly.io/.env.production.example
new file mode 100644
index 0000000..6fc737b
--- /dev/null
+++ b/infra/fly.io/.env.production.example
@@ -0,0 +1,143 @@
+# LinkForty Production Environment Configuration
+#
+# This file shows all environment variables needed for production deployment on Fly.io
+#
+# SECURITY WARNING: Never commit actual secrets to version control!
+# Use `fly secrets set` to configure these values securely.
+#
+# Example:
+# fly secrets set DATABASE_URL="postgresql://..."
+# fly secrets set REDIS_URL="rediss://..."
+
+# ============================================================================
+# REQUIRED: Database Configuration
+# ============================================================================
+
+# PostgreSQL connection string
+# Format: postgresql://username:password@host:port/database?sslmode=require
+#
+# For Fly Postgres (automatically set by `fly postgres attach`):
+# postgresql://user:pass@appname.internal:5432/dbname?sslmode=require
+#
+# For external providers (Supabase, Neon, AWS RDS):
+# Use connection string from provider dashboard
+#
+# IMPORTANT: Always include ?sslmode=require for production
+DATABASE_URL="postgresql://username:password@host:5432/linkforty?sslmode=require"
+
+# ============================================================================
+# OPTIONAL BUT RECOMMENDED: Redis Cache
+# ============================================================================
+
+# Redis connection string (TLS encrypted)
+# Format: rediss://default:password@host:6379
+#
+# For Upstash Redis (via Fly.io):
+# Run: fly redis create
+# Use the REDIS_URL provided
+#
+# IMPORTANT: Use rediss:// (double 's') for TLS encryption in production
+#
+# If not set, LinkForty will work but without caching (slower, higher DB load)
+REDIS_URL="rediss://default:password@host:6379"
+
+# ============================================================================
+# APPLICATION CONFIGURATION
+# ============================================================================
+
+# Environment mode (should always be "production" for deployed apps)
+NODE_ENV="production"
+
+# Server port (default: 8080, must match fly.toml internal_port)
+PORT="8080"
+
+# ============================================================================
+# CORS CONFIGURATION
+# ============================================================================
+
+# Allowed origins for CORS (comma-separated for multiple origins)
+#
+# Single origin:
+# CORS_ORIGIN="https://yourdomain.com"
+#
+# Multiple origins:
+# CORS_ORIGIN="https://yourdomain.com,https://app.yourdomain.com,https://www.yourdomain.com"
+#
+# SECURITY WARNING: Never use "*" in production - only specific domains!
+CORS_ORIGIN="https://yourdomain.com"
+
+# ============================================================================
+# OPTIONAL: Advanced Configuration
+# ============================================================================
+
+# Log level (error, warn, info, debug)
+# Default: "info"
+# Use "error" or "warn" in production to reduce log volume
+LOG_LEVEL="info"
+
+# Request timeout in milliseconds
+# Default: 30000 (30 seconds)
+# REQUEST_TIMEOUT="30000"
+
+# Database connection pool settings
+# Default min: 2, max: 10
+# Adjust based on your database plan and expected load
+# DB_POOL_MIN="2"
+# DB_POOL_MAX="10"
+
+# Redis cache TTL (time-to-live) in seconds
+# Default: 3600 (1 hour)
+# Higher values = less DB load, potentially stale data
+# Lower values = more accurate, higher DB load
+# REDIS_TTL="3600"
+
+# ============================================================================
+# CUSTOM DOMAIN (Optional)
+# ============================================================================
+
+# If using a custom domain for short links
+# Example: If your short links should be https://go.yourdomain.com/abc123
+# Set this to match your domain configuration
+#
+# See: https://fly.io/docs/app-guides/custom-domains-with-fly/
+# CUSTOM_DOMAIN="go.yourdomain.com"
+
+# ============================================================================
+# MONITORING & OBSERVABILITY (Optional)
+# ============================================================================
+
+# Sentry DSN for error tracking
+# Sign up at https://sentry.io and get your DSN
+# SENTRY_DSN="https://...@sentry.io/..."
+
+# New Relic license key
+# NEW_RELIC_LICENSE_KEY="..."
+
+# Datadog API key
+# DATADOG_API_KEY="..."
+
+# ============================================================================
+# DEPLOYMENT CHECKLIST
+# ============================================================================
+#
+# Before deploying to production, ensure:
+#
+# [ ] DATABASE_URL is set with sslmode=require
+# [ ] REDIS_URL is set (or intentionally omitted)
+# [ ] CORS_ORIGIN is set to your actual domain(s), NOT "*"
+# [ ] NODE_ENV is set to "production"
+# [ ] All secrets are set via `fly secrets set`, not in fly.toml
+# [ ] You've reviewed SECURITY.md checklist
+# [ ] Database migrations have been tested
+# [ ] Health check endpoint (/health) works
+# [ ] You have database backups configured
+#
+# Set secrets with:
+# fly secrets set DATABASE_URL="..." --app your-app
+# fly secrets set REDIS_URL="..." --app your-app
+# fly secrets set CORS_ORIGIN="..." --app your-app
+#
+# Verify secrets (values are hidden):
+# fly secrets list --app your-app
+#
+# ============================================================================
diff --git a/infra/fly.io/DEPLOYMENT.md b/infra/fly.io/DEPLOYMENT.md
new file mode 100644
index 0000000..f423e21
--- /dev/null
+++ b/infra/fly.io/DEPLOYMENT.md
@@ -0,0 +1,372 @@
+# Deploying LinkForty to Fly.io
+
+This guide walks you through deploying LinkForty to [Fly.io](https://fly.io), a global application platform that makes deployment simple.
+
+## Prerequisites
+
+1. **Fly.io Account**
+ - Sign up at https://fly.io/app/sign-up
+ - Credit card required (but has generous free tier)
+
+2. **Fly CLI Installed**
+ ```bash
+ # macOS/Linux
+ curl -L https://fly.io/install.sh | sh
+
+ # Windows
+ iwr https://fly.io/install.ps1 -useb | iex
+ ```
+
+3. **Authenticate**
+ ```bash
+ fly auth login
+ ```
+
+4. **LinkForty Built Locally**
+ ```bash
+ npm install
+ npm run build
+ ```
+
+## Step 1: Customize Configuration
+
+1. Edit `infra/fly.io/fly.toml`:
+ ```toml
+ app = "your-unique-app-name" # Must be globally unique
+ primary_region = "iad" # Choose your region
+ ```
+
+2. Available regions (run `fly platform regions` for full list):
+ - `iad` - Washington DC (US East)
+ - `lax` - Los Angeles (US West)
+ - `lhr` - London (Europe)
+ - `fra` - Frankfurt (Europe)
+ - `syd` - Sydney (Asia-Pacific)
+ - `nrt` - Tokyo (Asia-Pacific)
+
+## Step 2: Create PostgreSQL Database
+
+LinkForty requires PostgreSQL 13+.
+
+```bash
+# Create a Postgres cluster
+fly postgres create --name linkforty-db --region iad
+
+# Choose configuration when prompted:
+# - Development: 1GB RAM, 10GB storage (free tier eligible)
+# - Production: 2GB+ RAM, 20GB+ storage
+
+# Attach database to your app
+fly postgres attach linkforty-db --app your-unique-app-name
+```
+
+This automatically sets the `DATABASE_URL` secret in your app.
+
+**Alternative:** Use [Supabase](https://supabase.com) or another managed PostgreSQL provider and set `DATABASE_URL` manually (see Step 5).
+
+📖 Detailed PostgreSQL setup: [fly.postgres.md](./fly.postgres.md)
+
+## Step 3: Create Redis Cache (Optional but Recommended)
+
+Redis improves performance by ~90% for repeated link lookups.
+
+```bash
+# Create Upstash Redis (Fly's Redis partner)
+fly redis create --name linkforty-redis --region iad
+
+# Choose plan when prompted:
+# - Development: Free tier (256MB)
+# - Production: Eviction-$10 or Eviction-$40
+
+# Note the REDIS_URL provided - you'll set it in Step 5
+```
+
+📖 Detailed Redis setup: [fly.redis.md](./fly.redis.md)
+
+## Step 4: Create Your Fly.io App
+
+From the root of your LinkForty project:
+
+```bash
+# Copy fly.toml to root directory
+cp infra/fly.io/fly.toml fly.toml
+
+# Create the app (don't deploy yet)
+fly apps create your-unique-app-name --org personal
+```
+
+## Step 5: Set Secrets (Environment Variables)
+
+```bash
+# DATABASE_URL (automatically set if you used fly postgres attach)
+# If using external PostgreSQL:
+fly secrets set DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require"
+
+# REDIS_URL (from Step 3 output)
+fly secrets set REDIS_URL="redis://default:password@host:6379"
+
+# CORS Origin (your frontend domain)
+fly secrets set CORS_ORIGIN="https://yourdomain.com"
+
+# Optional: Custom port (defaults to 8080)
+fly secrets set PORT="8080"
+
+# View configured secrets (values are hidden)
+fly secrets list
+```
+
+⚠️ **Security Note:** Never commit secrets to git. Use `fly secrets set` only.
+
+## Step 6: Initial Deployment
+
+```bash
+# Deploy from the root directory (where fly.toml is located)
+fly deploy
+
+# This will:
+# 1. Build your Docker image
+# 2. Push to Fly.io registry
+# 3. Run migrations (via release_command in fly.toml)
+# 4. Deploy to your chosen region(s)
+# 5. Start health checks
+```
+
+**First deployment takes 3-5 minutes.** Subsequent deployments are faster.
+
+## Step 7: Verify Deployment
+
+```bash
+# Check app status
+fly status
+
+# View recent logs
+fly logs
+
+# Open your app in browser
+fly open
+
+# Test the health endpoint
+curl https://your-app.fly.dev/health
+```
+
+## Step 8: Create Your First Link
+
+```bash
+# Using curl
+curl -X POST https://your-app.fly.dev/api/links \
+ -H "Content-Type: application/json" \
+ -d '{
+ "userId": "user-123",
+ "iosUrl": "myapp://product/123",
+ "androidUrl": "myapp://product/123",
+ "webUrl": "https://mysite.com/product/123"
+ }'
+
+# Response includes your short code
+# {"code":"abc123","shortUrl":"https://your-app.fly.dev/abc123"}
+```
+
+Test the redirect:
+```bash
+curl -L https://your-app.fly.dev/abc123
+```
+
+## Scaling Your Deployment
+
+### Vertical Scaling (More Resources Per Machine)
+
+Edit `fly.toml`:
+```toml
+[vm]
+ cpu_kind = "shared" # or "performance" for dedicated CPUs
+ cpus = 2
+ memory_mb = 512 # or 1024, 2048, etc.
+```
+
+Then deploy:
+```bash
+fly deploy
+```
+
+### Horizontal Scaling (More Machines)
+
+```bash
+# Scale to 3 machines in primary region
+fly scale count 3
+
+# Or use auto-scaling (edit fly.toml first)
+# Uncomment the [[services.autoscaling]] section
+fly deploy
+```
+
+### Multi-Region Deployment
+
+Edit `fly.toml` to uncomment regions:
+```toml
+[[regions]]
+name = "iad" # US East
+[[regions]]
+name = "lhr" # Europe
+[[regions]]
+name = "nrt" # Asia
+```
+
+Deploy:
+```bash
+fly deploy
+```
+
+Fly.io automatically routes users to the nearest region.
+
+## Database Migrations
+
+Migrations run automatically on deploy (via `release_command` in fly.toml).
+
+To run manually:
+```bash
+# SSH into a machine
+fly ssh console
+
+# Run migrations
+npm run migrate
+
+# Exit
+exit
+```
+
+## Monitoring and Logs
+
+```bash
+# Real-time logs
+fly logs
+
+# Filter by app instance
+fly logs -i instance-id
+
+# View metrics dashboard
+fly dashboard
+
+# Check machine status
+fly status
+
+# View recent deployments
+fly releases
+```
+
+## Troubleshooting
+
+### Deployment Fails
+
+```bash
+# Check build logs
+fly logs --app your-app
+
+# Verify secrets are set
+fly secrets list
+
+# Check app configuration
+fly config show
+```
+
+### Health Checks Failing
+
+Ensure your app exposes `/health` endpoint:
+
+```typescript
+// In your Fastify setup
+fastify.get('/health', async (request, reply) => {
+ return { status: 'ok' };
+});
+```
+
+Check `fly.toml` health check path matches.
+
+### Database Connection Issues
+
+```bash
+# Verify DATABASE_URL is set
+fly secrets list
+
+# Check database status
+fly postgres status linkforty-db
+
+# View database connection info
+fly postgres db list linkforty-db
+```
+
+### Out of Memory Errors
+
+Increase memory allocation in `fly.toml`:
+```toml
+[vm]
+ memory_mb = 512 # Increase from 256
+```
+
+### High Latency
+
+Consider:
+- Enabling Redis cache (see Step 3)
+- Multi-region deployment
+- Increasing machine resources
+- Database connection pooling (already configured)
+
+## Updating Your Deployment
+
+```bash
+# After making code changes:
+npm run build
+fly deploy
+
+# To rollback to previous version:
+fly releases
+fly releases rollback
+```
+
+## Cost Optimization
+
+**Free Tier Eligible Setup:**
+- 1x shared-cpu-1x machine (256MB) = Free
+- Fly Postgres (1GB, development tier) = Free
+- Upstash Redis (256MB) = Free
+- **Total: $0/month** for low-traffic apps
+
+**Small Production Setup (~$10-15/month):**
+- 1x shared-cpu-1x (512MB) = ~$3.50/month
+- Fly Postgres (2GB) = ~$7/month
+- Upstash Redis (Eviction-$10) = ~$10/month
+- **Total: ~$20/month**
+
+**View your costs:**
+```bash
+fly billing show
+```
+
+## Security Checklist
+
+Before going to production, review [SECURITY.md](./SECURITY.md) for:
+- Environment variable security
+- Database SSL configuration
+- CORS settings
+- Secret rotation
+- Backup strategy
+
+## Next Steps
+
+- Set up custom domain: https://fly.io/docs/app-guides/custom-domains-with-fly/
+- Configure TLS certificates: Automatic with Fly.io
+- Set up monitoring alerts: https://fly.io/docs/reference/metrics/
+- Enable database backups: See [fly.postgres.md](./fly.postgres.md)
+
+## Getting Help
+
+- **Fly.io Docs:** https://fly.io/docs/
+- **Fly.io Community:** https://community.fly.io/
+- **LinkForty Issues:** https://github.com/yourusername/linkforty-core/issues
+- **Security Issues:** See [SECURITY.md](../../SECURITY.md)
+
+## Resources
+
+- [Fly.io Pricing](https://fly.io/docs/about/pricing/)
+- [Fly.io Regions](https://fly.io/docs/reference/regions/)
+- [Fly.io Node.js Guide](https://fly.io/docs/languages-and-frameworks/node/)
+- [Fly.io PostgreSQL](https://fly.io/docs/postgres/)
diff --git a/infra/fly.io/SECURITY.md b/infra/fly.io/SECURITY.md
new file mode 100644
index 0000000..151bb57
--- /dev/null
+++ b/infra/fly.io/SECURITY.md
@@ -0,0 +1,391 @@
+# Security Checklist for Production Deployment
+
+This checklist helps ensure your LinkForty deployment on Fly.io follows security best practices.
+
+## ✅ Pre-Deployment Security Checklist
+
+### Environment Variables & Secrets
+
+- [ ] All sensitive values are set using `fly secrets set` (never in `fly.toml`)
+- [ ] `DATABASE_URL` includes `?sslmode=require` for PostgreSQL
+- [ ] `REDIS_URL` includes authentication credentials
+- [ ] `CORS_ORIGIN` is set to your actual frontend domain(s), not `*`
+- [ ] `NODE_ENV` is set to `"production"`
+- [ ] No secrets are committed to version control
+- [ ] `.env` files are in `.gitignore`
+
+### Database Security
+
+- [ ] PostgreSQL uses SSL/TLS connections (`sslmode=require`)
+- [ ] Database password is strong (20+ characters, random)
+- [ ] Database is not publicly accessible (Fly Postgres is private by default)
+- [ ] Connection pooling is configured (default: 2-10 connections)
+- [ ] Database backups are enabled (see [fly.postgres.md](./fly.postgres.md))
+- [ ] Backup retention policy is configured
+
+### Redis Security
+
+- [ ] Redis requires authentication (Upstash Redis includes this by default)
+- [ ] Redis connection uses TLS (`rediss://` protocol)
+- [ ] Redis is not publicly accessible
+- [ ] Connection timeout is configured
+
+### Application Security
+
+- [ ] CORS is properly configured (not set to `*` in production)
+- [ ] Rate limiting is enabled for link creation endpoints
+- [ ] Input validation is active (Zod schemas)
+- [ ] SQL injection protection via parameterized queries (pg library handles this)
+- [ ] Health check endpoint (`/health`) exposes no sensitive data
+- [ ] Error messages don't leak sensitive information
+- [ ] Logging doesn't include secrets or PII
+
+### Network Security
+
+- [ ] HTTPS is enforced (`force_https = true` in fly.toml)
+- [ ] HTTP is redirected to HTTPS
+- [ ] Health checks use HTTPS
+- [ ] No sensitive services are exposed publicly
+
+### Access Control
+
+- [ ] Fly.io account uses strong password + 2FA
+- [ ] Fly.io organization access is limited to required team members
+- [ ] Deploy tokens (if used in CI/CD) have minimum required permissions
+- [ ] Database credentials are rotated regularly (quarterly minimum)
+
+---
+
+## 🔐 Security Configuration Details
+
+### 1. Database URL Security
+
+Your `DATABASE_URL` should look like:
+```
+postgresql://user:password@host:5432/dbname?sslmode=require
+```
+
+Key requirements:
+- `sslmode=require` - Forces SSL/TLS encryption
+- Strong password (20+ characters)
+- Host should be internal Fly network (`.internal` domain) if using Fly Postgres
+
+Set it securely:
+```bash
+fly secrets set DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require"
+```
+
+### 2. Redis URL Security
+
+Your `REDIS_URL` should look like:
+```
+rediss://default:password@host:6379
+```
+
+Key requirements:
+- `rediss://` protocol (TLS encrypted)
+- Authentication password included
+- Upstash Redis (recommended) includes TLS by default
+
+Set it securely:
+```bash
+fly secrets set REDIS_URL="rediss://default:password@host:6379"
+```
+
+### 3. CORS Configuration
+
+For production, set specific origins:
+
+```bash
+# Single origin
+fly secrets set CORS_ORIGIN="https://yourdomain.com"
+
+# Multiple origins (comma-separated)
+fly secrets set CORS_ORIGIN="https://yourdomain.com,https://app.yourdomain.com"
+```
+
+Never use `*` in production - this allows any website to make requests to your API.
+
+### 4. Rate Limiting
+
+LinkForty includes built-in rate limiting. Verify it's enabled in your deployment:
+
+- Link creation: Limited by IP address
+- Analytics queries: Limited by userId
+- Redirect endpoints: Unlimited (by design for fast redirects)
+
+Monitor rate limit metrics:
+```bash
+fly logs | grep "rate limit"
+```
+
+### 5. Input Validation
+
+LinkForty uses Zod for input validation. Ensure validation errors are logged:
+
+```bash
+fly logs | grep "validation"
+```
+
+Common validation issues to monitor:
+- Invalid URLs
+- Malformed userId
+- Invalid UTM parameters
+- Expired links
+
+---
+
+## 🔄 Secret Rotation Policy
+
+Rotate secrets regularly to minimize compromise risk.
+
+### Quarterly Rotation (Every 3 months)
+
+1. **Database Password**
+ ```bash
+ # On Fly Postgres
+ fly postgres connect -a linkforty-db
+ ALTER USER your_user WITH PASSWORD 'new-strong-password';
+ \q
+
+ # Update secret
+ fly secrets set DATABASE_URL="postgresql://user:new-password@host:5432/dbname?sslmode=require"
+ ```
+
+2. **Redis Password**
+ ```bash
+ # Generate new Upstash Redis credentials
+ # In Upstash dashboard: Reset password
+
+ # Update secret
+ fly secrets set REDIS_URL="rediss://default:new-password@host:6379"
+ ```
+
+3. **API Keys** (if you add API authentication)
+ ```bash
+ fly secrets set API_KEY="new-random-key"
+ ```
+
+### After Security Incident
+
+Rotate ALL secrets immediately:
+- Database credentials
+- Redis credentials
+- Any API keys
+- Fly.io deploy tokens (if compromised)
+
+---
+
+## 🔍 Security Monitoring
+
+### Log Monitoring
+
+Monitor these security-relevant events:
+
+```bash
+# Failed database connections
+fly logs | grep "connection refused"
+
+# Rate limit hits
+fly logs | grep "rate limit"
+
+# Validation errors
+fly logs | grep "validation error"
+
+# Suspicious activity patterns
+fly logs | grep "error"
+```
+
+### Metrics to Watch
+
+- **Unusual traffic spikes** - Potential DDoS or abuse
+- **High error rates** - Potential attack or misconfiguration
+- **Database connection errors** - Credential issues or network problems
+- **Redis connection failures** - Service degradation
+
+Access metrics:
+```bash
+fly dashboard # Web UI with graphs
+fly status # Current health
+```
+
+### Automated Alerts (Recommended)
+
+Set up alerts for:
+- High error rates (>5% of requests)
+- Service downtime
+- Database connection failures
+- Unusual traffic patterns
+
+Fly.io integrates with:
+- Sentry (error tracking)
+- Datadog (monitoring)
+- Prometheus (metrics)
+
+---
+
+## 🛡️ Incident Response Plan
+
+### If Secrets Are Compromised
+
+1. **Immediately rotate all secrets**
+ ```bash
+ # See "Secret Rotation Policy" section above
+ ```
+
+2. **Review access logs**
+ ```bash
+ fly logs --all
+ ```
+
+3. **Check for unauthorized database changes**
+ ```bash
+ fly postgres connect -a linkforty-db
+ SELECT * FROM users ORDER BY created_at DESC LIMIT 100;
+ SELECT * FROM links ORDER BY created_at DESC LIMIT 100;
+ ```
+
+4. **Review Fly.io access logs**
+ - Check Fly.io dashboard for recent deploys
+ - Review team member access
+ - Check for unknown IP addresses
+
+5. **Deploy with new secrets**
+ ```bash
+ fly deploy
+ ```
+
+6. **Document the incident**
+ - What was compromised?
+ - How was it discovered?
+ - What actions were taken?
+ - How to prevent in the future?
+
+### If Database Is Compromised
+
+1. **Restore from backup**
+ ```bash
+ # See fly.postgres.md for backup/restore instructions
+ ```
+
+2. **Analyze what data was accessed/modified**
+
+3. **Notify affected users** (if PII was compromised)
+
+4. **Review and strengthen security measures**
+
+---
+
+## 🔒 Data Protection
+
+### Personal Identifiable Information (PII)
+
+LinkForty collects:
+- IP addresses (for geolocation)
+- User agent strings (for device detection)
+- Referrer URLs (for analytics)
+- User IDs (provided by you)
+
+**Your responsibilities:**
+- Comply with GDPR, CCPA, or relevant privacy laws
+- Implement data retention policies
+- Provide user data deletion endpoints
+- Maintain privacy policy
+- Obtain user consent where required
+
+### Data Retention
+
+Consider implementing:
+- Automatic deletion of click events older than X days
+- User data export functionality
+- Data anonymization for old analytics
+
+Example cleanup query:
+```sql
+DELETE FROM click_events WHERE created_at < NOW() - INTERVAL '90 days';
+```
+
+Schedule via cron or Fly.io scheduled tasks.
+
+### Encryption
+
+- **In transit:** HTTPS/TLS for all connections (enforced by Fly.io)
+- **At rest:** Fly Postgres encrypts data at rest automatically
+- **Application level:** Consider encrypting sensitive user data in JSONB fields
+
+---
+
+## 🚨 Security Vulnerabilities
+
+### Reporting Security Issues
+
+**DO NOT** open public GitHub issues for security vulnerabilities.
+
+Instead:
+- Email security@yourdomain.com (set up a security contact)
+- Use GitHub Security Advisories (private disclosure)
+- Allow 90 days for responsible disclosure
+
+### Keeping Dependencies Updated
+
+```bash
+# Check for vulnerabilities
+npm audit
+
+# Fix automatically where possible
+npm audit fix
+
+# Review and update dependencies quarterly
+npm outdated
+npm update
+```
+
+Subscribe to security advisories:
+- Node.js security releases
+- Fastify security updates
+- PostgreSQL security announcements
+
+---
+
+## ✅ Security Checklist Summary
+
+Print and complete this checklist before every production deployment:
+
+**Pre-Deploy:**
+- [ ] Secrets set via `fly secrets set` (not in code)
+- [ ] Database uses SSL (`sslmode=require`)
+- [ ] CORS configured for specific domains
+- [ ] HTTPS enforced in `fly.toml`
+- [ ] No secrets in version control
+
+**Post-Deploy:**
+- [ ] Health check passes
+- [ ] HTTPS redirect works
+- [ ] CORS allows only intended domains
+- [ ] Database connection successful
+- [ ] Redis connection successful
+- [ ] Rate limiting active
+- [ ] Error logging working
+
+**Ongoing:**
+- [ ] Secrets rotated quarterly
+- [ ] Dependencies updated monthly
+- [ ] Logs monitored weekly
+- [ ] Backups verified monthly
+- [ ] Access control reviewed quarterly
+
+---
+
+## 📚 Additional Resources
+
+- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
+- [Fly.io Security Best Practices](https://fly.io/docs/reference/security/)
+- [PostgreSQL Security](https://www.postgresql.org/docs/current/runtime-config-connection.html#RUNTIME-CONFIG-CONNECTION-SSL)
+- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)
+- [Fastify Security](https://www.fastify.io/docs/latest/Guides/Security/)
+
+---
+
+**Last Updated:** 2025-01-13
+
+Review and update this checklist quarterly or after any security incident.
diff --git a/infra/fly.io/fly.postgres.md b/infra/fly.io/fly.postgres.md
new file mode 100644
index 0000000..dac0d8a
--- /dev/null
+++ b/infra/fly.io/fly.postgres.md
@@ -0,0 +1,561 @@
+# PostgreSQL Setup on Fly.io
+
+This guide covers setting up and managing PostgreSQL for LinkForty on Fly.io.
+
+## Overview
+
+LinkForty requires PostgreSQL 13 or higher. Fly.io offers managed PostgreSQL clusters with:
+- Automated backups
+- Point-in-time recovery
+- High availability options
+- Built-in monitoring
+- Private networking
+
+## Creating a PostgreSQL Cluster
+
+### Option 1: Development/Small Production
+
+For testing or small deployments:
+
+```bash
+fly postgres create --name linkforty-db \
+ --region iad \
+ --initial-cluster-size 1 \
+ --vm-size shared-cpu-1x \
+ --volume-size 10
+```
+
+**Configuration:**
+- 1 machine (no high availability)
+- 1 CPU, 256MB RAM
+- 10GB storage
+- **Cost:** Free tier eligible
+
+### Option 2: Production with High Availability
+
+For production deployments:
+
+```bash
+fly postgres create --name linkforty-db \
+ --region iad \
+ --initial-cluster-size 3 \
+ --vm-size shared-cpu-2x \
+ --volume-size 20
+```
+
+**Configuration:**
+- 3 machines (1 primary + 2 replicas)
+- 2 CPUs, 512MB RAM per machine
+- 20GB storage per machine
+- Automatic failover
+- **Cost:** ~$20-30/month
+
+### Interactive Creation
+
+Simply run without arguments for interactive prompts:
+
+```bash
+fly postgres create
+```
+
+The CLI will ask:
+1. App name (e.g., `linkforty-db`)
+2. Region (choose closest to your app)
+3. Configuration (Development vs Production)
+
+## Attaching Database to Your App
+
+After creating the cluster:
+
+```bash
+fly postgres attach linkforty-db --app your-linkforty-app
+```
+
+This automatically:
+- Creates a dedicated database and user for your app
+- Sets the `DATABASE_URL` secret in your app
+- Configures connection pooling
+- Enables SSL connections
+
+## Manual Connection String Setup
+
+If you prefer manual setup or use external PostgreSQL:
+
+```bash
+# Get connection details
+fly postgres connect -a linkforty-db
+
+# In psql:
+CREATE DATABASE linkforty;
+CREATE USER linkforty_user WITH ENCRYPTED PASSWORD 'your-strong-password';
+GRANT ALL PRIVILEGES ON DATABASE linkforty TO linkforty_user;
+\q
+
+# Set the DATABASE_URL secret
+fly secrets set DATABASE_URL="postgresql://linkforty_user:your-strong-password@linkforty-db.internal:5432/linkforty?sslmode=require" --app your-linkforty-app
+```
+
+## Connection String Format
+
+Your `DATABASE_URL` should follow this format:
+
+```
+postgresql://username:password@host:port/database?sslmode=require
+```
+
+**Components:**
+- `username` - Database user (created by `attach` or manually)
+- `password` - Strong password (20+ characters)
+- `host` - For Fly Postgres: `appname.internal` (private network)
+- `port` - Usually `5432`
+- `database` - Database name
+- `sslmode=require` - **Required** for secure connections
+
+## Running Migrations
+
+### Automatic (Recommended)
+
+Migrations run automatically on deploy via `fly.toml`:
+
+```toml
+[deploy]
+ release_command = "npm run migrate"
+```
+
+Every deployment:
+1. Builds your app
+2. Runs `npm run migrate` (before starting the app)
+3. Deploys if migrations succeed
+
+### Manual Migration
+
+If you need to run migrations manually:
+
+```bash
+# SSH into your app
+fly ssh console --app your-linkforty-app
+
+# Run migrations
+npm run migrate
+
+# Exit
+exit
+```
+
+Or connect directly to the database:
+
+```bash
+# Connect via psql
+fly postgres connect -a linkforty-db
+
+# Run SQL manually
+\i /path/to/migration.sql
+
+# Or use LinkForty's migration tool
+\q
+```
+
+## Database Management
+
+### Viewing Database Info
+
+```bash
+# Status of Postgres cluster
+fly postgres status linkforty-db
+
+# List databases
+fly postgres db list linkforty-db
+
+# List users
+fly postgres users list linkforty-db
+
+# View connection info
+fly postgres config view linkforty-db
+```
+
+### Connecting to Database
+
+```bash
+# Interactive psql session
+fly postgres connect -a linkforty-db
+
+# Once connected:
+\l # List databases
+\c linkforty # Connect to specific database
+\dt # List tables
+\d links # Describe table schema
+SELECT COUNT(*) FROM links; # Query data
+\q # Quit
+```
+
+### Monitoring
+
+```bash
+# View Postgres logs
+fly logs -a linkforty-db
+
+# Check CPU/memory usage
+fly status -a linkforty-db
+
+# Dashboard with metrics
+fly dashboard linkforty-db
+```
+
+## Backups and Recovery
+
+### Automatic Backups
+
+Fly Postgres includes automated backups:
+- **Development tier:** Daily backups, 7-day retention
+- **Production tier:** Daily backups, 30-day retention
+- Point-in-time recovery available
+
+### Manual Backup
+
+```bash
+# Create a snapshot
+fly volumes snapshots create -a linkforty-db
+
+# List snapshots
+fly volumes snapshots list -a linkforty-db
+
+# Get volume ID
+fly volumes list -a linkforty-db
+```
+
+### Export Database (Manual Backup)
+
+```bash
+# Dump entire database
+fly postgres connect -a linkforty-db -c "pg_dump linkforty" > backup.sql
+
+# Dump only schema
+fly postgres connect -a linkforty-db -c "pg_dump --schema-only linkforty" > schema.sql
+
+# Dump only data
+fly postgres connect -a linkforty-db -c "pg_dump --data-only linkforty" > data.sql
+```
+
+### Restore from Backup
+
+```bash
+# Restore from SQL dump
+fly postgres connect -a linkforty-db
+
+# In psql:
+DROP DATABASE linkforty; # ⚠️ CAUTION: Deletes all data
+CREATE DATABASE linkforty;
+\q
+
+# Import dump
+cat backup.sql | fly postgres connect -a linkforty-db -d linkforty
+```
+
+### Point-in-Time Recovery
+
+Contact Fly.io support for point-in-time recovery:
+
+```bash
+fly support create "Need PITR for linkforty-db to "
+```
+
+## Scaling PostgreSQL
+
+### Vertical Scaling (More Resources)
+
+```bash
+# Scale VM size
+fly postgres update linkforty-db --vm-size shared-cpu-2x
+
+# Increase storage
+fly volumes extend --size 40 -a linkforty-db
+```
+
+Available VM sizes:
+- `shared-cpu-1x` - 256MB RAM (free tier)
+- `shared-cpu-2x` - 512MB RAM
+- `shared-cpu-4x` - 1GB RAM
+- `shared-cpu-8x` - 2GB RAM
+- `performance-1x` - 2GB RAM (dedicated CPU)
+- `performance-2x` - 4GB RAM (dedicated CPU)
+
+### Horizontal Scaling (Read Replicas)
+
+Add replicas for read scaling:
+
+```bash
+# Add replica in same region
+fly postgres update linkforty-db --add-replica
+
+# Add replica in different region (multi-region)
+fly postgres update linkforty-db --add-replica --region lhr
+```
+
+**Note:** LinkForty's connection pool handles read/write routing automatically.
+
+### High Availability
+
+For production, use 3-node cluster:
+
+```bash
+fly postgres update linkforty-db --initial-cluster-size 3
+```
+
+Benefits:
+- Automatic failover (30-60 seconds)
+- 2 replicas for read scaling
+- Higher durability
+
+## Performance Tuning
+
+### Connection Pooling
+
+LinkForty includes connection pooling (configured in `src/database/pool.ts`):
+
+```typescript
+// Default configuration
+min: 2, // Minimum connections
+max: 10, // Maximum connections
+```
+
+Adjust based on your VM size:
+- 256MB RAM: max 5-10 connections
+- 512MB RAM: max 10-20 connections
+- 1GB+ RAM: max 20-50 connections
+
+### Indexes
+
+LinkForty creates these indexes automatically (via migrations):
+
+```sql
+-- Fast lookups by code
+CREATE INDEX idx_links_code ON links(code);
+
+-- User isolation
+CREATE INDEX idx_links_user_id ON links(user_id);
+
+-- Analytics queries
+CREATE INDEX idx_click_events_link_id ON click_events(link_id);
+CREATE INDEX idx_click_events_created_at ON click_events(created_at);
+```
+
+### Query Performance
+
+Monitor slow queries:
+
+```bash
+fly postgres connect -a linkforty-db
+
+# Enable logging of slow queries (>1s)
+ALTER DATABASE linkforty SET log_min_duration_statement = 1000;
+
+# View slow queries in logs
+\q
+
+fly logs -a linkforty-db | grep "duration:"
+```
+
+### Vacuum and Analyze
+
+PostgreSQL automatically runs `VACUUM` and `ANALYZE`. To run manually:
+
+```bash
+fly postgres connect -a linkforty-db
+
+VACUUM ANALYZE links;
+VACUUM ANALYZE click_events;
+```
+
+## Security
+
+### SSL/TLS Connections
+
+Always use `sslmode=require` in production:
+
+```bash
+DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"
+```
+
+Verify SSL is active:
+
+```bash
+fly postgres connect -a linkforty-db
+
+SELECT ssl.* FROM pg_stat_ssl ssl, pg_stat_activity a
+WHERE ssl.pid = a.pid AND a.usename = 'linkforty_user';
+```
+
+### Password Rotation
+
+Rotate database passwords quarterly:
+
+```bash
+fly postgres connect -a linkforty-db
+
+ALTER USER linkforty_user WITH PASSWORD 'new-strong-password';
+\q
+
+# Update secret in your app
+fly secrets set DATABASE_URL="postgresql://linkforty_user:new-strong-password@linkforty-db.internal:5432/linkforty?sslmode=require" --app your-linkforty-app
+```
+
+### Network Isolation
+
+Fly Postgres is only accessible:
+- Within your Fly.io private network (`.internal`)
+- Via WireGuard VPN (for admin access)
+
+Not exposed to public internet.
+
+## Monitoring and Alerts
+
+### Key Metrics to Monitor
+
+```bash
+# Connection count
+fly postgres connect -a linkforty-db -c "SELECT count(*) FROM pg_stat_activity;"
+
+# Database size
+fly postgres connect -a linkforty-db -c "SELECT pg_size_pretty(pg_database_size('linkforty'));"
+
+# Table sizes
+fly postgres connect -a linkforty-db -c "
+SELECT
+ schemaname,
+ tablename,
+ pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
+FROM pg_tables
+WHERE schemaname = 'public'
+ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
+"
+```
+
+### Set Up Alerts
+
+Monitor these conditions:
+- Storage > 80% full
+- Connection pool exhausted
+- Replication lag > 10s
+- CPU > 80% for 5+ minutes
+
+Use Fly.io monitoring or integrate with:
+- Datadog
+- New Relic
+- Prometheus + Grafana
+
+## Troubleshooting
+
+### Connection Refused
+
+```bash
+# Check Postgres status
+fly postgres status linkforty-db
+
+# Check DATABASE_URL is correct
+fly secrets list --app your-linkforty-app
+
+# Verify Postgres is running
+fly logs -a linkforty-db
+```
+
+### Out of Connections
+
+Increase max connections or reduce pool size:
+
+```bash
+# Check current connections
+fly postgres connect -a linkforty-db -c "SELECT count(*) FROM pg_stat_activity;"
+
+# Check max_connections
+fly postgres connect -a linkforty-db -c "SHOW max_connections;"
+```
+
+### Slow Queries
+
+```bash
+# Enable query logging
+fly postgres connect -a linkforty-db
+
+ALTER DATABASE linkforty SET log_min_duration_statement = 1000;
+
+# Analyze slow queries
+EXPLAIN ANALYZE SELECT * FROM links WHERE code = 'abc123';
+```
+
+### Storage Full
+
+```bash
+# Check current usage
+fly volumes list -a linkforty-db
+
+# Extend volume
+fly volumes extend --size 40 -a linkforty-db
+```
+
+## Alternative: External PostgreSQL
+
+Instead of Fly Postgres, you can use:
+
+### Supabase
+
+```bash
+# Create project at https://supabase.com
+# Get connection string from project settings
+
+fly secrets set DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.xxxxx.supabase.co:5432/postgres?sslmode=require"
+```
+
+### Neon
+
+```bash
+# Create project at https://neon.tech
+# Copy connection string
+
+fly secrets set DATABASE_URL="postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/neondb?sslmode=require"
+```
+
+### AWS RDS
+
+```bash
+# Create RDS PostgreSQL instance
+# Ensure security group allows Fly.io IPs
+
+fly secrets set DATABASE_URL="postgresql://admin:password@mydb.xxxxx.us-east-1.rds.amazonaws.com:5432/linkforty?sslmode=require"
+```
+
+## Cost Optimization
+
+### Free Tier Setup
+- 1x shared-cpu-1x (256MB)
+- 10GB storage
+- 1 region
+- **Cost: $0/month**
+
+### Small Production (~$7/month)
+- 1x shared-cpu-2x (512MB)
+- 20GB storage
+- Daily backups
+- **Cost: ~$7/month**
+
+### High Availability (~$20/month)
+- 3x shared-cpu-2x (512MB each)
+- 20GB storage per instance
+- Auto-failover
+- **Cost: ~$20/month**
+
+Check pricing:
+```bash
+fly pricing postgres
+```
+
+## Resources
+
+- [Fly.io Postgres Docs](https://fly.io/docs/postgres/)
+- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
+- [Connection Pooling Best Practices](https://www.postgresql.org/docs/current/runtime-config-connection.html)
+
+---
+
+**Need Help?**
+- Fly.io Community: https://community.fly.io/
+- PostgreSQL Help: https://www.postgresql.org/support/
diff --git a/infra/fly.io/fly.redis.md b/infra/fly.io/fly.redis.md
new file mode 100644
index 0000000..09fe7c0
--- /dev/null
+++ b/infra/fly.io/fly.redis.md
@@ -0,0 +1,464 @@
+# Redis Setup on Fly.io
+
+This guide covers setting up and managing Redis for LinkForty on Fly.io.
+
+## Overview
+
+Redis is **optional but highly recommended** for LinkForty. According to the project documentation, Redis caching can reduce database queries by ~90% for repeated link lookups.
+
+Fly.io partners with [Upstash](https://upstash.com/) to provide managed Redis with:
+- Serverless pricing (pay per request)
+- Global replication
+- TLS encryption
+- Automatic scaling
+- No idle charges
+
+## Creating a Redis Instance
+
+### Option 1: Upstash Redis via Fly.io (Recommended)
+
+```bash
+fly redis create --name linkforty-redis --region iad
+```
+
+You'll be prompted to choose a plan:
+
+**Free Tier:**
+- 256MB storage
+- 10,000 commands/day
+- Perfect for development
+- **Cost: $0/month**
+
+**Eviction-$10:**
+- 1GB storage
+- 100K commands/day
+- Evicts least-recently-used keys when full
+- **Cost: $10/month**
+
+**Eviction-$40:**
+- 5GB storage
+- 500K commands/day
+- Evicts least-recently-used keys when full
+- **Cost: $40/month**
+
+**No-Eviction-$120:**
+- 5GB storage
+- 1M commands/day
+- Never evicts keys (blocks writes when full)
+- **Cost: $120/month**
+
+### Option 2: Direct Upstash Setup
+
+Alternatively, create directly at [Upstash](https://upstash.com/):
+
+1. Sign up at https://upstash.com
+2. Create a new Redis database
+3. Choose region (match your Fly.io region)
+4. Select pricing plan
+5. Copy the connection string
+
+## Setting Redis URL
+
+After creation, you'll receive a `REDIS_URL`. Set it as a secret:
+
+```bash
+fly secrets set REDIS_URL="rediss://default:password@host:6379" --app your-linkforty-app
+```
+
+**Important:** Use `rediss://` (with double 's') for TLS encryption.
+
+## Redis URL Format
+
+Your `REDIS_URL` should follow this format:
+
+```
+rediss://default:password@host:6379
+```
+
+**Components:**
+- `rediss://` - Redis with TLS (**required** for security)
+- `default` - Username (Upstash default)
+- `password` - Authentication token from Upstash
+- `host` - Upstash endpoint (e.g., `xxx-us-east-1.upstash.io`)
+- `6379` - Standard Redis port
+
+## Verifying Redis Connection
+
+After setting the `REDIS_URL`, deploy and check logs:
+
+```bash
+fly deploy
+fly logs
+```
+
+Look for:
+```
+Redis connected successfully
+```
+
+If Redis is unavailable, LinkForty will still work but will query the database for every link lookup (slower).
+
+## How LinkForty Uses Redis
+
+### Cache Strategy
+
+LinkForty caches link lookups with this pattern:
+
+1. **Request:** User visits `https://your-app.fly.dev/abc123`
+2. **Check Redis:** Look for cached link data
+ - **Cache hit:** Return immediately (< 5ms)
+ - **Cache miss:** Query database, cache result
+3. **TTL:** Cached for 1 hour (configurable)
+
+### What Gets Cached
+
+- Link metadata (URLs, targeting rules, UTM params)
+- Link expiration status
+- User ID associations
+
+### What Doesn't Get Cached
+
+- Click events (always written to database)
+- Analytics aggregations
+- Link creation/updates
+
+## Performance Benefits
+
+With Redis enabled:
+- **Link redirects:** ~5ms (vs ~50-200ms without cache)
+- **Database load:** Reduced by ~90% for popular links
+- **Scalability:** Handle 1000+ req/s on small instances
+
+Without Redis:
+- Every redirect hits the database
+- Higher latency for users
+- More database resources needed
+- Lower maximum throughput
+
+## Monitoring Redis
+
+### Check Connection
+
+```bash
+# View app logs
+fly logs --app your-linkforty-app | grep -i redis
+
+# Check if REDIS_URL is set
+fly secrets list --app your-linkforty-app
+```
+
+### Monitor Usage (Upstash Dashboard)
+
+Visit [Upstash Console](https://console.upstash.com/):
+- View daily command count
+- Monitor memory usage
+- Check hit/miss ratio
+- See latency metrics
+
+### Redis CLI Access
+
+Connect to your Redis instance:
+
+```bash
+# Install redis-cli if needed
+brew install redis # macOS
+apt-get install redis-tools # Linux
+
+# Connect using REDIS_URL
+redis-cli -u "rediss://default:password@host:6379"
+
+# Once connected:
+PING # Should return PONG
+KEYS link:* # List cached links
+GET link:abc123 # View specific cached link
+TTL link:abc123 # Check time-to-live
+INFO stats # View Redis stats
+DBSIZE # Count of keys
+FLUSHDB # ⚠️ Clear all cache (use with caution)
+```
+
+## Cache Invalidation
+
+### Automatic Invalidation
+
+LinkForty automatically invalidates cache when:
+- Link is updated
+- Link is deleted
+- Link expires
+
+### Manual Invalidation
+
+If needed, clear cache for a specific link:
+
+```bash
+redis-cli -u "rediss://default:password@host:6379" DEL link:abc123
+```
+
+Clear all cached links:
+
+```bash
+redis-cli -u "rediss://default:password@host:6379" FLUSHDB
+```
+
+### Cache TTL Configuration
+
+Default TTL is 1 hour. To adjust, modify the caching layer in LinkForty:
+
+```typescript
+// In your Redis cache implementation
+const TTL = 60 * 60; // 1 hour in seconds
+await redis.set(key, value, 'EX', TTL);
+```
+
+Consider:
+- **Short TTL (5-15 min):** More accurate, higher DB load
+- **Long TTL (1-4 hours):** Lower DB load, potentially stale data
+- **No TTL:** Manual invalidation only (not recommended)
+
+## Scaling Redis
+
+### When to Upgrade
+
+Monitor these metrics:
+- **Commands/day approaching limit** → Upgrade plan
+- **Memory usage > 80%** → Upgrade plan or enable eviction
+- **High latency (>10ms)** → Consider global replication
+- **Frequent cache misses** → Increase TTL or memory
+
+### Upgrade Plan
+
+```bash
+# Via Upstash dashboard
+# https://console.upstash.com/ → Select database → Change plan
+```
+
+No downtime during plan changes.
+
+### Global Replication (Multi-Region)
+
+For lower latency worldwide, enable global replication:
+
+1. In Upstash dashboard, enable "Read Replicas"
+2. Choose replica regions (e.g., `us-east`, `eu-west`, `ap-southeast`)
+3. Upstash automatically routes reads to nearest replica
+
+**Cost:** ~2x base price per replica region
+
+## Security
+
+### TLS Encryption
+
+Always use `rediss://` (TLS) in production:
+
+```bash
+# ✅ Correct (encrypted)
+rediss://default:password@host:6379
+
+# ❌ Wrong (unencrypted)
+redis://default:password@host:6379
+```
+
+### Password Rotation
+
+Rotate Redis password quarterly:
+
+1. In Upstash dashboard: **Database → Settings → Reset Password**
+2. Copy new password
+3. Update secret:
+ ```bash
+ fly secrets set REDIS_URL="rediss://default:NEW_PASSWORD@host:6379"
+ ```
+4. Deploy:
+ ```bash
+ fly deploy
+ ```
+
+### Network Security
+
+Upstash Redis is publicly accessible but:
+- Requires authentication (password)
+- Uses TLS encryption
+- Supports IP allowlisting (in Upstash dashboard)
+
+For extra security, enable IP allowlisting:
+1. Get your Fly.io app's public IPs: `fly ips list`
+2. In Upstash dashboard: **Settings → IP Allowlist**
+3. Add Fly.io IPs
+
+## Troubleshooting
+
+### Redis Connection Fails
+
+```bash
+# Check REDIS_URL is set correctly
+fly secrets list --app your-linkforty-app
+
+# Test connection manually
+redis-cli -u "rediss://default:password@host:6379" PING
+
+# Check app logs for errors
+fly logs --app your-linkforty-app | grep -i redis
+```
+
+Common issues:
+- Wrong password → Reset in Upstash dashboard
+- Missing `rediss://` protocol → Use TLS version
+- Firewall blocking port 6379 → Check Upstash IP allowlist
+
+### High Latency
+
+```bash
+# Test latency
+redis-cli -u "rediss://default:password@host:6379" --latency
+
+# Check from your app
+redis-cli -u "rediss://default:password@host:6379" SLOWLOG GET 10
+```
+
+Solutions:
+- Enable global replication for multi-region apps
+- Check Upstash status page
+- Verify region matches your app region
+
+### Memory Full (Eviction Errors)
+
+```bash
+# Check memory usage
+redis-cli -u "rediss://default:password@host:6379" INFO memory
+
+# Check eviction stats
+redis-cli -u "rediss://default:password@host:6379" INFO stats | grep evicted
+```
+
+Solutions:
+- Upgrade to larger plan
+- Reduce cache TTL
+- Implement selective caching (only cache popular links)
+
+### Cache Hit Rate Too Low
+
+```bash
+# Check hit rate
+redis-cli -u "rediss://default:password@host:6379" INFO stats
+# Look for: keyspace_hits and keyspace_misses
+```
+
+Low hit rate causes:
+- TTL too short
+- Traffic pattern (mostly unique links)
+- Not enough memory (evictions)
+
+Solutions:
+- Increase TTL (if data staleness is acceptable)
+- Increase memory (upgrade plan)
+- Profile which links are being accessed
+
+## Running Without Redis
+
+LinkForty works without Redis, but with performance trade-offs:
+
+### Performance Without Redis
+
+- **Latency:** ~50-200ms per redirect (vs ~5ms with Redis)
+- **Database load:** 10x higher
+- **Throughput:** Lower maximum requests/second
+- **Costs:** Higher database instance needed
+
+### Disabling Redis
+
+Simply don't set `REDIS_URL`:
+
+```bash
+fly secrets unset REDIS_URL --app your-linkforty-app
+```
+
+LinkForty detects missing Redis and falls back to database-only mode.
+
+### When to Skip Redis
+
+Skip Redis if:
+- Very low traffic (< 100 redirects/day)
+- Budget is extremely tight
+- All links are unique (no repeated lookups)
+- Development/testing only
+
+## Cost Optimization
+
+### Free Tier (Good for Development)
+- 256MB storage
+- 10,000 commands/day
+- ~300 redirects/day with caching
+- **Cost: $0/month**
+
+### Small Production ($10/month)
+- 1GB storage
+- 100K commands/day
+- ~3,000 redirects/day with caching
+- **Cost: $10/month**
+
+### Estimate Your Needs
+
+Calculate commands/day:
+- Link redirect with cache hit: 1 command (`GET`)
+- Link redirect with cache miss: 2 commands (`GET` + `SET`)
+- Assume 50% hit rate: 1.5 commands per redirect
+- For 10,000 redirects/day: ~15,000 commands/day
+
+Choose plan accordingly.
+
+### Cost Monitoring
+
+```bash
+# In Upstash dashboard
+# View: Billing → Current Usage
+```
+
+Set up alerts for:
+- 80% of command limit reached
+- 80% of storage used
+
+## Alternative: Self-Hosted Redis
+
+Instead of Upstash, you can run Redis on Fly.io:
+
+### Create Redis Machine
+
+```bash
+fly launch --image redis:7-alpine --name linkforty-redis --region iad
+```
+
+**Pros:**
+- Lower cost for high traffic
+- Full control
+
+**Cons:**
+- You manage backups
+- You manage security
+- No automatic scaling
+- More operational overhead
+
+**Not recommended** unless you have specific requirements or very high traffic (>1M commands/day).
+
+## Best Practices
+
+1. **Always use TLS** (`rediss://`)
+2. **Set reasonable TTL** (1-4 hours for links)
+3. **Monitor hit rate** (target >70%)
+4. **Rotate passwords** quarterly
+5. **Enable global replication** for multi-region deployments
+6. **Set up alerts** for command limits
+7. **Start small** (free tier) and scale up based on metrics
+
+## Resources
+
+- [Upstash Documentation](https://docs.upstash.com/redis)
+- [Redis Documentation](https://redis.io/documentation)
+- [Fly.io Redis Guide](https://fly.io/docs/reference/redis/)
+- [Cache Invalidation Strategies](https://redis.io/docs/manual/keyspace-notifications/)
+
+---
+
+**Need Help?**
+- Upstash Support: https://upstash.com/docs/redis/support
+- Redis Community: https://redis.io/community
+- Fly.io Community: https://community.fly.io/
diff --git a/infra/fly.io/fly.toml b/infra/fly.io/fly.toml
new file mode 100644
index 0000000..9f34057
--- /dev/null
+++ b/infra/fly.io/fly.toml
@@ -0,0 +1,71 @@
+# Fly.io configuration for LinkForty
+# See https://fly.io/docs/reference/configuration/ for full documentation
+
+app = "linkforty-core" # Change this to your unique app name
+primary_region = "iad" # Change to your preferred region (iad = Washington DC)
+
+# Build configuration
+[build]
+ # Use the Dockerfile from the examples directory
+ dockerfile = "../../examples/Dockerfile"
+
+# Environment variables (non-sensitive)
+[env]
+ NODE_ENV = "production"
+ PORT = "8080"
+ # DATABASE_URL and REDIS_URL should be set as secrets (see DEPLOYMENT.md)
+
+# HTTP service configuration
+[http_service]
+ internal_port = 8080
+ force_https = true
+ auto_stop_machines = false # Keep at least one machine always running
+ auto_start_machines = true
+ min_machines_running = 1 # Always keep at least one instance running
+
+ # HTTP service concurrency
+ [http_service.concurrency]
+ type = "requests"
+ hard_limit = 250 # Maximum concurrent requests per machine
+ soft_limit = 200 # Target concurrent requests before scaling
+
+# Health checks
+[[http_service.checks]]
+ grace_period = "10s" # Wait before starting health checks
+ interval = "30s" # Check every 30 seconds
+ method = "GET"
+ timeout = "5s"
+ path = "/health" # Ensure your app has a /health endpoint
+
+# VM resources
+# Start with shared-cpu-1x (256MB RAM) - scale up as needed
+[vm]
+ cpu_kind = "shared"
+ cpus = 1
+ memory_mb = 256
+
+# Metrics (optional but recommended)
+[[metrics]]
+ port = 9091 # If you add Prometheus metrics
+ path = "/metrics"
+
+# Deploy configuration
+[deploy]
+ release_command = "npm run migrate" # Run migrations before deploying
+ strategy = "rolling" # Rolling deployment strategy
+
+# Scaling configuration
+# Uncomment and adjust for auto-scaling based on traffic
+# [[services.autoscaling]]
+# enabled = true
+# min_count = 1 # Minimum number of machines
+# max_count = 10 # Maximum number of machines
+
+# Multi-region deployment (optional)
+# Uncomment to deploy to multiple regions for lower latency
+# [[regions]]
+# name = "iad" # Primary region (US East)
+# [[regions]]
+# name = "lhr" # Europe (London)
+# [[regions]]
+# name = "syd" # Asia-Pacific (Sydney)