Basic info

Damian Jankov
About me

B2B contractor  ·  Remote  ·  Open to EU/US

I'm Damián, a full stack engineer .NET and TypeScript systems, currently working on a global clinical study platform for a US client in a regulated environment. I have shipped production software across backend APIs, cloud infrastructure and modern React frontends. Outside of client work I build my own projects and tools that you can checkout on this website!

Education
Master's in Informatics2022 – 2024
Technical University Košice
  • Thesis: Application for generating test specifications and reports for various levels of ultrasound verification testing (grade A)
  • Functional Programming, Metaprogramming, Semantics, Logical Thinking, Parallel Programming
Bachelor of Computer Networking2019 – 2022
Technical University Košice
  • Thesis: Authenticated encryption mode OCB-AES (grade A)
  • Mathematical basics, telecommunications, computer networking and cryptography
Additional education & achievements
B1 Deutsches Sprachdiplom — Gymnázium Trebišovská 122019
3rd place — Živé IT projekty competition  (see here)2022

Job history

These are relevant long term projects I have been involved in.

My tech stack

Some technologies I work/worked with either professionally or in my free time.
Backend
Frontend
Cloud & devops
Databases
Tools & others

Personal projects

These are some of my personal projects that I have worked on in my free time. Please keep in mind all of these are fetched from my GitHub and rendered here
BobMcp

A remote Model Context Protocol server in C# that exposes tools to Claude over Streamable HTTP, persisting to Postgres through EF Core. Authentication is mandatory and production-shaped: it runs as an OAuth 2.1 resource server, validating JWTs issued by WorkOS AuthKit and enforcing an email allowlist on every single call. Built on the official MCP C# SDK and hosted behind public HTTPS so claude.ai can connect to it directly.

C# 97.8%, Dockerfile 2.2% · 11 commits · updated 17/07/2026 · View on GitHub

Bobi Facts MCP Server

