diff --git a/Q1/.dockerignore b/Q1/.dockerignore
new file mode 100644
index 0000000..4adcd8c
--- /dev/null
+++ b/Q1/.dockerignore
@@ -0,0 +1,6 @@
+data/
+*.pyc
+__pycache__/
+.git
+.gitignore
+README.md
diff --git a/Q1/Dockerfile b/Q1/Dockerfile
new file mode 100644
index 0000000..6e3252e
--- /dev/null
+++ b/Q1/Dockerfile
@@ -0,0 +1,13 @@
+FROM node:20-alpine3.16
+
+WORKDIR /app
+
+COPY --chown=node:node server.js .
+
+RUN npm init -y && npm install express cors
+
+USER node
+
+EXPOSE 3000
+
+CMD ["node" , "server.js"]
\ No newline at end of file
diff --git a/Q1/index.html b/Q1/index.html
new file mode 100644
index 0000000..ab0818e
--- /dev/null
+++ b/Q1/index.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+ لوحة مراقبة الـ API
+
+
+
+
+
+
مراقبة النظام
+
+
+ الحالة: جاري التحميل...
+
+
+ زمن الاستجابة: 0 ms
+
+
+ عدد الطلبات الإجمالي: 0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Q1/monitor.sh b/Q1/monitor.sh
new file mode 100755
index 0000000..ad9eb65
--- /dev/null
+++ b/Q1/monitor.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+API_URL=$1
+
+echo "Starting monitor script..."
+echo "API URL: $API_URL"
+echo "------------------------------------------------"
+
+while true; do
+ HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}" -m 5 "$API_URL")
+
+ if [ "$HTTP_STATUS" -eq 200 ]; then
+ response=$(curl -s -m 5 "$API_URL")
+
+ status=$(echo "$response" | jq -r '.status' 2>/dev/null)
+ if [ -n "$status" ] && [ "$status" != "null" ]; then
+ echo "Status: $status | Response Time: ${response_time}ms"
+ else
+ echo "HTTP 200 OK, but invalid JSON format received."
+ fi
+ elif [ "$HTTP_STATUS" -eq 000 ]; then
+ echo "Application is Offline or unreachable (Timeout)."
+ else
+ echo "Application is unhealthy. HTTP Status: $HTTP_STATUS"
+ fi
+
+ sleep 30
+done
\ No newline at end of file
diff --git a/Q1/server.js b/Q1/server.js
new file mode 100644
index 0000000..5d007da
--- /dev/null
+++ b/Q1/server.js
@@ -0,0 +1,35 @@
+const express = require('express');
+const cors = require('cors');
+const app = express();
+
+app.use(cors());
+
+let requestCount = 0;
+let lastResponseTime = 0;
+
+app.use((req, res, next) => {
+ requestCount++;
+ const start = Date.now();
+ res.on('finish', () => {
+ lastResponseTime = Date.now() - start;
+ });
+ next();
+});
+
+app.get('/health', (req, res) => {
+ res.json({
+ status: 'Online',
+ uptime_seconds: process.uptime(),
+ request_count: requestCount,
+ response_time_ms: lastResponseTime
+ });
+});
+
+app.get('/', (req, res) => {
+ res.send('Welcome to the Backend API!');
+});
+
+const PORT = process.env.PORT || 3000;
+app.listen(PORT, () => {
+ console.log(`API is running on port ${PORT}`);
+});
\ No newline at end of file
diff --git a/Q2/readme,md b/Q2/readme,md
new file mode 100644
index 0000000..33863e4
--- /dev/null
+++ b/Q2/readme,md
@@ -0,0 +1,271 @@
+# Incident Postmortem: Core Backend API Service Interruption
+
+> **Incident Date:** July 27, 2026
+> **Affected Service:** Core .NET Backend API (Ghaymah Platform)
+> **Severity:** SEV-1 (Critical)
+> **Status:** Resolved
+
+---
+
+# Table of Contents
+
+- [Incident Overview](#incident-overview)
+- [Incident Metadata](#incident-metadata)
+- [Executive Summary](#executive-summary)
+- [Incident Timeline](#incident-timeline)
+- [Root Cause Analysis](#root-cause-analysis)
+- [Proposed Auto-Scaling Architecture](#proposed-auto-scaling-architecture)
+- [Horizontal Pod Autoscaler Configuration](#horizontal-pod-autoscaler-configuration)
+- [Observability & Early Detection](#observability--early-detection)
+- [Action Items](#action-items)
+- [Lessons Learned](#lessons-learned)
+
+---
+
+# Incident Overview
+
+On **July 27, 2026**, the **Core .NET Backend API** experienced a complete service outage lasting **45 minutes**.
+
+The outage was caused by repeated **OOMKilled (Exit Code 137)** events after the application exhausted its available memory during a sudden traffic spike.
+
+The platform entered a continuous **CrashLoop** state, resulting in **100% API failure** until memory resources were increased and the containers were restarted.
+
+---
+
+# Incident Metadata
+
+| Category | Details |
+|-----------|---------|
+| **Affected Service** | Core .NET Backend API |
+| **Platform** | Ghaymah |
+| **Date** | 2026-07-27 |
+| **Downtime** | 45 Minutes |
+| **Time** | 14:00 – 14:45 EEST |
+| **Severity** | SEV-1 (Critical) |
+| **Customer Impact** | 100% API transaction failures |
+| **Observed Errors** | 502 Bad Gateway |
+
+---
+
+# Executive Summary
+
+A sudden **400% increase in traffic** caused the backend API to consume memory rapidly.
+
+The application maintained an **unbounded in-memory cache**, storing increasingly large datasets without expiration or eviction.
+
+Once the container reached its configured memory limit, the Linux kernel terminated the process (**OOMKilled - Exit Code 137**) to protect node stability.
+
+The orchestration platform continuously restarted the containers, causing a **CrashLoopBackOff** cycle and preventing the service from recovering automatically.
+
+Service was restored after:
+
+- Increasing the container memory limit
+- Restarting the backend pods
+- Verifying successful application startup
+
+Future mitigation requires:
+
+- Memory-based auto-scaling
+- Cache eviction policies
+- API pagination
+- Improved monitoring and alerting
+
+---
+
+# Incident Timeline
+
+| Time | Event |
+|------|-------|
+| **13:50** | Traffic increased by approximately **400%** due to an unexpected external campaign. |
+| **13:58** | Container memory utilization exceeded **95%** of allocated memory. |
+| **14:00** | First container terminated with **OOMKilled (Exit Code 137)**. Service degradation begins. |
+| **14:05 – 14:30** | Containers repeatedly restarted by the orchestrator, resulting in a CrashLoop and complete outage. |
+| **14:30** | On-call infrastructure engineer identified repeated OOMKilled events from platform logs. |
+| **14:35** | Memory limit increased from **512Mi** to **2Gi** and backend pods restarted manually. |
+| **14:45** | Service stabilized and API responses returned **HTTP 200 OK**. Incident closed. |
+
+---
+
+# Root Cause Analysis
+
+## Direct Cause
+
+The Linux kernel terminated the backend process because the application attempted to allocate more memory than the container's configured memory limit.
+
+```
+Exit Code: 137
+Reason: OOMKilled
+```
+
+---
+
+## Underlying Causes
+
+### Unbounded In-Memory Cache
+
+The application stored data inside a local in-memory dictionary that had:
+
+- No Time-To-Live (TTL)
+- No maximum cache size
+- No eviction policy
+
+Large requests continuously expanded the cache until the process exhausted available memory.
+
+---
+
+### Missing API Pagination
+
+Several endpoints returned very large datasets.
+
+Without pagination:
+
+- Large payloads were cached
+- Memory usage increased rapidly
+- Garbage collection became inefficient
+
+---
+
+### Lack of Horizontal Auto-Scaling
+
+The deployment relied on static memory limits and a fixed number of replicas.
+
+As traffic increased:
+
+- No new replicas were created
+- Existing containers absorbed all incoming traffic
+- Memory utilization reached critical levels
+
+---
+
+# Proposed Auto-Scaling Architecture
+
+To prevent similar incidents, backend workloads should scale automatically based on memory utilization.
+
+| Parameter | Recommended Value |
+|-----------|-------------------|
+| **Scaling Metric** | Average Memory Utilization |
+| **Target Utilization** | 75% |
+| **Minimum Replicas** | 3 |
+| **Maximum Replicas** | 12 |
+| **Scale-Up Policy** | Add up to 4 replicas immediately |
+| **Scale-Down Policy** | Wait 5 minutes after memory falls below 40% |
+
+---
+
+# Horizontal Pod Autoscaler Configuration
+
+```yaml
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: backend-api-scaler
+
+spec:
+ minReplicas: 3
+ maxReplicas: 12
+
+ metrics:
+ - type: Resource
+ resource:
+ name: memory
+ target:
+ type: Utilization
+ averageUtilization: 75
+```
+
+---
+
+# Observability & Early Detection
+
+To move from reactive troubleshooting to proactive monitoring, the following observability improvements should be implemented.
+
+## Alerting Rules
+
+### Memory Utilization Alert
+
+Trigger notification when:
+
+- Memory utilization exceeds **70%**
+- Sustained for **2 consecutive minutes**
+
+Notification targets:
+
+- Email
+- Slack
+- Webhook
+
+---
+
+### CrashLoop Detection
+
+Create a high-priority incident whenever:
+
+- Container restart count exceeds **3**
+- Within a **10-minute** window
+
+---
+
+## Dashboard Metrics
+
+Create a dedicated monitoring dashboard showing:
+
+- Container memory usage
+- Container memory limits
+- Application heap size
+- Garbage Collection duration
+- API request rate
+- Current replica count
+- Container restart count
+- CPU utilization
+- Response latency
+- Error rate (4xx / 5xx)
+
+---
+
+# Action Items
+
+| Task | Owner | Priority | Status |
+|------|-------|----------|--------|
+| Implement cache eviction (LRU + TTL) | Development Team | Critical | In Progress |
+| Enforce API pagination | Development Team | Critical | In Progress |
+| Deploy memory-based Horizontal Pod Autoscaler | DevOps Team | High | Not Started |
+| Create synthetic load tests (5× traffic) | QA Team | Medium | Not Started |
+| Update on-call operational runbooks | Operations Team | Low | Completed |
+
+---
+
+# Lessons Learned
+
+The incident highlighted several architectural improvements required to increase platform resilience.
+
+## Infrastructure
+
+- Configure Horizontal Pod Autoscaler (HPA)
+- Define resource requests and limits carefully
+- Monitor memory consumption continuously
+
+## Application
+
+- Implement cache eviction (LRU)
+- Apply cache expiration (TTL)
+- Enforce pagination on all large API endpoints
+- Optimize memory allocation patterns
+
+## Operations
+
+- Improve proactive alerting
+- Expand observability dashboards
+- Regularly execute load and stress testing
+- Update incident response runbooks
+
+---
+
+# Resolution Summary
+
+| Item | Result |
+|------|--------|
+| **Root Cause** | Unbounded in-memory cache caused container OOM |
+| **Immediate Fix** | Increased memory limit (512Mi → 2Gi) and restarted pods |
+| **Long-Term Fixes** | HPA, cache eviction, pagination, monitoring improvements |
+| **Incident Status** | ✅ Resolved |
+```
\ No newline at end of file
diff --git a/Q3-CICD/README.md b/Q3-CICD/README.md
new file mode 100644
index 0000000..3e9c476
--- /dev/null
+++ b/Q3-CICD/README.md
@@ -0,0 +1,149 @@
+# Ghaymah CLI Integration & Environment Strategy
+
+This document outlines the deployment environment strategy and provides comprehensive documentation for installing, configuring, and authenticating with the **Ghaymah Command Line Interface (CLI)**.
+
+---
+
+# Table of Contents
+
+- [Environment Strategy](#environment-strategy)
+ - [Staging Environment](#staging-environment)
+ - [Production Environment](#production-environment)
+- [Ghaymah CLI](#ghaymah-cli)
+ - [Prerequisites](#prerequisites)
+ - [Installation](#installation)
+ - [Authentication](#authentication)
+ - [Core Commands](#core-commands)
+
+---
+
+# Environment Strategy
+
+To ensure reliable deployments and stable software releases, Ghaymah uses separate environments for testing and production workloads.
+
+| Feature | Staging | Production |
+|----------|----------|------------|
+| **Purpose** | Final testing, QA, and integration validation. Mirrors production configuration as closely as possible. | Live customer-facing environment with maximum stability and availability. |
+| **Users** | Developers, QA engineers, and internal stakeholders. | End users and customers. |
+| **Data** | Mock, seeded, or anonymized datasets. | Real production data. |
+| **Deployment** | Automatic deployment after merging into the staging branch (Continuous Deployment). | Manual approval with version tracking (release tags or commit SHA). |
+| **Resources** | Lower CPU and memory allocation to reduce infrastructure costs. | High availability, load balancing, monitoring, and autoscaling. |
+
+---
+
+# Ghaymah CLI
+
+The **Ghaymah CLI** allows developers and CI/CD pipelines to interact with the platform directly from the terminal.
+
+It can be used to:
+
+- Authenticate with the platform
+- Manage applications
+- Deploy services
+- View running applications
+- Integrate deployments into CI/CD workflows
+
+---
+
+## Prerequisites
+
+Before using the CLI, you must generate a **Personal Access Token**.
+
+1. Log in to the **Ghaymah Web Console**.
+2. Navigate to:
+
+```
+Account Settings
+ └── Developer Settings
+```
+
+3. Generate a new **Personal Access Token**.
+4. Copy the token immediately.
+
+> **Note**
+>
+> The token is displayed only once. Store it securely.
+
+---
+
+## Installation
+
+### Linux (Debian/Ubuntu/Linux Mint)
+
+Install the CLI using:
+
+```bash
+curl -sL https://cli.ghaymah.systems/install.sh | sudo bash
+```
+
+Verify the installation:
+
+```bash
+ghaymah --version
+```
+
+---
+
+## Authentication
+
+For local development or automated CI/CD pipelines, authenticate using your Personal Access Token.
+
+### Step 1 — Export the Token
+
+```bash
+export GHAYMAH_TOKEN="your_personal_access_token_here"
+```
+
+### Step 2 — Login
+
+```bash
+ghaymah login --token "$GHAYMAH_TOKEN"
+```
+
+If authentication succeeds, the CLI is ready to use.
+
+---
+
+# Core Commands
+
+## List Applications
+
+Display all deployed applications and their current health status.
+
+```bash
+ghaymah apps list
+```
+
+Example output:
+
+```text
+NAME STATUS REGION
+frontend Running eu-central
+backend Running eu-central
+database Running eu-central
+```
+
+---
+
+## Check CLI Version
+
+```bash
+ghaymah --version
+```
+
+---
+
+## Display Help
+
+```bash
+ghaymah --help
+```
+
+---
+
+# Environment Summary
+
+| Environment | Deployment | Data | Approval |
+|-------------|------------|------|----------|
+| **Staging** | Automatic | Test/Mock | Not Required |
+| **Production** | Manual | Live | Required |
\ No newline at end of file
diff --git a/Q3-CICD/workflow.yml b/Q3-CICD/workflow.yml
new file mode 100644
index 0000000..1c616b2
--- /dev/null
+++ b/Q3-CICD/workflow.yml
@@ -0,0 +1,112 @@
+name: CI/CD Pipeline to Ghyamah
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+env:
+ GHYAMAH_REGISTRY: app.gitpasha.com
+ GHYAMAH_USERNAME: ${{ github.actor }}
+ ImageName: ${{ env.GHYAMAH_USERNAME }}/my-app
+jobs:
+ ###############################
+ # Test App Code Job
+ #################################
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: '16'
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Run tests
+ run: npm test
+
+ - name: Build project
+ run: npm run build
+ ########################################
+ # build and push docker image to ghyamah registry
+ ########################################
+ BuildAndPush:
+ runs-on: ubuntu-latest
+ needs: test
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Log in to Ghyamah Docker registry
+ uses: docker/login-action@v2
+ with:
+ registry: ${{ env.GHYAMAH_REGISTRY }}
+ username: ${{ env.GHYAMAH_USERNAME }}
+ password: ${{ secrets.GHYAMAH_PASSWORD }}
+
+ - name: Build and Push Docker Image
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ push: true
+ tags: |
+ ${{ env.GHYAMAH_REGISTRY }}/${{ env.ImageName }}:${{ github.sha }}
+ ${{ env.GHYAMAH_REGISTRY }}/${{ env.ImageName }}:latest
+
+#################################
+# Deploy to Staging and Production Jobs
+#################################
+
+ deploy-staging:
+ runs-on: ubuntu-latest
+ needs: BuildAndPush
+ steps:
+ - name: Install Ghyamah CLI
+ run: |
+ curl -sSL https://get.ghaymah.systems/cli/install.sh | sh
+ echo "$HOME/.ghaymah/bin" >> $GITHUB_PATH
+ - name: Log in to Ghyamah
+ run: |
+ ghyamah login --username ${{ env.GHYAMAH_USERNAME }} --password ${{ secrets.GHYAMAH_PASSWORD }}
+
+ - name: Deploy to Staging
+ env:
+ GHYAMAH_TOKEN: ${{ secrets.GHYAMAH_PASSWORD }}
+ run: |
+ ghayamah app update my-staging-app --image ${{ env.GHYAMAH_REGISTRY }}/${{ env.ImageName }}:latest
+
+
+
+#######################################
+# Deploy to Production Job with Manual Approval
+#######################################
+ deploy-production:
+ runs-on: ubuntu-latest
+ needs: BuildAndPush
+ steps:
+ - name: Wait for Manual Approval
+ uses: trstringer/manual-approval@v1
+ with:
+ secret: ${{ secrets.GHYAMAH_PASSWORD }}
+ issue-title: "Approval Required for Deployment to Production"
+ issue-body: "Please review the changes and approve the deployment to production."
+ - name: Install Ghyamah CLI
+ run: |
+ curl -sSL https://get.ghaymah.systems/cli/install.sh | sh
+ echo "$HOME/.ghaymah/bin" >> $GITHUB_PATH
+ - name: Log in to Ghyamah
+ run: |
+ ghyamah login --username ${{ env.GHYAMAH_USERNAME }} --password ${{ secrets.GHYAMAH_PASSWORD }}
+
+ - name: Deploy to Production
+ env:
+ GHYAMAH_TOKEN: ${{ secrets.GHYAMAH_PASSWORD }}
+ run: |
+ ghayamah app update my-production-app --image ${{ env.GHYAMAH_REGISTRY }}/${{ env.ImageName }}:latest
\ No newline at end of file
diff --git a/Q4/GhaymahAPI.png b/Q4/GhaymahAPI.png
new file mode 100644
index 0000000..d7a40dd
Binary files /dev/null and b/Q4/GhaymahAPI.png differ
diff --git a/Q4/README.md b/Q4/README.md
new file mode 100644
index 0000000..9977b4b
--- /dev/null
+++ b/Q4/README.md
@@ -0,0 +1,307 @@
+# High-Traffic Architecture Design (15,000 Requests/Second)
+
+This document describes the proposed architecture for handling **15,000 requests per second (RPS)** on the Ghaymah platform. It covers the system architecture, container capacity planning, cold start mitigation strategies, and the use of Ghaymah Block Storage for stateful workloads.
+
+> **Note**
+>
+> The architecture diagram below is a placeholder. Replace it with your architecture image.
+
+---
+
+# Table of Contents
+
+- [Architecture Overview](#architecture-overview)
+- [Architecture Diagram](#architecture-diagram)
+- [Request Flow](#request-flow)
+- [Container Capacity Planning](#container-capacity-planning)
+- [Cold Start Strategy](#cold-start-strategy)
+- [Ghaymah Block Storage for Stateful Workloads](#ghaymah-block-storage-for-stateful-workloads)
+- [Key Design Decisions](#key-design-decisions)
+
+---
+
+# Architecture Overview
+
+The system is designed to process **15,000 requests per second** while maintaining high availability, scalability, and fault tolerance.
+
+The architecture consists of:
+
+- WAF / CDN
+- Ghaymah Load Balancer
+- Auto-scaling application containers
+- Redis cache cluster
+- Primary database
+- Read replicas
+- Ghaymah Block Storage
+
+The application containers remain **stateless**, while all persistent data is stored on external block storage.
+
+---
+
+# Architecture Diagram
+
+The architecture diagram below shows the proposed high-traffic deployment.
+
+
+
+---
+
+# Request Flow
+
+The following sequence illustrates how requests are processed.
+
+1. Clients send requests to the application.
+2. The **WAF/CDN** filters malicious traffic and caches static assets.
+3. Requests are forwarded to the **Ghaymah Load Balancer**.
+4. The load balancer distributes traffic across healthy application containers.
+5. Containers first attempt to retrieve data from the **Redis cache**.
+6. Cache misses are forwarded to the database.
+7. Read operations are served by database replicas whenever possible.
+8. Write operations are handled by the primary database.
+9. All database data is stored on **Ghaymah Block Storage**, ensuring persistence.
+
+---
+
+# System Components
+
+| Component | Responsibility |
+|-----------|----------------|
+| **Clients** | Generate incoming traffic (15,000 RPS) |
+| **WAF / CDN** | Security filtering, DDoS protection, static content caching |
+| **Ghaymah Load Balancer** | Evenly distributes requests across healthy containers |
+| **Application Containers** | Stateless application processing |
+| **Redis Cluster** | High-speed caching layer |
+| **Primary Database** | Handles write operations |
+| **Read Replicas** | Offload read traffic from the primary database |
+| **Ghaymah Block Storage** | Persistent storage for stateful workloads |
+
+---
+
+# Container Capacity Planning
+
+The infrastructure must support **15,000 requests per second**.
+
+## Assumptions
+
+| Metric | Value |
+|---------|------:|
+| Expected Traffic | 15,000 req/s |
+| Capacity per Container | 500 req/s |
+
+---
+
+## Base Capacity
+
+```
+15,000 ÷ 500 = 30 Containers
+```
+
+---
+
+## Safety Buffer
+
+To absorb unexpected traffic spikes:
+
+```
+30 × 30% = 9 Containers
+```
+
+---
+
+## Total Required Containers
+
+```
+30 + 9 = 39 Containers
+```
+
+| Calculation | Result |
+|-------------|-------:|
+| Base Containers | 30 |
+| Safety Margin | 9 |
+| **Recommended Total** | **39 Containers** |
+
+---
+
+## Recommendation
+
+During peak traffic periods:
+
+- Maintain approximately **39 running containers**.
+- Configure the auto-scaling policy to keep the minimum replica count close to this value.
+- Scale beyond this threshold during sustained traffic increases.
+
+---
+
+# Cold Start Strategy
+
+## Problem
+
+When a new container starts, it requires time to:
+
+- Pull the container image
+- Initialize the runtime
+- Establish database connections
+- Load application dependencies
+
+If traffic is routed before initialization completes, users may experience increased latency or request failures.
+
+---
+
+## Recommended Mitigations
+
+### Readiness Probes
+
+Configure readiness probes so that traffic is routed only after the application is fully initialized.
+
+Example endpoint:
+
+```
+/health/ready
+```
+
+Only containers returning **HTTP 200 OK** should receive production traffic.
+
+---
+
+### Pre-Warming
+
+Initialize expensive resources during startup:
+
+- Database connections
+- Redis connections
+- Configuration loading
+- Dependency injection
+- Frequently used libraries
+
+Avoid performing heavy initialization during the first user request.
+
+---
+
+### Image Optimization
+
+Reduce startup time by:
+
+- Using lightweight base images
+- Removing unnecessary packages
+- Minimizing image layers
+- Keeping image sizes as small as possible
+
+Examples:
+
+- Alpine Linux
+- Distroless Images
+
+---
+
+### Capacity Buffer
+
+Maintain spare capacity (30% safety margin) so existing containers can absorb traffic while new containers complete startup.
+
+Benefits:
+
+- Reduced request latency
+- Smoother auto-scaling
+- Improved user experience
+
+---
+
+# Ghaymah Block Storage for Stateful Workloads
+
+Containers are **ephemeral** by design.
+
+If a container is deleted or restarted, its local filesystem is also removed.
+
+Persistent workloads such as:
+
+- PostgreSQL
+- MySQL
+- MariaDB
+- MongoDB
+- Message Brokers
+
+must store data externally.
+
+---
+
+## How It Works
+
+1. Provision a **Ghaymah Block Storage** volume.
+2. Attach the volume to the database container.
+3. Store all database files on the mounted volume.
+4. If the container fails, the storage remains intact.
+5. A replacement container automatically reattaches the existing volume.
+
+---
+
+## Benefits
+
+### Persistent Data
+
+Application data survives:
+
+- Container restarts
+- Platform upgrades
+- Node failures
+- Container replacements
+
+---
+
+### Compute and Storage Separation
+
+Containers remain disposable while storage persists independently.
+
+This allows infrastructure updates without risking data loss.
+
+---
+
+### Faster Recovery
+
+If the database container crashes:
+
+1. A replacement container starts.
+2. The existing block storage volume is attached.
+3. The database resumes operation with its original data.
+
+---
+
+### High Performance
+
+Ghaymah Block Storage provides dedicated storage performance for demanding workloads.
+
+Benefits include:
+
+- High IOPS
+- Low latency
+- Reliable throughput
+- Consistent database performance
+
+---
+
+# Key Design Decisions
+
+| Area | Decision |
+|------|----------|
+| Compute | Stateless application containers |
+| Scaling | Horizontal auto-scaling |
+| Load Distribution | Ghaymah Load Balancer |
+| Caching | Redis Cluster |
+| Database | Primary + Read Replica architecture |
+| Storage | Ghaymah Block Storage |
+| Availability | Multi-container deployment |
+| Cold Start Mitigation | Readiness probes, pre-warming, optimized images |
+| Capacity Planning | 39 containers (30 base + 30% buffer) |
+
+---
+
+# Summary
+
+This architecture is designed to provide:
+
+- High availability
+- Horizontal scalability
+- Fault tolerance
+- Persistent storage
+- Fast recovery from failures
+- Efficient handling of **15,000 requests per second**
+
+By combining stateless application containers, intelligent load balancing, Redis caching, database replication, and Ghaymah Block Storage, the platform can maintain stable performance during normal operations as well as sudden traffic spikes.
\ No newline at end of file
diff --git a/Q5/.dockerignore b/Q5/.dockerignore
new file mode 100644
index 0000000..4adcd8c
--- /dev/null
+++ b/Q5/.dockerignore
@@ -0,0 +1,6 @@
+data/
+*.pyc
+__pycache__/
+.git
+.gitignore
+README.md
diff --git a/Q5/Dockerfile b/Q5/Dockerfile
new file mode 100644
index 0000000..e19b0c2
--- /dev/null
+++ b/Q5/Dockerfile
@@ -0,0 +1,40 @@
+# syntax=docker/dockerfile:1
+
+FROM python:3.12-alpine
+
+# Standard-library only — no requirements.txt needed. ca-certificates is
+# required so ssl.create_default_context() can validate the target's
+# certificate chain when checking SSL expiry.
+RUN apk add --no-cache ca-certificates && update-ca-certificates
+
+# Run as a non-root user
+RUN addgroup -S monitor && adduser -S monitor -G monitor
+
+WORKDIR /app
+
+COPY monitor.py /app/monitor.py
+COPY Entrypoint.sh /app/entrypoint.sh
+COPY index.html /app/web/index.html
+
+RUN chmod +x /app/entrypoint.sh \
+ && mkdir -p /app/web/data \
+ && chown -R monitor:monitor /app
+
+USER monitor
+
+# Defaults — override any of these at `docker run` / platform deploy time.
+ENV TARGET_URL="https://mithal.space" \
+ SEARCH_PATH="/search?q=test" \
+ CHECK_INTERVAL=60 \
+ RETENTION_HOURS=24 \
+ REQUEST_TIMEOUT=10 \
+ DATA_FILE="/app/web/data/metrics.json" \
+ WEB_DIR="/app/web" \
+ PORT=8080
+
+EXPOSE 8080
+
+HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
+ CMD python3 -c "import urllib.request,sys; sys.exit(0) if urllib.request.urlopen('http://127.0.0.1:${PORT}/', timeout=3).status==200 else sys.exit(1)"
+
+ENTRYPOINT ["/app/entrypoint.sh"]
\ No newline at end of file
diff --git a/Q5/Entrypoint.sh b/Q5/Entrypoint.sh
new file mode 100644
index 0000000..79605aa
--- /dev/null
+++ b/Q5/Entrypoint.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# entrypoint.sh — runs the Python monitoring loop and a static HTTP server
+# side by side in a single container. POSIX sh so it works on Alpine's
+# default shell.
+
+set -eu
+
+PORT="${PORT:-8080}"
+WEB_DIR="${WEB_DIR:-/app/web}"
+
+echo "[entrypoint] starting monitor loop"
+python3 /app/monitor.py &
+MONITOR_PID=$!
+
+echo "[entrypoint] serving dashboard from ${WEB_DIR} on 0.0.0.0:${PORT}"
+cd "${WEB_DIR}"
+python3 -m http.server "${PORT}" --bind 0.0.0.0 &
+SERVER_PID=$!
+
+# Forward termination signals to both children and wait for them so the
+# container shuts down cleanly (e.g. on `docker stop` / platform redeploys).
+term_handler() {
+ echo "[entrypoint] shutting down..."
+ kill -TERM "$MONITOR_PID" "$SERVER_PID" 2>/dev/null || true
+ wait "$MONITOR_PID" "$SERVER_PID" 2>/dev/null || true
+ exit 0
+}
+trap term_handler TERM INT
+
+# busybox ash (Alpine's /bin/sh) has no `wait -n`, so poll instead: if
+# either child dies unexpectedly, bring the whole container down so the
+# orchestrator (Docker/Kubernetes/Cloud Run/etc.) can restart it.
+while true; do
+ if ! kill -0 "$MONITOR_PID" 2>/dev/null; then
+ echo "[entrypoint] monitor loop exited unexpectedly — stopping container"
+ term_handler
+ fi
+ if ! kill -0 "$SERVER_PID" 2>/dev/null; then
+ echo "[entrypoint] http server exited unexpectedly — stopping container"
+ term_handler
+ fi
+ sleep 2
+done
\ No newline at end of file
diff --git a/Q5/README.md b/Q5/README.md
new file mode 100644
index 0000000..1d29524
--- /dev/null
+++ b/Q5/README.md
@@ -0,0 +1,80 @@
+# mithal-space-monitor
+
+A lightweight, stdlib-only uptime/latency/SSL monitor with a single-page
+Chart.js dashboard, packaged into one container.
+
+## What's inside
+
+| File | Purpose |
+|---|---|
+| `monitor.py` | Standard-library-only Python script. Every `CHECK_INTERVAL` seconds it checks DNS resolution time, HTTP status + latency, search-endpoint latency, and SSL cert expiry, then writes a rolling `RETENTION_HOURS` window to a JSON file. |
+| `index.html` | Single-page dashboard (HTML/CSS/JS + Chart.js via CDN). Polls the JSON file every 30s and renders 24h uptime %, a 60-minute latency line chart, SSL expiry, and a table of the last 10 checks. |
+| `entrypoint.sh` | Starts `monitor.py` and `python -m http.server` side by side, forwards signals, and exits the container if either process dies (so the orchestrator restarts it). |
+| `Dockerfile` | `python:3.12-alpine` base, non-root user, healthcheck, no external Python deps. |
+
+## Configuration (environment variables)
+
+| Variable | Default | Description |
+|---|---|---|
+| `TARGET_URL` | `https://mithal.space` | URL to monitor |
+| `SEARCH_PATH` | `/search?q=test` | Path appended to the target's origin for the search-latency check |
+| `CHECK_INTERVAL` | `60` | Seconds between checks |
+| `RETENTION_HOURS` | `24` | Rolling window kept in the JSON log |
+| `REQUEST_TIMEOUT` | `10` | Per-request timeout (seconds) |
+| `PORT` | `8080` | Dashboard HTTP server port |
+
+## Build & run locally
+
+```bash
+docker build -t mithal-space-monitor .
+
+docker run -d \
+ --name mithal-monitor \
+ -p 8080:8080 \
+ -e TARGET_URL="https://mithal.space" \
+ -e SEARCH_PATH="/search?q=test" \
+ -v mithal_monitor_data:/app/data \
+ mithal-space-monitor
+
+# open http://localhost:8080
+```
+
+The `-v mithal_monitor_data:/app/data` volume is optional but recommended so
+your 24h history survives a container restart/redeploy.
+
+## Push to a registry
+
+```bash
+docker tag mithal-space-monitor registry.example.com/yourorg/mithal-space-monitor:latest
+docker push registry.example.com/yourorg/mithal-space-monitor:latest
+```
+
+## Deploy
+
+This image is a single process group exposing one HTTP port, so it runs
+as-is on most container platforms:
+
+- **Cloud Run / Container Apps / Fly.io**: deploy the image, set `PORT`
+ to match the platform's expected port (Cloud Run injects `PORT`
+ automatically - the entrypoint already respects it), mount a persistent
+ volume if the platform supports one (otherwise history resets on redeploy,
+ which is fine - it just rebuilds over the next `RETENTION_HOURS`).
+- **Kubernetes**: run as a `Deployment` with 1 replica, a `Service` of type
+ `ClusterIP`/`LoadBalancer`, and optionally a `PersistentVolumeClaim`
+ mounted at `/app/data`. The built-in `HEALTHCHECK` maps naturally to a
+ liveness probe on `GET /`.
+- **Plain VM / docker-compose**: use the `docker run` command above behind
+ your existing reverse proxy / TLS terminator.
+
+## Notes & extension points
+
+- Everything in `monitor.py` uses only the Python standard library
+ (`urllib`, `socket`, `ssl`, `json`) - no `pip install` step, no
+ dependency surface in the image.
+- Data is written atomically (`write → temp file → os.replace`) so the
+ dashboard never reads a half-written JSON file.
+- To monitor multiple targets, run one container per target (each with its
+ own `TARGET_URL`/port), or extend `monitor.py` to loop over a list of
+ targets and extend `index.html` with a target selector.
+- Add basic auth / IP allowlisting at your reverse proxy if the dashboard
+ shouldn't be public.
\ No newline at end of file
diff --git a/Q5/index.html b/Q5/index.html
new file mode 100644
index 0000000..40f6ccf
--- /dev/null
+++ b/Q5/index.html
@@ -0,0 +1,395 @@
+
+
+
+
+
+Site Watch — Status
+
+
+
+
+
+
+
+
+
+
+
+
+ Uptime · 24h
+ —
+ — checks recorded
+
+
+ Latency · latest
+ —
+ HTTP response time
+
+
+ DNS resolve
+ —
+ Latest lookup time
+
+
+ SSL expires in
+ —
+ Certificate validity
+
+
+
+
+
+
Latency — last 60 minutes
+ site vs. search endpoint, ms
+
+
+
+
+
+
+
Recent checks
+ last 10
+
+
+
+
+ | Time (UTC) |
+ Status |
+ Latency |
+ DNS |
+ Search |
+ SSL days |
+
+
+
+ | Waiting for first data… |
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Q5/monitor.py b/Q5/monitor.py
new file mode 100644
index 0000000..3cfcea2
--- /dev/null
+++ b/Q5/monitor.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python3
+"""
+monitor.py — Lightweight uptime / latency / SSL monitor.
+
+Standard-library only (urllib, ssl, socket, json, time). Runs an infinite
+loop, checks a target URL (and optionally a search endpoint) every
+CHECK_INTERVAL seconds, and persists a rolling RETENTION_HOURS window of
+results to a JSON file that the static dashboard reads.
+
+Configuration is via environment variables so the same image can monitor
+any site without a rebuild:
+
+ TARGET_URL Full URL to monitor (default: https://mithal.space)
+ SEARCH_PATH Path appended to origin for a (default: /search?q=test)
+ secondary "search" check. Set to "" to disable.
+ CHECK_INTERVAL Seconds between checks (default: 60)
+ RETENTION_HOURS How much history to keep (default: 24)
+ REQUEST_TIMEOUT Per-request timeout, seconds (default: 10)
+ DATA_FILE Where to write the JSON log (default: /app/web/data/metrics.json)
+"""
+
+import json
+import os
+import socket
+import ssl
+import sys
+import time
+import urllib.error
+import urllib.request
+from datetime import datetime, timezone
+from urllib.parse import urlparse
+
+# --------------------------------------------------------------------------
+# Configuration
+# --------------------------------------------------------------------------
+
+TARGET_URL = os.environ.get("TARGET_URL", "https://mithal.space")
+SEARCH_PATH = os.environ.get("SEARCH_PATH", "/search?q=test")
+CHECK_INTERVAL = int(os.environ.get("CHECK_INTERVAL", "60"))
+RETENTION_HOURS = float(os.environ.get("RETENTION_HOURS", "24"))
+REQUEST_TIMEOUT = float(os.environ.get("REQUEST_TIMEOUT", "10"))
+DATA_FILE = os.environ.get("DATA_FILE", "/app/web/data/metrics.json")
+USER_AGENT = "uptime-monitor/1.0 (+standard-library)"
+
+_parsed = urlparse(TARGET_URL)
+HOSTNAME = _parsed.hostname
+PORT = _parsed.port or (443 if _parsed.scheme == "https" else 80)
+SEARCH_URL = f"{_parsed.scheme}://{_parsed.netloc}{SEARCH_PATH}" if SEARCH_PATH else None
+
+
+def now_iso() -> str:
+ return datetime.now(timezone.utc).isoformat(timespec="seconds")
+
+
+def measure_dns(hostname: str):
+ """Return DNS resolution time in milliseconds, or None on failure."""
+ start = time.perf_counter()
+ try:
+ socket.getaddrinfo(hostname, None)
+ except socket.gaierror as exc:
+ return None, str(exc)
+ elapsed_ms = (time.perf_counter() - start) * 1000
+ return round(elapsed_ms, 2), None
+
+
+def timed_get(url: str, timeout: float):
+ """
+ Perform an HTTP GET and return (status_code, latency_ms, error_str).
+ latency_ms measures time-to-first-byte-of-full-response (connect + TLS
+ + request + response), matching what a real visitor experiences.
+ """
+ req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
+ start = time.perf_counter()
+ try:
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
+ resp.read(1) # confirm the body actually starts streaming
+ status = resp.status
+ latency_ms = (time.perf_counter() - start) * 1000
+ return status, round(latency_ms, 2), None
+ except urllib.error.HTTPError as exc:
+ # Still a "successful" connection from a monitoring standpoint —
+ # the server responded, just with an error status.
+ latency_ms = (time.perf_counter() - start) * 1000
+ return exc.code, round(latency_ms, 2), None
+ except (urllib.error.URLError, socket.timeout, TimeoutError) as exc:
+ latency_ms = (time.perf_counter() - start) * 1000
+ return None, round(latency_ms, 2), str(getattr(exc, "reason", exc))
+
+
+def measure_ssl_expiry(hostname: str, port: int, timeout: float):
+ """Return (days_remaining, error_str) for the TLS certificate."""
+ if not hostname:
+ return None, "no hostname"
+ try:
+ ctx = ssl.create_default_context()
+ with socket.create_connection((hostname, port), timeout=timeout) as sock:
+ with ctx.wrap_socket(sock, server_hostname=hostname) as ssock:
+ cert = ssock.getpeercert()
+ not_after = cert.get("notAfter")
+ if not not_after:
+ return None, "no notAfter field in certificate"
+ expiry_dt = datetime.strptime(not_after, "%b %d %H:%M:%S %Y %Z").replace(
+ tzinfo=timezone.utc
+ )
+ days_remaining = (expiry_dt - datetime.now(timezone.utc)).total_seconds() / 86400
+ return round(days_remaining, 1), None
+ except Exception as exc: # noqa: BLE001 — monitoring must never crash the loop
+ return None, str(exc)
+
+
+def run_check() -> dict:
+ dns_ms, dns_err = measure_dns(HOSTNAME)
+ status, latency_ms, http_err = timed_get(TARGET_URL, REQUEST_TIMEOUT)
+
+ search_status, search_latency_ms, search_err = None, None, None
+ if SEARCH_URL:
+ search_status, search_latency_ms, search_err = timed_get(SEARCH_URL, REQUEST_TIMEOUT)
+
+ ssl_days, ssl_err = (None, None)
+ if _parsed.scheme == "https":
+ ssl_days, ssl_err = measure_ssl_expiry(HOSTNAME, PORT, REQUEST_TIMEOUT)
+
+ success = status is not None and 200 <= status < 400
+
+ return {
+ "timestamp": now_iso(),
+ "target": TARGET_URL,
+ "http_status": status,
+ "success": success,
+ "latency_ms": latency_ms,
+ "dns_ms": dns_ms,
+ "search_status": search_status,
+ "search_latency_ms": search_latency_ms,
+ "ssl_days_remaining": ssl_days,
+ "error": http_err or dns_err or ssl_err or search_err,
+ }
+
+
+def load_history(path: str) -> list:
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ data = json.load(f)
+ return data.get("checks", []) if isinstance(data, dict) else data
+ except (FileNotFoundError, json.JSONDecodeError):
+ return []
+
+
+def prune(history: list, retention_hours: float) -> list:
+ cutoff = time.time() - retention_hours * 3600
+ pruned = []
+ for entry in history:
+ try:
+ ts = datetime.fromisoformat(entry["timestamp"]).timestamp()
+ except (KeyError, ValueError):
+ continue
+ if ts >= cutoff:
+ pruned.append(entry)
+ return pruned
+
+
+def save_history(path: str, history: list) -> None:
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ payload = {
+ "target": TARGET_URL,
+ "search_url": SEARCH_URL,
+ "updated_at": now_iso(),
+ "check_interval_seconds": CHECK_INTERVAL,
+ "retention_hours": RETENTION_HOURS,
+ "checks": history,
+ }
+ tmp_path = f"{path}.tmp"
+ with open(tmp_path, "w", encoding="utf-8") as f:
+ json.dump(payload, f, indent=2)
+ os.replace(tmp_path, path) # atomic write so the dashboard never reads a half-written file
+
+
+def main() -> None:
+ print(f"[monitor] target={TARGET_URL} interval={CHECK_INTERVAL}s "
+ f"retention={RETENTION_HOURS}h data_file={DATA_FILE}", flush=True)
+
+ history = load_history(DATA_FILE)
+
+ while True:
+ cycle_start = time.time()
+ result = run_check()
+ history.append(result)
+ history = prune(history, RETENTION_HOURS)
+ save_history(DATA_FILE, history)
+
+ status_str = result["http_status"] if result["http_status"] is not None else "ERR"
+ print(
+ f"[monitor] {result['timestamp']} status={status_str} "
+ f"latency={result['latency_ms']}ms dns={result['dns_ms']}ms "
+ f"ssl_days={result['ssl_days_remaining']} "
+ f"search_latency={result['search_latency_ms']}ms "
+ f"{'error=' + result['error'] if result['error'] else 'ok'}",
+ flush=True,
+ )
+
+ elapsed = time.time() - cycle_start
+ time.sleep(max(0, CHECK_INTERVAL - elapsed))
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except KeyboardInterrupt:
+ sys.exit(0)
\ No newline at end of file