A Model Context Protocol server (official C# SDK, ModelContextProtocol 2.0.0-preview.1) that stores facts about Bobi the cat. It runs as a Streamable HTTP server and persists to Postgres (Supabase) via EF Core.

Tools

ToolArgumentsDescription
get_factsReturn all facts about Bobi.
insert_factcontent: stringAdd a new fact; returns the created fact.
delete_factid: intDelete a fact by id; returns if it hit.

Layout

src/BobMcp.Server/
  Models/Fact.cs            EF Core entity
  Dtos/                     Wire DTOs (FactDto, DeleteResultDto)
  Data/BobiDbContext.cs     EF Core DbContext
  Data/DesignTimeDbContextFactory.cs   For `dotnet ef`
  Migrations/               EF Core migrations (Npgsql)
  Services/IFactService.cs  Injectable service abstraction
  Services/FactService.cs   EF Core implementation (IDbContextFactory)
  Tools/FactTools.cs        [McpServerToolType] — thin handlers, delegate to service
  Program.cs                Host + DI + HTTP MCP wiring
src/BobMcp.Client/          A real MCP client (HTTP) used to test the server

The tool layer holds no logic — every tool maps arguments to an injected IFactService call, which is the only place that touches the database.

Configuration

Env varRequiredDefault
BOBI_DB_CONNECTIONyes
BOBI_HTTP_URLnohttp://localhost:5111
AUTH_ISSUERyes
MCP_RESOURCEyes
ALLOWED_EMAILSyes

Authentication is mandatory (see Authentication): if AUTH_ISSUER, MCP_RESOURCE, or ALLOWED_EMAILS is missing the server throws on startup and will not run — there is no unauthenticated mode.

BOBI_DB_CONNECTION is an Npgsql key/value string (not a postgresql:// URI). For Supabase use the Session pooler with channel binding disabled:

Host=aws-1-eu-central-1.pooler.supabase.com;Port=5432;Username=postgres.<project-ref>;Password=<pw>;Database=postgres;SSL Mode=Require;Trust Server Certificate=true;Channel Binding=Disable

Channel Binding=Disable is required — Supavisor rejects Npgsql's default SCRAM channel binding and otherwise returns a misleading 28P01 password authentication failed.

Build & run

dotnet build BobMcp.slnx -c Release

BOBI_DB_CONNECTION="Host=...;Channel Binding=Disable" \
AUTH_ISSUER="https://your-app.authkit.app" \
MCP_RESOURCE="http://localhost:5111/mcp" \
ALLOWED_EMAILS="you@gmail.com" \
  dotnet src/BobMcp.Server/bin/Release/net10.0/BobMcp.Server.dll

The auth vars are required — the server throws on startup without them. See Authentication.

The MCP endpoint is http://localhost:5111/mcp. The server applies EF Core migrations on startup.

Test it (acts as an MCP client)

dotnet src/BobMcp.Client/bin/Release/net10.0/BobMcp.Client.dll          # localhost:5111
dotnet src/BobMcp.Client/bin/Release/net10.0/BobMcp.Client.dll https://host/mcp

Or with curl:

curl -i -X POST http://localhost:5111/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

Migrations

dotnet ef migrations add <Name> --project src/BobMcp.Server

Authentication

The server is an OAuth 2.1 resource server, and auth is required — it refuses to start unless AUTH_ISSUER, MCP_RESOURCE, and ALLOWED_EMAILS are all set. It never shows a login page itself — it validates a JWT access token on every /mcp call and only lets through tokens whose email claim is in ALLOWED_EMAILS. The actual "sign in with Google" flow is run by WorkOS AuthKit, which acts as the OAuth authorization server. claude.ai (the MCP client) drives the whole flow: it discovers AuthKit from /.well-known/oauth-protected-resource, registers itself (DCR), sends you to Google to log in, then retries with Authorization: Bearer <token>.

claude.ai ──initialize──▶ /mcp ──401 + WWW-Authenticate──▶ claude.ai
claude.ai ──discovers──▶ WorkOS AuthKit ──Google login──▶ access token (aud = MCP_RESOURCE)
claude.ai ──Bearer token──▶ /mcp ──validate JWT + email allowlist──▶ tools

Why AuthKit and not Google directly: Google's OAuth doesn't support Dynamic Client Registration and won't scope tokens to this server, both of which MCP clients need.

The wiring lives in Auth/AuthenticationExtensions.cs: AddJwtBearer validates the token, .AddMcp(...) serves the resource metadata, and an authorization policy enforces the email allowlist.

One-time WorkOS AuthKit setup

  1. Create a WorkOS account and an AuthKit application (free tier is enough).
  2. Enable Google login: WorkOS Dashboard → Authentication → add Google OAuth as a sign-in method.
  3. Surface the email claim: Authentication → Sessions → Configure JWT Template, add "email": "{{ user.email }}" so the access token carries the email claim the allowlist checks.
  4. Enable Dynamic Client Registration: Applications / OAuth → turn on DCR (lets claude.ai register itself).
  5. Add a Resource Indicator equal to your public MCP URL, e.g. https://<host>/mcp. This becomes the token's aud. (Without it AuthKit uses a default environment-scoped aud, which won't match MCP_RESOURCE.)
  6. Copy your AuthKit domain (e.g. https://your-app.authkit.app) — that's AUTH_ISSUER.

Env vars

AUTH_ISSUER=https://your-app.authkit.app   # AuthKit domain (the OAuth issuer)
MCP_RESOURCE=https://<host>/mcp            # must match the AuthKit Resource Indicator + the URL you add in claude.ai
ALLOWED_EMAILS=you@gmail.com               # comma-separated allowlist

Hosting for claude.ai

claude.ai connects to remote MCP servers over Streamable HTTP. To expose this one:

  1. Run it pointed at your Postgres/Supabase DB, with the Authentication env vars set.
  2. Put it behind public HTTPS (reverse proxy or a tunnel such as Cloudflare Tunnel / ngrok) — claude.ai requires https://. Make MCP_RESOURCE match this public /mcp URL.
  3. In claude.ai → Settings → Connectors, add https://<host>/mcp. claude.ai will walk you through the Google sign-in on first connect.
RAG

A Retrieval-Augmented Generation pipeline written from scratch in C# — no LangChain, no hosted AI API. It extracts text from PDFs, splits it into overlapping chunks, generates embeddings with a local Ollama/Phi3 model, and stores them in a Qdrant vector database for semantic search. Implements the full RAG loop end to end: ingestion, vectorization, similarity retrieval, context augmentation, and answer generation.

C# 96.6%, Shell 3.4% · 25 commits · updated 19/02/2026 · View on GitHub

RAG (Retrieval-Augmented Generation) - C# Implementation

A C# implementation of a Retrieval-Augmented Generation (RAG) system that processes PDF documents and enables intelligent question-answering using local AI models.

Overview

This RAG implementation allows you to ask questions about PDF documents by combining semantic search with large language models. The system extracts text from PDFs, converts it into semantic vectors, stores them in a vector database, and retrieves relevant context to generate accurate answers.

Architecture

The system uses the following components:

  • Qdrant: Vector database for storing and searching semantic embeddings
  • Ollama with Phi3 model: Local LLM for generating embeddings and completions
  • PdfPig: PDF text extraction library
  • .NET 9.0: Runtime environment

How It Works

  1. PDF Processing: Extract text content from PDF documents
  2. Text Chunking: Split the text into manageable chunks
  3. Overlay Chunking: Create overlapping chunks to maintain context continuity
  4. Semantic Vectorization: Convert each chunk into semantic vectors using Ollama's Phi3 model
  5. Vector Storage: Store all vectors in Qdrant vector database
  6. Query Processing: Convert user queries into semantic vectors
  7. Similarity Search: Use Qdrant to find the most relevant chunks via semantic search
  8. Context Augmentation: Compose a final prompt with retrieved context
  9. Answer Generation: Generate accurate answers using the LLM with relevant context

Prerequisites

  • Docker with GPU passthrough support enabled
  • NVIDIA GPU (required for Ollama to run efficiently)
  • .NET 9.0 SDK or later

Docker GPU Setup

Ensure Docker has access to your GPU. You need:

  • NVIDIA Container Toolkit installed
  • Docker configured with --gpus=all flag support
DjOrm

A lightweight ORM for .NET built from the ground up, mapping annotated C# classes onto PostgreSQL tables. It generates schema and junction tables from entity definitions, and walks LINQ expression trees to translate lambdas into parameterized SQL WHERE clauses. It also resolves related entities recursively through their junction tables — the parts of EF Core most people treat as magic, implemented by hand.

C# 100.0% · 46 commits · updated 16/04/2026 · View on GitHub

DjOrm

A lightweight ORM for .NET that maps C# classes to PostgreSQL tables using custom attributes and a generic DbContext<T>.

Features

  • Auto-generates CREATE TABLE SQL from annotated C# entities
  • Junction table generation for entity relationships
  • Full CRUD operations via a single generic context
  • Translates simple LINQ expressions into SQL WHERE clauses
  • Recursive fetching of related objects via junction tables
  • Async throughout

Usage

1. Annotate your entities

[TableAttribute]
public class CarEntity
{
    [PrimaryKeyAttribute]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Make { get; set; }

    [SecondaryKeyAttribute]
    public DriverEntity? Driver { get; set; }
}

2. Create tables and run queries

var db = new DatabaseConnector(connString);

// Auto-create tables from entity definitions
var entities = new TableEntitiesMaker(new TypeTranslator()).CreateObjectEntities();
await db.ExecuteCommands(new SqlCreateTablesTranslator(entities).TranslateEntitiesToCreateTables());

// CRUD
IDbContext<CarEntity> ctx = new DbContext<CarEntity>(db);

await ctx.InsertData(new CarEntity("Civic", "Honda", driver));
var all    = await ctx.GetData();
var hondas = await ctx.GetDataBy(x => x.Make == "Honda" && x.Name != "Accord");
await ctx.UpdateData(car);
await ctx.DeleteData(car);

LINQ expression supportGetDataBy walks the expression tree and translates binary expressions (==, !=, <, <=, >, >=, &&, ||) directly into parameterized SQL conditions.

3. Recursive fetching of related objects

Pass recursive: true (or isRecursive: true on DbContext) to automatically load related entities via their junction tables:

// Fetch all articles and populate their related TagEntity objects
var articles = await ctx.GetData(isRecursive: true);

// Same with a filter
var articles = await ctx.GetDataBy(x => x.Title == "Hello", isRecursive: true);

For each fetched object, DjOrm:

  1. Queries the junction table ({ParentType}{RelatedType}) using the parent's primary key.
  2. Looks up the related entity by its primary key.
  3. Assigns the result to the [SecondaryKey]-annotated property.

This works recursively — related objects that themselves have [SecondaryKey] properties are also populated.

Configuration

Connection string is loaded from a .env file:

HOST=localhost
PORT=5432
USERNAME=postgres
PASSWORD=postgres
DATABASE=testdb
RSTracker

A production .NET 9 and React 19 platform for football coaching staff, tracking player RPE and wellness to compute training load and the Acute:Chronic Workload Ratio used to flag injury risk. Ships with Microsoft Entra ID authentication, Azure Key Vault and Blob Storage, and integration tests that run against real PostgreSQL instances via Testcontainers. Every push to main runs the suite, builds both Docker images, and deploys to Azure Container Apps through GitHub Actions.

JavaScript 57.7%, C# 40.5%, CSS 0.8% · 162 commits · updated 18/02/2026 · View on GitHub

RSTracker

RSTracker helps coaching staff make data-driven decisions about player load management. By tracking daily RPE (Rating of Perceived Exertion) and wellness scores across a squad, the application calculates key performance indicators such as training load volume, intensity, and the Acute:Chronic Workload Ratio (ACWR) -- a widely recognized metric for assessing injury risk.

The platform is designed around the concept of league weeks (7-day windows), allowing staff to review and compare data on a week-by-week basis aligned with the competitive schedule.


Key Features

Player Management

  • Maintain a full squad roster with player profiles (name, age, position, weight, height)
  • Add and remove players with cascading data cleanup

RPE Tracking

  • Record daily session RPE values and training duration per player
  • Automatic calculation of session training load (RPE x duration)
  • Daily team averages, volume percentages, and intensity metrics
  • Comparison against built-in reference norms for each day of the week

Wellness Monitoring

  • Track four daily wellness dimensions per player: Muscle Status, Recovery Status, Stress, and Sleep
  • Each dimension scored on a 1-7 scale (total wellness score out of 28)
  • Mid-week recovery assessment via Wednesday-Thursday-Friday averages

ACWR Analysis

  • Acute:Chronic Workload Ratio calculation over configurable time windows
  • Multi-week trend visualization to identify periods of elevated injury risk
  • Combined volume and intensity breakdown per week

Data Visualization

  • Bar charts for daily wellness and RPE summaries
  • Combo charts comparing actual RPE loads against daily reference norms
  • Stacked bar charts for weekly RPE breakdowns across the ACWR period
  • ACWR ratio trend charts spanning multiple weeks

Weekly Data Views

  • Day-by-day tables showing individual player values alongside team averages
  • Week summary tables with aggregated statistics
  • Date picker navigation to browse historical data by league week

Screenshots

RPE Bar Chart RPE Bar Chart -- daily training load visualization

RPE Norm Chart RPE Norm Chart -- actual loads compared against daily reference norms

Wellness Chart Wellness Chart -- daily wellness score overview

ACWR Analysis ACWR Analysis -- workload ratio trends across multiple weeks

Player Management Player Management -- squad roster administration

Player Data RPE Wellness Management Player Data, RPE, Wellness and Management -- combined data entry view


Sports Science Background

RPE (Rating of Perceived Exertion)

RPE is a subjective measure of how hard a player perceives a training session to be. When combined with session duration, it produces the session RPE or total training load:

Session RPE=RPE Value×Duration (minutes)\text{Session RPE} = \text{RPE Value} \times \text{Duration (minutes)}

Volume and Intensity

  • Volume is expressed as a percentage of the maximum expected daily load (baseline of 760):

Volume=Total RPE760×100\text{Volume} = \frac{\text{Total RPE}}{760} \times 100

  • Intensity normalizes volume by time to account for differences in session length:

Intensity=VolumeCommon Time/95\text{Intensity} = \frac{\text{Volume}}{\text{Common Time} / 95}

Wellness Scoring

Players self-report four metrics daily, each on a 1-7 scale:

MetricDescription
Muscle StatusPerceived muscle soreness and readiness
Recovery StatusGeneral sense of physical recovery
StressMental and emotional stress levels
SleepSleep quality and duration

The total wellness score ranges from 4 to 28. The platform also calculates a Wednesday-Thursday-Friday average, commonly used to assess recovery state heading into match day.

ACWR (Acute:Chronic Workload Ratio)

The ACWR compares the current week's training load against the rolling average of the previous four weeks:

ACWR=Current Week LoadAverage of Previous 4 WeeksACWR = \frac{\text{Current Week Load}}{\text{Average of Previous 4 Weeks}}

ACWR RangeInterpretation
Below 0.8Undertraining -- potential detraining risk
0.8 - 1.3Safe zone -- optimal load management
1.3 - 1.5Caution -- elevated injury risk
Above 1.5Danger zone -- high injury risk

Daily Reference Norms

The system includes built-in daily RPE norms for a typical training week to compare against actual loads:

DayMonTueWedThuFriSatSun
Reference RPE3006005801102207600

Technology Stack

Frontend

TechnologyPurpose
React 19UI framework
ViteBuild tool and dev server
React Router DOMClient-side routing
React Bootstrap / Bootstrap 5UI component library and styling
Chart.js + react-chartjs-2Data visualization and charting
chartjs-plugin-datalabelsChart label overlays
MSAL React + MSAL BrowserMicrosoft Entra ID authentication
date-fnsDate manipulation and formatting
react-datepickerDate selection component
react-selectEnhanced dropdown selects
react-toastifyToast notifications
react-spinnersLoading indicators

Backend

TechnologyPurpose
.NET 9 / ASP.NET CoreWeb API framework
Entity Framework Core 9ORM and database migrations
PostgreSQL (via Npgsql)Relational database
JWT Bearer AuthenticationAPI security
Swashbuckle / SwaggerAPI documentation
Newtonsoft.JsonJSON serialization
Azure Key VaultSecrets management
Azure Blob StorageStructured operation logging
In-Memory CacheResponse caching with TTL-based invalidation

Infrastructure

TechnologyPurpose
DockerContainerization with multi-stage builds
NginxFrontend reverse proxy and SPA routing
Azure Container AppsCloud hosting
Azure Key VaultCentralized secrets management
Azure Blob StorageAppend-style daily log files

Testing

TechnologyPurpose
xUnitTest framework
TestcontainersIntegration testing with real PostgreSQL instances

CI/CD

The project uses GitHub Actions for continuous integration and deployment. A workflow is triggered on every push to the main branch and performs the following steps:

  1. Test -- restores .NET dependencies, pulls a PostgreSQL image, and runs the full xUnit test suite (including Testcontainers-based integration tests)
  2. Build -- builds Docker images for both the backend (ASP.NET Core API) and frontend (React SPA served by Nginx), tagged with the short commit hash
  3. Push -- pushes both images to Docker Hub
  4. Deploy -- authenticates with Azure and updates both Azure Container Apps (backend and frontend) to use the newly built images

This ensures that every merge to main is automatically tested, containerized, and deployed to production.


Authentication

RSTracker uses Microsoft Entra ID (formerly Azure Active Directory) for authentication.

  • Users log in through a browser redirect flow via the Microsoft identity platform
  • The frontend acquires an access token using MSAL (Microsoft Authentication Library) with the configured API scope
  • Every API request includes the token in the Authorization: Bearer {token} header
  • The backend validates the JWT against the configured Azure AD tenant and audience
  • All API endpoints are protected -- unauthenticated requests receive a 401 response

Testing

The test suite uses xUnit with Testcontainers to spin up real PostgreSQL instances during integration tests, ensuring database operations are tested against actual database behavior rather than mocks.

Test coverage includes:

Test FileCoverage
PlayerHelperTestsPlayer CRUD operations and data retrieval
RPEManagerTestsRPE calculations, weekly aggregation, volume, and intensity
WelnessManagerTestsWellness scoring, weekly summaries, and mid-week averages

Azure Blob Storage logging is mocked during tests to isolate database-focused assertions.


License

All rights reserved.

Intermezzo Staff

A staff management and time-tracking app built for a real cafe in my home city — it has actual daily users, not just a demo deployment. Next.js 16 with Google OAuth and MongoDB Atlas, using role-based access so standard staff can only edit their own recent entries while admins see every employee and the full wallet history. Covers shift scheduling, daily work logs, and business cash-flow tracking.

TypeScript 89.9%, CSS 8.6%, JavaScript 1.4% · 37 commits · updated 09/03/2026 · View on GitHub

Intermezzo Staff

A staff management and time-tracking web app for small businesses — built to track daily work inputs, cash flow, and shift schedules. Built for a small cafe in my home city.


Tech Stack

LayerTechnology
FrameworkNext.js 16 (App Router) + React 19 + TypeScript
AuthNextAuth v5 — Google OAuth
DatabaseMongoDB (Atlas)
UIReact Bootstrap 5 + Custom CSS Modules
CalendarFullCalendar (day/week/month views)
Utilitiesdate-fns, react-spinners
HostingVercel-ready (Next.js server actions)

Role-Based Access

Access is controlled via environment-level email whitelists:

  • Standard users — can view and edit their own data only; edit window limited to current month + last 2 days
  • Admin users — elevated privileges: view all employees' data, filter by employee, access full wallet history

Authentication is handled through Google OAuth — only whitelisted emails can log in.


Pages

Wallet /wallet

Tracks the business cash balance. Users can submit balance updates with a timestamp. Admins see the full history of all updates (shown in the História accordion); standard users see the current balance only.

Wallet page


My Inputs /myinputs

Daily work log table. Each entry records hours worked, start/end times (auto-calculates hours), cash and terminal turnover, day expenses, and start/end float amounts. Filterable by month. Admins can view entries for all staff. Older entries outside the edit window are masked with ****.

My Inputs page


Timetable /timetable

A color-coded shift calendar showing staff schedules. Powered by FullCalendar with day, week, and month views. Each employee is assigned a distinct color.

Timetable page


ChoreMaster

A household chore delegation app pairing a .NET 9 API with a React and TypeScript frontend over PostgreSQL and Entity Framework. The Azure infrastructure is provisioned with Terraform, secured with JWT and Google OAuth, and covered by xUnit and Testcontainers integration tests running in GitHub Actions.

TypeScript 49.9%, C# 42.7%, HCL 4.7% · 77 commits · updated 19/02/2026 · View on GitHub

ChoreMaster

A Simple Household Task Management System
Transform your chaotic chore routine into an organized experience!


Technologies Used

  • Frontend: React 18 + TypeScript
  • Styling: Bootstrap
  • Backend: .NET 9
  • Database: PostgreSQL with Entity Framework ORM
  • Authentication: JWT + OAuth2 (Google)
  • Deployment: Azure + Terraform
  • Testing: xUnit + .NET Testcontainers
  • CI/CD Automation: GitHub Actions

Core Features

  • Create, edit, and delete chores
  • Manage users (create/edit/delete)
  • Delegate chores to specific users
  • Mark tasks as completed and reassign them to a user
  • View full task history
  • Configure custom task time thresholds
  • Display relations and time left for each task

Overview of the App

ChoreMaster Dashboard

Setting It Up Locally

Backend

Prerequisites

  • .NET 9 SDK
  • Entity Framework CLI tools
  • Docker and Docker Compose

Setup Steps

  1. Start PostgreSQL database:

    docker-compose up -d
    
  2. Create environment file: Create a .env file in the backend directory with your connection string:

    DATABASE_CONNECTION_STRING=your_connection_string_here
    
  3. Run Entity Framework migrations:

    dotnet ef database update
    
  4. Start the backend:

    dotnet build
    dotnet run
    

Frontend

Prerequisites

  • Node.js (npm version 10.8.2 or higher)

Setup Steps

  1. Install dependencies:

    npm install
    
  2. Start the development server:

    npm run dev
    

    The app automatically uses http://localhost:5272/api for the backend in development.


Production Deployment

Prerequisites

  • Azure CLI installed and configured
  • Terraform installed
  • Docker Hub account
  • Azure subscription

GitHub Secrets Configuration

Before deploying to production, configure the following secrets in your GitHub repository (Settings → Secrets and variables → Actions):

Secret NameDescription
DOCKERHUB_USERNAMEYour Docker Hub username
DOCKERHUB_TOKENYour Docker Hub access token
AZURE_CREDENTIALSAzure service principal credentials (JSON format)
PRODUCTION_API_URLYour production API URL (e.g., https://api.yourapp.com)

Deployment Steps

  1. Login to Azure:

    az login
    
  2. Navigate to the infrastructure directory:

    cd infrastructure
    
  3. Initialize Terraform:

    terraform init
    
  4. Review the deployment plan:

    terraform plan
    
  5. Apply the infrastructure:

    terraform apply
    
  6. Push to main branch:

    Once the infrastructure is set up, pushing to the main branch will trigger the CI/CD pipeline that:

    • Runs backend tests
    • Builds Docker images for frontend and backend
    • Pushes images to Docker Hub
    • Updates Azure Container Apps with the new images

Life Organizer

A personal productivity app on Next.js and MongoDB with Google OAuth, covering daily tasks, work reports, workouts, and notes. Includes a built-in Pomodoro timer and rich-text editors throughout, deployed on Vercel with Atlas.

TypeScript 92.1%, CSS 4.6%, JavaScript 3.2% · 89 commits · updated 19/07/2026 · View on GitHub

Life Organizer

A personal productivity application built with Next.js 14, featuring task management, work reports, and workout tracking. The app uses Google OAuth for authentication and MongoDB for data persistence.

Tech Stack

  • Framework: Next.js 14 (App Router)
  • Authentication: NextAuth.js with Google OAuth
  • Database: MongoDB
  • UI: React Bootstrap
  • Language: TypeScript
  • Deployment: Vercel free tier + Atlas

Home (/)

  • Landing page showing welcome message for authenticated users
  • Displays access denied message for unauthenticated users

Tasks (/tasks)

Tasks Page

  • Daily task management system
  • Date-based task organization
  • Create, update, and toggle task completion
  • Tasks persist per date in MongoDB

Work Reports (/workreports)

Work Reports Page

  • Daily work report editor
  • Rich text editor with formatting options
  • Date-based report management
  • Integrated Pomodoro timer for time management
  • Create and update reports for specific dates

Workouts (/workouts)

Workouts Page

  • Workout tracking and management
  • Built-in stopwatch timer
  • Rich text editor for workout details
  • Add, edit, and delete workout entries
  • View workout history

Notes (/notes)

Workouts Page

  • Note tracking and management
  • Rich text editor for note details
  • Add, edit, and delete note entries
AES

The AES-128 block cipher implemented from scratch in C#, validated against the official NIST SP 800-38A and FIPS 197 test vectors. Benchmarked with BenchmarkDotNet against .NET's native implementation to quantify the cost of a naive approach — roughly 5x slower on encryption, 9x on decryption. Written to understand the algorithm properly; explicitly not for production use.

C# 100.0% · 23 commits · updated 19/02/2026 · View on GitHub

AES Implementation in C#

A simple, educational implementation of the Advanced Encryption Standard (AES) algorithm in C#. This project demonstrates the core AES encryption and decryption processes with a focus on clarity and learning.

Educational Purpose Only: This implementation is intended for learning and educational purposes. Do NOT use this in production environments or for securing sensitive data.

Quick Start

Prerequisites

Supported Features

  • ✅ AES-128 encryption and decryption
  • ✅ Single block operations (16 bytes)
  • ✅ Row-based state representation
  • ✅ NIST SP 800-38A test vector compliance

Limitations

  • ❌ Only supports 128-bit keys
  • ❌ Single block only (no chaining modes like ECB, CBC)
  • ❌ No initialization vectors (IV)
  • ❌ Not optimized for production use

Performance Benchmarks

Latest benchmark results on Ubuntu 24.04.3 LTS with Intel Core i5-12450H:

MethodMeanErrorStdDevRatio
Custom AES Encrypt4,048.8 ns74.34 ns62.07 ns~4.9x
Custom AES Decrypt7,394.6 ns31.49 ns29.46 ns~8.7x
.NET AES Encrypt832.0 ns3.82 ns3.58 ns1.0x
.NET AES Decrypt847.2 ns5.56 ns4.64 ns1.0x

Testing

The implementation is thoroughly tested against NIST Special Publication 800-38A and FIPS 197 test vectors:

dotnet test --verbosity normal

All tests validate:

  • Correct encryption of known plaintext/key pairs
  • Proper decryption back to original plaintext
  • Compliance with NIST standards

Learning Resources

License

This project is for educational purposes only. Please refer to the license file for details.

Contributing

This is an educational project. Feel free to fork and experiment, but remember this is not intended for production use.

Mandelbrot

A desktop Mandelbrot set explorer built with Avalonia UI on .NET 9, rendering the fractal to a 1000x1000 canvas. Supports keyboard pan and zoom, with an adaptive iteration count that scales as you magnify so detail stays sharp at depth.

C# 100.0% · 8 commits · updated 17/02/2026 · View on GitHub

Mandelbrot Set Visualizer

A desktop application for rendering and exploring the Mandelbrot set, built with C# and Avalonia UI targeting .NET 9.

Mandelbrot Set Preview

About

The Mandelbrot set is one of the most famous fractals in mathematics. A point cc in the complex plane belongs to the set if the sequence defined by:

zn+1=zn2+c,z0=0z_{n+1} = z_n^2 + c, \quad z_0 = 0

remains bounded (does not diverge to infinity). This application renders the set on a 1000×1000 pixel canvas, coloring points that belong to the set in red.

Features

  • Real-time rendering of the Mandelbrot set
  • Pan navigation using arrow keys to explore different regions
  • Zoom in by pressing Space to increase magnification
  • Adaptive iteration count — the maximum number of iterations automatically increases as you zoom deeper, improving detail at higher magnifications

Controls

KeyAction
Arrow UpPan up
Arrow DownPan down
Arrow LeftPan left
Arrow RightPan right
SpaceZoom in

Performance Note

You can scroll (zoom) into the set to explore its infinitely complex boundary, but be aware that performance degrades significantly after a few zoom levels. The rendering is not heavily optimized — each zoom step requires recalculating every pixel on the canvas, and as the iteration count increases with deeper zoom levels, the computation becomes substantially more expensive. This is a known limitation of the current implementation and would require significant optimization (e.g., multithreading, GPU acceleration, or perturbation theory) to handle deep zooms smoothly.

Getting Started

Prerequisites

Run

cd MandelBrot/MandelbrotApp
dotnet run

Tech Stack

  • C# / .NET 9
  • Avalonia UI 11.3 — cross-platform UI framework
  • WriteableBitmap — pixel-level rendering
PriceChecker

A small .NET utility that watches product pages and emails you when one hits your target price. Items are declared in XML with a URL, a price goal, and an extraction regex; the tool polls on a schedule and sends comparison summaries over Mailjet SMTP.

C# 100.0% · 11 commits · updated 19/02/2026 · View on GitHub

PriceChecker

A .NET tool that monitors product prices on the web and notifies you by email when your target price is hit.

How It Works

  1. Define items to track in InputConfig.xml — each entry has a URL, a target price, and a regex to extract the current price.
  2. The tool fetches each page, parses the price, and compares it to your goal.
  3. Every 30 minutes, it sends an email summary via Mailjet SMTP showing actual vs. target prices and whether each goal was reached.

Configuration

<root>
    <Recipient>you@example.com</Recipient>
    <ItemWebResource>
        <url>https://example.com/product</url>
        <priceGoal>49.99</priceGoal>
        <regexPricePattern>price-regex</regexPricePattern>
    </ItemWebResource>
</root>

Setup

Set your Mailjet SMTP keys as environment variables:

PUBLIC_KEY_PARSERTOOL=<your-public-key>
PRIVATE_KEY_PARSERTOOL=<your-private-key>

Then run:

dotnet run --project PriceChecker
CATBOT2

A Facebook Messenger bot running as an Azure HTTP-triggered Function, handling Meta webhook verification and inbound message events. Anyone who messages the page gets a random cat photo back from TheCatAPI. Deliberately silly, but a clean end-to-end example of serverless webhook plumbing.

C# 100.0% · 10 commits · updated 17/02/2026 · View on GitHub

CATBOT Azure HTTP Function with Meta Webhook

This Azure Function project implements a Facebook Messenger bot that automatically sends cute cat photos to users who message your Facebook page. The bot uses Azure HTTP-triggered functions to handle Meta webhooks and respond with adorable cat images.

How It Works

  1. Users send messages to your Facebook page via Messenger
  2. Facebook sends webhook events to your Azure Function endpoint
  3. The function processes the incoming message
  4. Bot responds automatically with a cute cat photo utilizing free cat api https://developers.thecatapi.com/

Architecture

  • HTTP Trigger Function: The azure function receives POST requests from Facebook's webhook system
  • Webhook Verification: Validates incoming requests using Facebook's verification token
  • Message Processing: Parses incoming message events and user data
  • Cat Photo API Integration: Fetches random cat images from external API
  • Messenger Send API: Sends cat photos back to users via Facebook's Graph API

Setup Instructions

Prerequisites

  • Azure subscription with Function App deployed
  • Facebook Developer Account
  • Facebook Page for the bot

Facebook Developer Setup

  1. Create Facebook App: Visit Facebook Developers and create a new app
  2. Configure Webhooks: Follow the official Facebook Graph API Webhooks Documentation
  3. Set Webhook URL: Point to your Azure Function HTTP endpoint
  4. Configure Page Access Token: Generate token for your Facebook page
  5. Subscribe to Events: Enable messages and messaging_postbacks events

Azure Function Configuration

  1. Deploy Function: Deploy this project to your Azure Function App

  2. Environment Variables: Configure the following app settings:

    • META_PAT: Page access token from Facebook
    • META_APP_SECRET: Your webhook verification token (the secret)
  3. HTTP Endpoint: Note your function's HTTP trigger URL for webhook configuration

Learn More

Contact me

Have a question or want to get in touch? Fill in the form below and your Gmail email client will open with the message ready to send.