diff --git a/.sphinx/_static/custom.css b/.sphinx/_static/custom.css
index 65f6b56..9eca16f 100644
--- a/.sphinx/_static/custom.css
+++ b/.sphinx/_static/custom.css
@@ -150,6 +150,21 @@ code.literal {
line-height: 1.5;
font-size: 0.9rem;
overflow-x: auto;
+ white-space: pre !important;
+}
+
+/* Ensure code blocks preserve whitespace */
+pre, code.highlight pre, div.highlight pre {
+ white-space: pre !important;
+}
+
+/* Specific override for console/text code blocks */
+.highlight-console pre,
+.highlight-text pre,
+.highlight-none pre {
+ white-space: pre !important;
+ font-family: 'Courier New', Courier, monospace !important;
+ tab-size: 4;
}
/** Enhanced image styling **/
diff --git a/.sphinx/_static/ngkore-logo.png b/.sphinx/_static/ngkore-logo.png
index 13091e4..dfbcba2 100644
Binary files a/.sphinx/_static/ngkore-logo.png and b/.sphinx/_static/ngkore-logo.png differ
diff --git a/ai-ml/ai-code-is-going-to-kill-your-startup.md b/ai-ml/ai-code-is-going-to-kill-your-startup.md
index c42665b..1f08b42 100644
--- a/ai-ml/ai-code-is-going-to-kill-your-startup.md
+++ b/ai-ml/ai-code-is-going-to-kill-your-startup.md
@@ -4,9 +4,8 @@
**Published:** Nov 15, 2025
-*What happened when I watched the smartest security engineers in the world realize they don’t know what to do about AI-generated code*
+*What happened when I watched the smartest security engineers in the world realize they don't know what to do about AI-generated code*
----

In a conference room in Prague sat some of the most experienced security engineers in the world — people who maintain OpenSSL, the people whose code encrypts your bank transactions, your medical records, and every password you’ve ever typed. They’ve seen every nightmare scenario: buffer overflows that crashed servers worldwide, Heartbleed, and timing attacks so subtle they took years to discover. These people have forgotten more about security than most of us will ever learn, and in 2025 they were genuinely unsure whether AI-generated code should ever touch their codebase.
@@ -250,5 +249,4 @@ You can ship features faster with AI. Prototype more quickly. Explore more ideas
* Can LLMs Generate Correct and Secure Backends? (Vero et al., 2025): [arXiv 2502.11844](https://arxiv.org/abs/2502.11844)
* Prompting Techniques for Secure Code Generation (Tony et al.): [arXiv 2407.07064](https://arxiv.org/abs/2407.07064)
----
> *Now go fix your security practices before AI breaks them for you.*
diff --git a/ai-ml/devops/ai-automation-in-aws-with-mcp.md b/ai-ml/devops/ai-automation-in-aws-with-mcp.md
index 9377ec3..d2097e8 100644
--- a/ai-ml/devops/ai-automation-in-aws-with-mcp.md
+++ b/ai-ml/devops/ai-automation-in-aws-with-mcp.md
@@ -26,13 +26,13 @@ This architecture means you can give Qwen a high-level goal in natural language,
The first step was to configure Qwen to use our three AWS-focused MCP servers. I started by checking the current setup.
-```shell
+```bash
qwen mcp list
```
As expected, it returned “No MCP servers configured.” I then applied a pre-configured settings file to connect Qwen to the servers.
-```shell
+```bash
mkdir -p /root/.qwen
cp /root/settings.json /root/.qwen/settings.json
```
diff --git a/ai-ml/devops/ai-incident-commander.md b/ai-ml/devops/ai-incident-commander.md
index 2c2c4b7..51e90a6 100644
--- a/ai-ml/devops/ai-incident-commander.md
+++ b/ai-ml/devops/ai-incident-commander.md
@@ -27,7 +27,7 @@ This structure allowed me to manage the incident strategically, focusing on the
Before diving into the production fire, the first step was to ensure my AI team was online and ready. I ran a quick check to list the installed agents and verify the connection to our Plane ticketing system.
-```shell
+```bash
# Verify available agents
qwen --prompt "List installed agents" 2>/dev/null
# Test Plane MCP Integration
@@ -42,7 +42,7 @@ The system confirmed three agents were active (cloud-architect, kubernetes-speci
The most critical issue was the application downtime caused by the crashing pods. I navigated to the relevant directory and delegated the problem to our Kubernetes expert.
-```shell
+```bash
cd /root/k8s-incident
qwen -y 2>/dev/null
```
@@ -82,7 +82,7 @@ Think of Terraform code as the official architectural blueprint for a house. Dri
This is precisely the problem our cloud-architect agent was designed to solve: to programmatically detect this drift, report on it, and bring our infrastructure back into alignment with our code.
-```shell
+```bash
cd /root/terraform-static-site
qwen -y 2>/dev/null
```
diff --git a/ai-ml/devops/building-virtual-devops-team-with-qwen-subagents.md b/ai-ml/devops/building-virtual-devops-team-with-qwen-subagents.md
index 48b5a35..9f27cb2 100644
--- a/ai-ml/devops/building-virtual-devops-team-with-qwen-subagents.md
+++ b/ai-ml/devops/building-virtual-devops-team-with-qwen-subagents.md
@@ -31,7 +31,7 @@ This means we are not building a complex application or spinning up a new contai
The first step was to “hire” my new team members by installing their agent files. A simple setup script handled the installation
-```shell
+```bash
bash /root/setup-agents.sh
```
@@ -74,7 +74,7 @@ The new Dockerfile used a multi-stage build, switched to a slim Python base imag
With the docker-optimized agent's work complete, it was time for the moment of truth. I built the new, optimized Docker image using the agent-generated Dockerfile.optimized
-```shell
+```bash
docker build -f /root/production-issues/bad-docker/Dockerfile.optimized
-t my-app:optimized /root/production-issues/bad-docker/
Press enter or click to view image in full size
@@ -87,7 +87,7 @@ The build completed successfully, installing only the necessary dependencies and
When I checked the final image size:
-```shell
+```bash
docker images | grep my-app
```
diff --git a/ai-ml/k2-think-deployment-via-sglang.md b/ai-ml/k2-think-deployment-via-sglang.md
index a47a04b..2849582 100644
--- a/ai-ml/k2-think-deployment-via-sglang.md
+++ b/ai-ml/k2-think-deployment-via-sglang.md
@@ -18,7 +18,7 @@ Let’s start with the prerequisites:
- A K8s cluster installed with Nvidia GPU Operator
-```bash
+```console
NAMESPACE NAME READY STATUS RESTARTS AGE
cert-manager cert-manager-5969544f77-pmrt7 1/1 Running 0 9d
cert-manager cert-manager-cainjector-65967ff5cc-tbntm 1/1 Running 0 9d
@@ -113,7 +113,7 @@ prometheus prometheus-prometheus-node-exporter-j4tjb
- Storage class configured
-```bash
+```console
kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer true 13d
diff --git a/ai-ml/openai-120b-model-deployment.md b/ai-ml/openai-120b-model-deployment.md
index 7f00ee3..a21ab75 100644
--- a/ai-ml/openai-120b-model-deployment.md
+++ b/ai-ml/openai-120b-model-deployment.md
@@ -21,7 +21,7 @@ This guide will walk you through the deployment of OpenAI OSS Models
Make sure you have a K8s cluster with NVidia GPU operator installed:
-```bash
+```console
NAME READY STATUS RESTARTS AGE
gpu-feature-discovery-v2xpk 1/1 Running 0 4d8h
gpu-operator-644fb64985-ffzws 1/1 Running 0 4d8h
@@ -381,4 +381,4 @@ spec:
### Reference
-- https://openai.com/index/introducing-gpt-oss/
+-
diff --git a/ai-ml/production-ready-vllm-stack-on-kubernetes-with-hpa-autoscaling.md b/ai-ml/production-ready-vllm-stack-on-kubernetes-with-hpa-autoscaling.md
index dc27d78..13f153b 100644
--- a/ai-ml/production-ready-vllm-stack-on-kubernetes-with-hpa-autoscaling.md
+++ b/ai-ml/production-ready-vllm-stack-on-kubernetes-with-hpa-autoscaling.md
@@ -4,16 +4,12 @@
**Published:** November 1, 2025
----
-

Running large language models (LLMs) in production requires careful orchestration of resources, efficient scaling mechanisms, and robust infrastructure. In this guide, I’ll walk you through deploying a multi-model VLLM (Very Large Language Model) stack on Kubernetes with Horizontal Pod Autoscaling (HPA), based on our battle-tested production configuration.
This setup has been successfully managing multiple LLMs from OpenAI, Meta, Qwen, and Google, serving production traffic with automatic scaling based on demand.
----
-
## Why VLLM?
VLLM is a fast and memory-efficient inference engine designed specifically for LLMs. It provides:
@@ -23,8 +19,6 @@ VLLM is a fast and memory-efficient inference engine designed specifically for L
- Optimized CUDA kernels for better GPU utilization
- OpenAI-compatible API endpoints
----
-
## Architecture Overview
Our production stack consists of three key components:
@@ -33,8 +27,6 @@ Our production stack consists of three key components:
- **Router Service**: A lightweight router that distributes incoming requests across model instances and handles load balancing.
- **Autoscaling Infrastructure**: HPA configuration that scales deployments based on custom Prometheus metrics, specifically monitoring the number of waiting requests.
----
-
## Prerequisites
Before diving into the deployment, ensure you have:
@@ -45,23 +37,19 @@ Before diving into the deployment, ensure you have:
- A storage class that supports ReadWriteMany (RWX) access mode
- Hugging Face tokens for model downloads
----
-
## Installation Guide
### Step 1: Add the Repository
-```
+```bash
helm repo add vllm https://vllm-project.github.io/production-stack
```
----
-
### Step 2: Understanding the Configuration
Let me break down the key configuration parameters for each model:
-```
+```yaml
- name: "gptoss-20b"
repository: "vllm/vllm-openai" # VLLM image supporting your model
tag: "latest"
@@ -75,15 +63,13 @@ Let me break down the key configuration parameters for each model:
- ReadWriteMany # Critical for fast scaling!
```
-**Important Note**: Using `ReadWriteMany` (RWX) access mode is crucial when HPA is enabled. This allows multiple pods to mount the same persistent volume simultaneously, dramatically reducing scaling time since new pods don't need to re-download the model weights.
-
----
+> **Note**: Using `ReadWriteMany` (RWX) access mode is crucial when HPA is enabled. This allows multiple pods to mount the same persistent volume simultaneously, dramatically reducing scaling time since new pods don't need to re-download the model weights.
### Step 3: VLLM Configuration Deep Dive
Each model has specific VLLM runtime configurations:
-```
+```yaml
vllmConfig:
enableChunkedPrefill: false
enablePrefixCaching: false
@@ -98,23 +84,19 @@ vllmConfig:
- `tensorParallelSize`: For larger models requiring multiple GPUs (e.g., 120B model uses 4 GPUs)
- `maxModelLen`: Context window size, adjust based on your use case
----
-
### Step 4: Autoscaling Configuration
Please refer to keda autoscaling guide for HPA:
https://docs.vllm.ai/projects/production-stack/en/latest/use_cases/autoscaling-keda.html
----
-
## Production Model Examples
### Small Model: Llama 3.1 8B Instruct
Ideal for cost-effective inference with good performance:
-```
+```yaml
- name: "meta-llama-31-8b-instruct"
repository: "vllm/vllm-openai"
tag: "latest"
@@ -134,7 +116,7 @@ Ideal for cost-effective inference with good performance:
For high-quality outputs requiring significant compute:
-```
+```yaml
- name: "gptoss-120b"
repository: "vllm/vllm-openai"
tag: "latest"
@@ -154,7 +136,7 @@ For high-quality outputs requiring significant compute:
Perfect for semantic search and RAG applications:
-```
+```yaml
- name: "qwen3-embedding-8b"
repository: "vllm/vllm-openai"
tag: "latest"
@@ -174,7 +156,7 @@ Perfect for semantic search and RAG applications:
Audio-visual-text multimodal model with custom CUDA image:
-```
+```yaml
- name: "qwen3-omni"
repository: "qwenllm/qwen3-omni" # Custom repository
tag: "3-cu124" # CUDA 12.4 optimized
@@ -195,7 +177,7 @@ Audio-visual-text multimodal model with custom CUDA image:
Specialized for complex reasoning tasks:
-```
+```yaml
- name: "llm360-k2-think"
repository: "vllm/vllm-openai"
tag: "v0.10.1.1" # Specific version for compatibility
@@ -216,7 +198,7 @@ Specialized for complex reasoning tasks:
Large-scale multimodal reasoning model:
-```
+```yaml
- name: "qwen3-thinking"
repository: "vllm/vllm-openai"
tag: "latest"
@@ -236,7 +218,7 @@ Large-scale multimodal reasoning model:
High-performance multimodal instruction-following model:
-```
+```yaml
- name: "qwen3-instruct"
repository: "vllm/vllm-openai"
tag: "latest"
@@ -252,13 +234,11 @@ High-performance multimodal instruction-following model:
extraArgs: ["--disable-log-requests", "--gpu-memory-utilization", "0.9"]
```
----
-
## Router Configuration
The router handles incoming traffic and distributes it across model instances:
-```
+```yaml
routerSpec:
replicaCount: 1
autoscaling:
@@ -276,13 +256,11 @@ routerSpec:
The router is lightweight and scales independently based on CPU utilization.
----
-
## Deployment
### Deploy the Stack
-```
+```bash
helm install vllm vllm -f prod-values.yaml -n vllm --install --create-namespace
```
@@ -290,36 +268,34 @@ helm install vllm vllm -f prod-values.yaml -n vllm --install --create-namespace
Check your pods:
-```
+```bash
kubectl get pods -n vllm
```
Expected output:
-```
-NAME READY STATUS RESTARTS AGE
-vllm-deployment-router-5bc8f96685-284m4 1/1 Running 0 8d
-vllm-gemma-3-27b-it-deployment-vllm-9d9f8b554-cdlvj 1/1 Running 2 8d
-vllm-gptoss-120b-deployment-vllm-d75bdcc7f-zprkb 1/1 Running 6 7d
-vllm-qwen3-omni-deployment-vllm-85bfc6dfc7-jb59f 1/1 Running 0 26h
+```console
+NAME READY STATUS RESTARTS AGE
+vllm-deployment-router-5bc8f96685-284m4 1/1 Running 0 8d
+vllm-gemma-3-27b-it-deployment-vllm-9d9f8b554-cdlvj 1/1 Running 2 8d
+vllm-gptoss-120b-deployment-vllm-d75bdcc7f-zprkb 1/1 Running 6 7d
+vllm-qwen3-omni-deployment-vllm-85bfc6dfc7-jb59f 1/1 Running 0 26h
```
Check HPA status:
-```
+```bash
kubectl get hpa -n vllm
```
Expected output:
-```
-NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
-vllm-gptoss-120b-hpa Deployment/vllm-gptoss-120b-deployment-vllm 0/20 1 2 1
-vllm-router-hpa Deployment/vllm-deployment-router cpu: 2%/80% 3 10 3
+```console
+NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
+vllm-gptoss-120b-hpa Deployment/vllm-gptoss-120b-deployment-vllm 0/20 1 2 1
+vllm-router-hpa Deployment/vllm-deployment-router cpu: 2%/80% 3 10 3
```
----
-
## Best Practices and Lessons Learned
### Storage Strategy
@@ -362,8 +338,6 @@ Key metrics to watch:
3. **Aggressive scale-to-zero**: Consider scaling to zero replicas during off-peak hours
4. **Batch requests**: VLLM’s continuous batching is most efficient with multiple concurrent requests
----
-
## Conclusion
Running a production VLLM stack requires careful attention to resource allocation, storage configuration, and autoscaling policies. The configuration shared here has proven stable and cost-effective for serving multiple models under varying load conditions.
@@ -377,15 +351,3 @@ Running a production VLLM stack requires careful attention to resource allocatio
- Leave GPU memory headroom for stability
This setup has been serving production traffic reliably for weeks, automatically handling traffic spikes and optimizing resource usage during quiet periods.
-
----
-
-## Next Steps
-
-- Implement request queuing and prioritization
-- Integrate with litellm for router optimization and token metering
-- Add model caching for frequently accessed weights
-- Set up comprehensive monitoring dashboards
-- Implement blue-green deployments for model updates
-- Explore multi-cluster deployments for high availability
-
diff --git a/custom_conf.py b/custom_conf.py
index bafe8b4..0c53633 100644
--- a/custom_conf.py
+++ b/custom_conf.py
@@ -231,6 +231,16 @@
"source_branch": "main",
"source_directory": "",
"footer_icons": [
+ {
+ "name": "Website",
+ "url": "https://ngkorefoundation.org/",
+ "html": """
+
+ """,
+ "class": "",
+ },
{
"name": "Email",
"url": "mailto:contact@ngkorefoundation.org",
@@ -251,6 +261,16 @@
""",
"class": "",
},
+ {
+ "name": "Matrix",
+ "url": "https://matrix.to/#/#ngkore:matrix.org",
+ "html": """
+
+ """,
+ "class": "",
+ },
{
"name": "X (Twitter)",
"url": "https://x.com/ngkore_org",
diff --git a/ebpf/building-ebpf-monitor-with-go.md b/ebpf/building-ebpf-monitor-with-go.md
index 40b430e..e3949ff 100644
--- a/ebpf/building-ebpf-monitor-with-go.md
+++ b/ebpf/building-ebpf-monitor-with-go.md
@@ -29,8 +29,6 @@ go install github.com/cilium/ebpf/cmd/bpf2go@latest
_Reference code/build guide repo: [GitHub - eBPF Process Monitor](https://github.com/Satyam-git-hub/eBPF_process_monitor.git)_
----
-
## 1. Writing the eBPF Program
Create a project directory and source file:
diff --git a/images/ngkore.png b/images/ngkore.png
index 13091e4..dfbcba2 100644
Binary files a/images/ngkore.png and b/images/ngkore.png differ
diff --git a/index.md b/index.md
index c6045bc..bbef2cd 100644
--- a/index.md
+++ b/index.md
@@ -40,6 +40,7 @@ ai-ml/index
security/index
kernel-bypass/index
ntn/index
+tutorials/index
how-to-contribute
repository-conventions
```
diff --git a/oran/smo-ric/index.md b/oran/smo-ric/index.md
index f2e818a..858d0c0 100644
--- a/oran/smo-ric/index.md
+++ b/oran/smo-ric/index.md
@@ -3,6 +3,7 @@
```{toctree}
:maxdepth: 1
+../../tutorials/osc-oam
osc-oam
uav-path-prediction
qoe-prediction
diff --git a/oran/smo-ric/osc-oam.md b/oran/smo-ric/osc-oam.md
index 9c19582..3cb17c4 100644
--- a/oran/smo-ric/osc-oam.md
+++ b/oran/smo-ric/osc-oam.md
@@ -28,48 +28,48 @@ Below is a snapshot of the kubernetes pods that together form a functional SMO/O

-```bash
+```console
ubuntu@ubuntu:~/o1-adapter$ kubectl get pods -A
-NAMESPACE NAME READY STATUS RESTARTS AGE
-cert-manager cert-manager-5bd57786d4-vcdxb 1/1 Running 0 3h32m
-cert-manager cert-manager-cainjector-57657d5754-94vl4 1/1 Running 0 3h32m
-cert-manager cert-manager-webhook-7d9f8748d4-j956r 1/1 Running 0 3h32m
-ingress-nginx ingress-nginx-admission-create-f8wnm 0/1 Completed 0 3h32m
-ingress-nginx ingress-nginx-admission-patch-crqqj 0/1 Completed 3 3h32m
-ingress-nginx ingress-nginx-controller-65d6d978b-87kpg 1/1 Running 0 3h32m
-kube-system calico-kube-controllers-6b78c44475-v86sb 1/1 Running 0 3h32m
-kube-system canal-wvvb7 2/2 Running 0 3h32m
-kube-system coredns-5cd5577cc9-7gq6t 1/1 Running 0 3h32m
-kube-system coredns-5cd5577cc9-tcr26 1/1 Running 0 3h32m
-kube-system etcd-ubuntu 1/1 Running 0 3h33m
-kube-system kube-apiserver-ubuntu 1/1 Running 0 3h33m
-kube-system kube-controller-manager-ubuntu 1/1 Running 0 3h32m
-kube-system kube-proxy-x7brv 1/1 Running 0 3h32m
-kube-system kube-scheduler-ubuntu 1/1 Running 0 3h33m
-kube-system metrics-server-74fbf9b9b4-xcd5j 1/1 Running 0 3h32m
-kube-system node-local-dns-gf7t6 1/1 Running 0 3h32m
-mariadb-operator mariadb-operator-844954c944-pnclh 1/1 Running 0 3h25m
-mariadb-operator mariadb-operator-cert-controller-769886898b-fqxc2 1/1 Running 0 3h25m
-mariadb-operator mariadb-operator-webhook-6768b6799d-2sgxz 1/1 Running 0 3h25m
-onap mariadb-galera-0 1/1 Running 0 3h22m
-onap onap-chartmuseum-7bc565d46-jnt47 1/1 Running 0 3h21m
-onap onap-dcae-datafile-collector-54fb56f757-ckfmf 1/1 Running 0 3h21m
-onap onap-dcae-ms-healthcheck-d7cf866bb-2ntqz 1/1 Running 0 3h21m
-onap onap-dcae-pm-mapper-9f5b6fc74-qggjd 1/1 Running 0 3h21m
-onap onap-dcae-ves-collector-5f57dcb588-9f4d8 1/1 Running 0 3h21m
-onap onap-dcae-ves-mapper-5dcc6dd778-h2fr2 1/1 Running 0 3h21m
-onap onap-dmaap-dr-mariadb-init-config-job-vrrd7 0/1 Completed 0 3h21m
-onap onap-dmaap-dr-node-0 1/1 Running 0 3h21m
-onap onap-dmaap-dr-prov-66bf788f8-8scnn 1/1 Running 0 3h21m
-onap onap-message-router-0 2/2 Running 0 111m
-onap onap-robot-5c748c57d9-5dp5m 1/1 Running 0 3h21m
-onap onap-sdnc-0 1/1 Running 0 3h21m
-onap onap-sdnc-sdnrdb-init-job-p4bjt 0/1 Completed 0 3h21m
-onap onap-sdnc-web-6cf9547dbd-hlqdd 1/1 Running 0 3h21m
-onap onap-strimzi-entity-operator-5b46c475d4-ln8nb 2/2 Running 0 3h22m
-onap onap-strimzi-kafka-0 1/1 Running 0 3h22m
-onap onap-strimzi-zookeeper-0 1/1 Running 0 3h23m
-strimzi-system strimzi-cluster-operator-585f6fd995-dfts5 1/1 Running 2 (119m ago) 3h26m
+NAMESPACE NAME READY STATUS RESTARTS AGE
+cert-manager cert-manager-5bd57786d4-vcdxb 1/1 Running 0 3h32m
+cert-manager cert-manager-cainjector-57657d5754-94vl4 1/1 Running 0 3h32m
+cert-manager cert-manager-webhook-7d9f8748d4-j956r 1/1 Running 0 3h32m
+ingress-nginx ingress-nginx-admission-create-f8wnm 0/1 Completed 0 3h32m
+ingress-nginx ingress-nginx-admission-patch-crqqj 0/1 Completed 3 3h32m
+ingress-nginx ingress-nginx-controller-65d6d978b-87kpg 1/1 Running 0 3h32m
+kube-system calico-kube-controllers-6b78c44475-v86sb 1/1 Running 0 3h32m
+kube-system canal-wvvb7 2/2 Running 0 3h32m
+kube-system coredns-5cd5577cc9-7gq6t 1/1 Running 0 3h32m
+kube-system coredns-5cd5577cc9-tcr26 1/1 Running 0 3h32m
+kube-system etcd-ubuntu 1/1 Running 0 3h33m
+kube-system kube-apiserver-ubuntu 1/1 Running 0 3h33m
+kube-system kube-controller-manager-ubuntu 1/1 Running 0 3h32m
+kube-system kube-proxy-x7brv 1/1 Running 0 3h32m
+kube-system kube-scheduler-ubuntu 1/1 Running 0 3h33m
+kube-system metrics-server-74fbf9b9b4-xcd5j 1/1 Running 0 3h32m
+kube-system node-local-dns-gf7t6 1/1 Running 0 3h32m
+mariadb-operator mariadb-operator-844954c944-pnclh 1/1 Running 0 3h25m
+mariadb-operator mariadb-operator-cert-controller-769886898b-fqxc2 1/1 Running 0 3h25m
+mariadb-operator mariadb-operator-webhook-6768b6799d-2sgxz 1/1 Running 0 3h25m
+onap mariadb-galera-0 1/1 Running 0 3h22m
+onap onap-chartmuseum-7bc565d46-jnt47 1/1 Running 0 3h21m
+onap onap-dcae-datafile-collector-54fb56f757-ckfmf 1/1 Running 0 3h21m
+onap onap-dcae-ms-healthcheck-d7cf866bb-2ntqz 1/1 Running 0 3h21m
+onap onap-dcae-pm-mapper-9f5b6fc74-qggjd 1/1 Running 0 3h21m
+onap onap-dcae-ves-collector-5f57dcb588-9f4d8 1/1 Running 0 3h21m
+onap onap-dcae-ves-mapper-5dcc6dd778-h2fr2 1/1 Running 0 3h21m
+onap onap-dmaap-dr-mariadb-init-config-job-vrrd7 0/1 Completed 0 3h21m
+onap onap-dmaap-dr-node-0 1/1 Running 0 3h21m
+onap onap-dmaap-dr-prov-66bf788f8-8scnn 1/1 Running 0 3h21m
+onap onap-message-router-0 2/2 Running 0 111m
+onap onap-robot-5c748c57d9-5dp5m 1/1 Running 0 3h21m
+onap onap-sdnc-0 1/1 Running 0 3h21m
+onap onap-sdnc-sdnrdb-init-job-p4bjt 0/1 Completed 0 3h21m
+onap onap-sdnc-web-6cf9547dbd-hlqdd 1/1 Running 0 3h21m
+onap onap-strimzi-entity-operator-5b46c475d4-ln8nb 2/2 Running 0 3h22m
+onap onap-strimzi-kafka-0 1/1 Running 0 3h22m
+onap onap-strimzi-zookeeper-0 1/1 Running 0 3h23m
+strimzi-system strimzi-cluster-operator-585f6fd995-dfts5 1/1 Running 2 (119m ago) 3h26m
```
The pods described above can be logically grouped into functional layers:
@@ -85,7 +85,6 @@ The pods described above can be logically grouped into functional layers:
The heart of the configuration management capability in the SMO is the SDN Controller. In the OSC distribution, this is a specific instantiation of the ONAP SDNC, which is itself built upon the OpenDaylight (ODL) platform.
- The primary function of the SDNC in the O1 context is to act as the **NETCONF Client**. It maintains persistent Secure Shell (SSH) or TLS connections to the southbound managed elements (O-DUs, O-CUs). Through these connections, it performs the following critical operations:
-
- **Capability Discovery**: During the NETCONF "Hello" exchange, the SDNC discovers which YANG modules are supported by the device.
- **Configuration Push**: It translates high-level network intents into `edit-config` RPC calls to modify the device's (DU, CU) running configuration.
- **Synchronization**: It periodically issues `get-config` calls to ensure its internal view of the network matches the actual device state.
@@ -243,7 +242,7 @@ Some example operational and data workflows within the OSC OAM/SMO environment a
Example, `pnfRegistration` event payload sent by the OAI O1 Adapter:
-```bash
+```json
"event": {
"commonEventHeader": {
"domain": "pnfRegistration",
diff --git a/pqc/index.md b/pqc/index.md
index 790ab79..5c4e6a3 100644
--- a/pqc/index.md
+++ b/pqc/index.md
@@ -14,4 +14,5 @@ lwe-quantum-proof-algorithm
telecom/index
constant-time/index
fips/index
+jostle-vs-bc/index
```
diff --git a/pqc/jostle-vs-bc/images/bc-mldsa.png b/pqc/jostle-vs-bc/images/bc-mldsa.png
new file mode 100644
index 0000000..e380011
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mldsa.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-mlkem-1.png b/pqc/jostle-vs-bc/images/bc-mlkem-1.png
new file mode 100644
index 0000000..74cbaf0
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mlkem-1.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-mlkem-2.png b/pqc/jostle-vs-bc/images/bc-mlkem-2.png
new file mode 100644
index 0000000..534bedd
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mlkem-2.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-mlkem-3.png b/pqc/jostle-vs-bc/images/bc-mlkem-3.png
new file mode 100644
index 0000000..b07aa5f
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mlkem-3.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-mlkem-4.png b/pqc/jostle-vs-bc/images/bc-mlkem-4.png
new file mode 100644
index 0000000..59954e8
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mlkem-4.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-mlkem-5.png b/pqc/jostle-vs-bc/images/bc-mlkem-5.png
new file mode 100644
index 0000000..b57a3f7
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mlkem-5.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-mlkem-6.png b/pqc/jostle-vs-bc/images/bc-mlkem-6.png
new file mode 100644
index 0000000..276d2ff
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-mlkem-6.png differ
diff --git a/pqc/jostle-vs-bc/images/bc-scrypt.png b/pqc/jostle-vs-bc/images/bc-scrypt.png
new file mode 100644
index 0000000..05e501e
Binary files /dev/null and b/pqc/jostle-vs-bc/images/bc-scrypt.png differ
diff --git a/pqc/jostle-vs-bc/images/jostle-mldsa.png b/pqc/jostle-vs-bc/images/jostle-mldsa.png
new file mode 100644
index 0000000..2202a92
Binary files /dev/null and b/pqc/jostle-vs-bc/images/jostle-mldsa.png differ
diff --git a/pqc/jostle-vs-bc/images/jostle-mlkem.png b/pqc/jostle-vs-bc/images/jostle-mlkem.png
new file mode 100644
index 0000000..d7dd6a8
Binary files /dev/null and b/pqc/jostle-vs-bc/images/jostle-mlkem.png differ
diff --git a/pqc/jostle-vs-bc/images/jostle-scrypt.png b/pqc/jostle-vs-bc/images/jostle-scrypt.png
new file mode 100644
index 0000000..d445557
Binary files /dev/null and b/pqc/jostle-vs-bc/images/jostle-scrypt.png differ
diff --git a/pqc/jostle-vs-bc/images/system-arch.png b/pqc/jostle-vs-bc/images/system-arch.png
new file mode 100644
index 0000000..c8a3b6b
Binary files /dev/null and b/pqc/jostle-vs-bc/images/system-arch.png differ
diff --git a/pqc/jostle-vs-bc/index.md b/pqc/jostle-vs-bc/index.md
new file mode 100644
index 0000000..b4b1f7d
--- /dev/null
+++ b/pqc/jostle-vs-bc/index.md
@@ -0,0 +1,11 @@
+# Jostle vs. Bouncy Castle
+
+```{toctree}
+:maxdepth: 1
+
+../../tutorials/pqc/openssl-jostle
+../../tutorials/pqc/bouncy-castle-java
+jostle-supported-algorithms
+../../tutorials/pqc/jostle-bc-crypto-benchmarking-tool
+jostle-vs-bc-crypto-benchmarking
+```
diff --git a/pqc/jostle-vs-bc/jostle-supported-algorithms.md b/pqc/jostle-vs-bc/jostle-supported-algorithms.md
new file mode 100644
index 0000000..e67e55c
--- /dev/null
+++ b/pqc/jostle-vs-bc/jostle-supported-algorithms.md
@@ -0,0 +1,151 @@
+# Jostle Supported Algorithms
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** January 27, 2026
+
+This document provides a complete reference for all cryptographic operations supported in the [OpenSSL Jostle provider](https://github.com/openssl-projects/openssl-jostle).
+
+> **Note:** Some algorithms might be missed from the list below. Please refer to the source code for the most accurate and up-to-date information.
+
+## 1. Symmetric Block Ciphers
+
+**Padding Pattern:**
+
+- ECB/CBC modes support NoPadding, PKCS5Padding, PKCS7Padding.
+- Stream modes (CFB, OFB, CTR) and AEAD modes (GCM, CCM, OCB) support NoPadding only.
+- Special purpose modes (XTS, WRAP, WRAP_PAD) support NoPadding only.
+
+### AES (Advanced Encryption Standard)
+
+| Property | Details |
+| :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Key Sizes** | 128, 192, 256 bits |
+| **Supported Block Modes** | ECB, CBC, CFB1, CFB8, CFB128, OFB, CTR, GCM, CCM, OCB, XTS, WRAP, WRAP_PAD |
+| **Implementation** | [AESBlockCipherSpi.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/AESBlockCipherSpi.java#L19) |
+| **Key Generator** | [AESKeyGenerator.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/AESKeyGenerator.java#L11) |
+| **Configuration** | [ProvAES.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvAES.java#L28) |
+| **Unsupported Findings** (handling commented) | AES128: WRAP, WRAP_PAD, OCB, CCM ([block_cipher_ctx.c](https://github.com/openssl-projects/openssl-jostle/blob/main/interface/util/block_cipher_ctx.c#L169-L173)) AES192: WRAP, WRAP_PAD, OCB, CCM, XTS ([block_cipher_ctx.c:221-232](https://github.com/openssl-projects/openssl-jostle/blob/main/interface/util/block_cipher_ctx.c#L221-L232)) AES256: WRAP, WRAP_PAD, OCB, CCM ([block_cipher_ctx.c:279-283](https://github.com/openssl-projects/openssl-jostle/blob/main/interface/util/block_cipher_ctx.c#L279-L283)) |
+
+### ARIA
+
+| Property | Details |
+| :----------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Key Sizes** | 128, 192, 256 bits |
+| **Supported Block Modes** | ECB, CBC, CFB1, CFB8, CFB128, CTR, OFB, GCM, CCM |
+| **Implementation** | [ARIABlockCipherSpi.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ARIABlockCipherSpi.java#L18) |
+| **Configuration** | [ProvARIA.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvARIA.java#L28) |
+| **Unsupported Findings** (handling commented) | ARIA128: CCM, GCM ([block_cipher_ctx.c](https://github.com/openssl-projects/openssl-jostle/blob/main/interface/util/block_cipher_ctx.c#L331-L332)) ARIA192: CCM, GCM ([block_cipher_ctx.c:375-376](https://github.com/openssl-projects/openssl-jostle/blob/main/interface/util/block_cipher_ctx.c#L375-L376)) ARIA256: CCM, GCM ([block_cipher_ctx.c:419-420](https://github.com/openssl-projects/openssl-jostle/blob/main/interface/util/block_cipher_ctx.c#L419-L420)) |
+
+### CAMELLIA
+
+| Property | Details |
+| :------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Key Sizes** | 128, 192, 256 bits |
+| **Supported Block Modes** | ECB, CBC, CFB1, CFB8, CFB128, OFB, CTR |
+| **Implementation** | [CAMELLIABlockCipherSpi.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/CAMELLIABlockCipherSpi.java#L18) |
+| **Configuration** | [ProvCAMELLIA.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvCAMELLIA.java#L28) |
+
+### SM4
+
+| Property | Details |
+| :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **Key Sizes** | 128 bits |
+| **Supported Block Modes** | ECB, CBC, CFB128, OFB, CTR |
+| **Implementation** | [SM4BlockCipherSpi.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/SM4BlockCipherSpi.java#L18) |
+| **Configuration** | [ProvSM4.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvSM4.java#L28) |
+
+## 2. Post-Quantum Cryptography
+
+### ML-DSA (Module-Lattice-Based Digital Signature Algorithm)
+
+| Property | Details |
+| :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **Parameter Sets** | ML-DSA-44, ML-DSA-65, ML-DSA-87 |
+| **Security Levels** | ML-DSA-44: NIST Level 2 (128-bit) ML-DSA-65: NIST Level 3 (192-bit) ML-DSA-87: NIST Level 5 (256-bit) |
+| **Signature Algorithms** | MLDSA (generic, uses key to determine parameter set) ML-DSA (alias for MLDSA) ML-DSA-44, ML-DSA-65, ML-DSA-87 ML-DSA-EXTERNAL-MU (external message randomization) ML-DSA-CALCULATE-MU (calculated message randomization) |
+| **Implementation** | Key Pair Generator: [MLDSAKeyPairGeneratorImpl.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/mldsa/MLDSAKeyPairGeneratorImpl.java#L25) Signature Spi: [MLDSASignatureSpi.java:28](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/mldsa/MLDSASignatureSpi.java#L28) Key Factory: [MLDSAKeyFactorySpiImpl.java:28](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/mldsa/MLDSAKeyFactorySpiImpl.java#L28) |
+| **Configuration** | [ProvMLDSA.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvMLDSA.java) |
+
+### SLH-DSA (Stateless Hash-Based Digital Signature Algorithm)
+
+| Property | Details |
+| :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Parameter Sets** | SLH-DSA-SHA2-128S, SLH-DSA-SHA2-128F, SLH-DSA-SHA2-192S, SLH-DSA-SHA2-192F, SLH-DSA-SHA2-256S, SLH-DSA-SHA2-256F, SLH-DSA-SHAKE-128S, SLH-DSA-SHAKE-128F, SLH-DSA-SHAKE-192S, SLH-DSA-SHAKE-192F, SLH-DSA-SHAKE-256S, SLH-DSA-SHAKE-256F |
+| **Hash Functions** | SHA2-based (6 variants), SHAKE-based (6 variants) |
+| **Performance Variants** | S (Small): Smaller signature size, slower signing F (Fast): Larger signature size, faster signing |
+| **Security Levels** | 128-bit (SLH-DSA-SHA2-128S/F, SLH-DSA-SHAKE-128S/F) 192-bit (SLH-DSA-SHA2-192S/F, SLH-DSA-SHAKE-192S/F) 256-bit (SLH-DSA-SHA2-256S/F, SLH-DSA-SHAKE-256S/F) |
+| **Signature Algorithms** | SLHDSA (generic, uses key to determine parameter set) SLH-DSA (alias for SLHDSA) SLH-DSA-SHA2-128S, SLH-DSA-SHA2-128F, SLH-DSA-SHA2-192S, SLH-DSA-SHA2-192F, SLH-DSA-SHA2-256S, SLH-DSA-SHA2-256F, SLH-DSA-SHAKE-128S, SLH-DSA-SHAKE-128F, SLH-DSA-SHAKE-192S, SLH-DSA-SHAKE-192F, SLH-DSA-SHAKE-256S, SLH-DSA-SHAKE-256F SLH-DSA-PURE (pure message signing) SLH-DSA-NONE (pre-hashed message) DET-SLH-DSA-PURE (deterministic pure) DET-SLH-DSA-NONE (deterministic pre-hashed) |
+| **Implementation** | Key Pair Generator: [SLHDSAKeyPairGenerator.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/slhdsa/SLHDSAKeyPairGenerator.java#L27) Signature Spi: [SLHDSASignatureSpi.java:26](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/slhdsa/SLHDSASignatureSpi.java#L26) Key Factory: [SLHDSAKeyFactorySpi.java:28](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/slhdsa/SLHDSAKeyFactorySpi.java#L28) |
+| **Configuration** | [ProvSLHDSA.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvSLHDSA.java) |
+
+### ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism)
+
+| Property | Details |
+| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Parameter Sets** | ML-KEM-512, ML-KEM-768, ML-KEM-1024 |
+| **Security Levels** | ML-KEM-512: NIST Level 1 (128-bit) ML-KEM-768: NIST Level 3 (192-bit) ML-KEM-1024: NIST Level 5 (256-bit) |
+| **Key Types** | KeyPairGenerator: MLKEM, ML-KEM-512, ML-KEM-768, ML-KEM-1024 KeyGenerator: MLKEM, ML-KEM-512, ML-KEM-768, ML-KEM-1024 KeyFactory: MLKEM, ML-KEM-512, ML-KEM-768, ML-KEM-1024 |
+| **Implementation** | Key Generator: [MLKEMKeyGenerator.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/mlkem/MLKEMKeyGenerator.java#L29) Key Pair Generator: [MLKEMKeyPairGenerator.java:28](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/mlkem/MLKEMKeyPairGenerator.java#L28) Key Factory: [MLKEMKeyFactorySpi.java:28](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/mlkem/MLKEMKeyFactorySpi.java#L28) |
+| **Configuration** | [ProvMLKEM.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvMLKEM.java) |
+
+## 3. Key Derivation Functions (KDF)
+
+### PBKDF2 (Password-Based Key Derivation Function 2)
+
+| Property | Details |
+| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Algorithm Names** | PBKDF2 (uses SHA-1 by default) PBKDF2WithHmacSHA1, PBKDF2WithHmacSHA224, PBKDF2WithHmacSHA256, PBKDF2WithHmacSHA384, PBKDF2WithHmacSHA512, PBKDF2WithHmacSHA512-224, PBKDF2WithHmacSHA512-256 PBKDF2WithHmacSHA3-224, PBKDF2WithHmacSHA3-256, PBKDF2WithHmacSHA3-384, PBKDF2WithHmacSHA3-512 PBKDF2WithHmacBLAKE2B-512, PBKDF2WithHmacBLAKE2S-256 PBKDF2WithHmacSM3, PBKDF2WithHmacMD5, PBKDF2WithHmacMD5-SHA1, PBKDF2WithHmacRIPEMD160 |
+| **HMAC Functions** | SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256 SHA3-224, SHA3-256, SHA3-384, SHA3-512 BLAKE2B-512, BLAKE2S-256 SM3, MD5, MD5-SHA1, RIPEMD160 |
+| **Implementation** | [PBEKDF2SecretKeyFactory.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/kdf/PBEKDF2SecretKeyFactory.java#L25) |
+| **Configuration** | [ProvPBEKDF.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvPBEKDF.java) |
+
+### Scrypt
+
+| Property | Details |
+| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Algorithm Names** | SCRYPT 1.3.6.1.4.1.11591.4.11 (OID format) |
+| **Parameters** | N (CPU/memory cost), r (block size), p (parallelization) |
+| **Implementation** | [ScryptSecretKeyFactory.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/kdf/ScryptSecretKeyFactory.java#L23) |
+| **Configuration** | [ProvScryptKDF.java](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvScryptKDF.java) |
+
+## 4. Algorithms Present But Not Exposed
+
+The following algorithms exist in the codebase but are NOT registered in the provider configuration.
+
+### Symmetric Ciphers (Unexposed)
+
+You can find these algorithms defined in [`OSSLCipher.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/OSSLCipher.java) but they are not registered in the provider.
+
+| Algorithm | Type | Available Modes |
+| :-------------------- | :----- | :-------------------- |
+| **RC4** | Stream | N/A |
+| **RC4_40** | Stream | N/A |
+| **IDEA** | Block | ECB, CFB64, OFB, CBC |
+| **RC2** | Block | ECB, CBC, CFB64, OFB |
+| **RC2_40** | Block | CBC |
+| **RC2_64** | Block | CBC |
+| **BlowFish** | Block | ECB, CBC, CFB64, OFB |
+| **CAST5** | Block | ECB, CBC, CFB64, OFB |
+| **ChaCha20** | Stream | N/A |
+| **ChaCha20-Poly1305** | AEAD | N/A |
+| **SEED** | Block | ECB, CBC, CFB128, OFB |
+
+### Signature Algorithms (Unexposed)
+
+You can find these algorithms defined in [`SigAlgs.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/SigAlgs.java) but they are not registered in the provider.
+
+| Algorithm |
+| :----------------------------- |
+| `DSA_SHA1` |
+| `DSA_SHA2_{256,384,512}` |
+| `DSA_SHA3_{224,256,384,512}` |
+| `RSA_SHA2_{224,256,384,512}` |
+| `RSA_SHA3_{224,256,384,512}` |
+| `ECDSA_SHA2_{224,256,384,512}` |
+| `ECDSA_SHA3_{224,256,384,512}` |
+| `ED25519` |
+| `ED25519_CTX` |
+| `ED25519_PH` |
+| `ED448` |
+| `ED448_PH` |
diff --git a/pqc/jostle-vs-bc/jostle-vs-bc-crypto-benchmarking.md b/pqc/jostle-vs-bc/jostle-vs-bc-crypto-benchmarking.md
new file mode 100644
index 0000000..3e20dbc
--- /dev/null
+++ b/pqc/jostle-vs-bc/jostle-vs-bc-crypto-benchmarking.md
@@ -0,0 +1,568 @@
+# OpenSSL Jostle vs Bouncy Castle Crypto Benchmarking
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/) & [Aditya Koranga](https://www.linkedin.com/in/aditya-koranga/)
+
+**Published:** January 30, 2026
+
+This project ([ngkore/jostle-bc-crypto-benchmarking](https://github.com/ngkore/jostle-bc-crypto-benchmarking)) is a micro-benchmarking suite built on the Java Microbenchmark Harness (JMH) that measures the performance of cryptographic operations across two Java security providers: [Bouncy Castle (BC)](https://github.com/bcgit/bc-java), a pure-Java implementation, and [OpenSSL Jostle](https://github.com/openssl-projects/openssl-jostle), a JNI bridge to the native OpenSSL library. The suite covers symmetric ciphers (AES, ARIA, Camellia, SM4), post-quantum algorithms standardized by NIST (ML-DSA, SLH-DSA, ML-KEM), and key derivation functions (PBKDF2, Scrypt). This blog provides an in-depth technical analysis of the system architecture, the Java Cryptography Architecture (JCA) provider model, and the specific implementation challenges encountered with post-quantum key encapsulation mechanisms and key derivation functions.
+
+## System Architecture
+
+The benchmarking environment is organized into three layers, each with a distinct responsibility.
+
+### Layer 1 - Benchmarking (JMH)
+
+JMH orchestrates the entire execution lifecycle. It manages JVM forks, warmup phases, measurement iterations, and result collection. All benchmark classes are annotated with JMH annotations that control trial setup, parameterization, and result consumption.
+
+### Layer 2 -- Application (Java)
+
+This layer contains the benchmark definitions in the `com.benchmark` package and the two cryptographic providers under test. Bouncy Castle executes all cryptographic operations in pure Java. The Jostle provider defines Java-side JNI wrappers that delegate to native code.
+
+### Layer 3 -- Native (OpenSSL)
+
+The Jostle provider loads two native shared libraries at runtime. `libssl.so` is the standard OpenSSL cryptographic library. `libinterface_jni.so` is a bridge library that translates Java JNI calls into OpenSSL API invocations. The JVM loads these via `System.loadLibrary`, controlled by the `java.library.path` system property and the `LD_LIBRARY_PATH` environment variable.
+
+
+
+## JMH Benchmarking Framework
+
+### Why using JMH?
+
+Standard Java benchmarking using `System.nanoTime()` produces unreliable results for three reasons.
+
+1. JIT compilation causes a performance profile that changes over time. The JVM interprets bytecode during early invocations and later compiles hot methods into optimized native code. Measurements taken during interpretation do not represent steady-state performance.
+
+2. Garbage collection introduces non-deterministic pauses. A GC event during a measurement window can inflate the reported time by orders of magnitude.
+
+3. Dead code elimination allows the JVM to optimize away computations whose results are unused. A naive benchmark that discards its output may measure nothing at all.
+
+JMH addresses these problems through three mechanisms. It runs each benchmark in a separate JVM fork, isolating it from the parent process and prior benchmark state. It provides configurable warmup iterations that are executed but discarded, allowing the JIT compiler to reach steady state before measurement begins. It supplies `Blackhole` objects that consume computed results in a way the JVM cannot optimize away.
+
+### Configuration Parameters
+
+JMH exposes several parameters that control the statistical quality of results.
+
+
+
+**Forks:** determine how many independent JVM processes execute the benchmark. Multiple forks reduce the impact of OS scheduling, ASLR, and other process-level variance.
+
+**Warmup Iterations:** are benchmark cycles that execute before measurement begins. They exist solely to trigger JIT compilation and allow the JVM to reach thermal equilibrium.
+
+**Measurement Iterations:** are the cycles where timing data is collected. More iterations reduce the standard error of the reported mean.
+
+**Benchmark Modes:** control what metric is reported:
+
+| Mode | Flag | Metric | Use Case |
+| :--------------- | :------- | :--------------------------------- | :--------------------------- |
+| Throughput | `thrpt` | Operations per second (ops/s) | Overall capacity measurement |
+| Average Time | `avgt` | Mean time per operation (ns/op) | Latency measurement |
+| Sample Time | `sample` | Percentile distribution (p50, p99) | Tail latency detection |
+| Single Shot Time | `ss` | Time for one cold-start execution | Startup cost measurement |
+
+### Benchmark Execution Script
+
+The project provides a shell script that configures and launches the benchmarks. The relevant parameters are passed to Gradle as project properties.
+
+
+
+Source: [`scripts/run_benchmarks.sh`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/scripts/run_benchmarks.sh)
+
+```bash
+WARMUP=1
+ITERATIONS=3
+TIME="1s"
+FORK=1
+MODE="all"
+
+GRADLE_ARGS="-PjmhWarmup=$WARMUP -PjmhIterations=$ITERATIONS -PjmhTime=$TIME -PjmhForks=$FORK -PjmhMode=$MODE"
+./gradlew jmh $GRADLE_ARGS --console=plain 2>&1 | tee "$LOG_FILE"
+```
+
+The script also configures the `NATIVE_LIB_PATH` environment variable, which the Gradle build passes to the JVM as `java.library.path`. This is required for the Jostle provider to locate and load `libinterface_jni.so` and `libssl.so` at runtime.
+
+> **Note:** The above values were being used for generating the current benchmarking results (JSON).
+
+## JCA Provider Model
+
+The Java Cryptography Architecture (JCA) defines a provider-independent API for cryptographic operations. Applications call factory methods such as `Cipher.getInstance("AES")` or `Signature.getInstance("ML-DSA")`, and the JCA runtime resolves these to provider-specific implementations through the Service Provider Interface (SPI).
+
+### SPI Contract
+
+For each cryptographic service, the JCA defines two classes:
+
+- An **application-facing class** (e.g., `javax.crypto.Cipher`) that provides the public API.
+- An **SPI class** (e.g., `javax.crypto.CipherSpi`) that defines the methods a provider must implement.
+
+When application code calls `Cipher.getInstance("AES", provider)`, the JCA:
+
+1. Looks up the `provider` object's service registry.
+2. Finds the implementation class mapped to `"Cipher.AES"`.
+3. Instantiates that class (which must extend `CipherSpi`) via reflection.
+4. Wraps it inside a `Cipher` object and returns it to the caller.
+
+This indirection allows application code to remain provider-agnostic. The same `Cipher.doFinal()` call executes Bouncy Castle's pure-Java AES engine or Jostle's native OpenSSL AES implementation depending on which provider was specified.
+
+### Provider Registration
+
+Both providers in this project implement the registration step differently, reflecting their distinct architectural philosophies.
+
+> **Note:** This is not a part of the project itself, this is something I get aware of during the project. Consider this a pre-requisite for beginners.
+
+## Benchmark Class
+
+The project might look big but the main source code lies around only 4 files which is organized as a class hierarchy rooted at `CryptoBenchmark`.
+
+```
+CryptoBenchmark (abstract)
+ |-- SymmetricBenchmark (abstract)
+ | |-- aes
+ | |-- aria
+ | |-- camellia
+ | |-- sm4
+ |
+ |-- PqcBenchmark (abstract)
+ | |-- mldsa
+ | |-- slhdsa
+ | |-- mlkem
+ |
+ |-- KdfBenchmark (abstract)
+ |-- pbkdf2
+ |-- scrypt
+```
+
+### 1. CryptoBenchmark.java - Provider Initialization
+
+The base class holds the provider instance and the parameterized provider name. JMH's `@Param` annotation causes the benchmark to be executed once per listed value, producing results for both providers from a single run.
+
+
+
+Source: [`src/main/java/com/benchmark/CryptoBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/CryptoBenchmark.java)
+
+```java
+@State(Scope.Benchmark)
+public abstract class CryptoBenchmark {
+
+ @Param({"BC", "Jostle"})
+ public String providerName;
+
+ public Provider provider;
+
+ public void initProvider() {
+ if ("BC".equalsIgnoreCase(providerName)) {
+ provider = new BouncyCastleProvider();
+ } else if ("Jostle".equalsIgnoreCase(providerName)) {
+ provider = new JostleProvider();
+ } else {
+ throw new IllegalArgumentException("Unknown provider: " + providerName);
+ }
+ Security.addProvider(provider);
+ }
+}
+```
+
+The `@State(Scope.Benchmark)` annotation tells JMH that a single instance of this class is shared across all threads executing a given benchmark method. The `initProvider()` method is called from each subclass's `@Setup` method before any benchmark iteration begins.
+
+### 2. SymmetricBenchmark.java -- Cipher Operations
+
+The symmetric benchmark subclass parameterizes over cipher transformations (algorithm/mode/padding) and key sizes. The `commonSetup` method constructs a key, generates test data, and pre-computes the ciphertext used by the decryption benchmark.
+
+
+
+Source: [`src/main/java/com/benchmark/SymmetricBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/SymmetricBenchmark.java)
+
+```java
+public abstract class SymmetricBenchmark extends CryptoBenchmark {
+
+ protected SecretKey key;
+ protected byte[] data;
+ protected byte[] encryptedData;
+ protected AlgorithmParameterSpec params;
+ protected String transform;
+
+ protected void commonSetup(String transform, int keySize) throws Exception {
+ this.transform = transform;
+ initProvider();
+
+ String[] parts = transform.split("/");
+ String algorithm = parts[0];
+ String mode = parts[1];
+
+ // ... key generation and IV/nonce setup ...
+
+ Cipher encryptCipher = Cipher.getInstance(transform, provider);
+ // ... encrypt test data for decryption benchmark ...
+ }
+
+ @Benchmark
+ public void encrypt(Blackhole bh) throws Exception {
+ Cipher cipher = Cipher.getInstance(transform, provider);
+ if (params != null) {
+ cipher.init(Cipher.ENCRYPT_MODE, key, params);
+ } else {
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ }
+ bh.consume(cipher.doFinal(data));
+ }
+
+ @Benchmark
+ public void decrypt(Blackhole bh) throws Exception {
+ Cipher cipher = Cipher.getInstance(transform, provider);
+ if (params != null) {
+ cipher.init(Cipher.DECRYPT_MODE, key, params);
+ } else {
+ cipher.init(Cipher.DECRYPT_MODE, key);
+ }
+ bh.consume(cipher.doFinal(encryptedData));
+ }
+}
+```
+
+Each concrete subclass declares its own `@Param` sets. For example, the `Aes` subclass benchmarks AES across GCM, ECB, CBC, CTR, OFB, and CFB modes with 128, 192, and 256-bit keys. The `Sm4` subclass is restricted to 128-bit keys per the SM4 specification.
+
+> **Note:** `Cipher.getInstance(transform, provider)` is called inside the `@Benchmark` method itself, not in `@Setup`. This is intentional: the benchmark measures the full cost of provider lookup, cipher instantiation, initialization, and execution.
+
+### 3. KdfBenchmark.java - KDF Operations
+
+The `KdfBenchmark` abstract class extends `CryptoBenchmark` and provides shared state for both PBKDF2 and Scrypt benchmarks: a password and a salt. The `commonSetup` method initializes the provider and sets fixed input values used across all KDF benchmark iterations.
+
+
+
+Source: [`src/main/java/com/benchmark/KdfBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/KdfBenchmark.java)
+
+```java
+public abstract class KdfBenchmark extends CryptoBenchmark {
+
+ protected char[] password;
+ protected byte[] salt;
+
+ protected void commonSetup() throws Exception {
+ initProvider();
+ password = "password".toCharArray();
+ salt = new byte[16]; // 128-bit salt
+ }
+```
+
+The password and salt are fixed across iterations. This is intentional: the benchmark measures the computational cost of the derivation function itself, not the variance introduced by different inputs. A 128-bit (16-byte) salt is standard for both PBKDF2 and Scrypt.
+
+#### 3.1 PBKDF2 Benchmark
+
+PBKDF2 derives a key by iteratively applying a pseudorandom function (PRF), typically HMAC with a chosen hash algorithm to the password and salt. The iteration count is the primary cost parameter: higher counts produce slower derivations.
+
+
+
+Source: [`src/main/java/com/benchmark/KdfBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/KdfBenchmark.java)
+
+```java
+public static class Pbkdf2 extends KdfBenchmark {
+
+ @Benchmark
+ public void deriveKey(Blackhole bh) throws Exception {
+ SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm, provider);
+ PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, 256);
+ bh.consume(skf.generateSecret(spec));
+ }
+}
+```
+
+The benchmark parameterizes over 11 HMAC-based PRF variants spanning three hash families: SHA-2 , SHA-3, and SM3. Each variant is benchmarked at two iteration counts - 1,000 and 10,000, producing 22 benchmark configurations per provider.
+
+The PBKDF2 benchmark code is entirely provider-agnostic. Both `SecretKeyFactory` and `PBEKeySpec` are standard JCA classes (`javax.crypto.SecretKeyFactory` and `javax.crypto.spec.PBEKeySpec`). No provider-specific imports or conditional branches are needed. This is possible because the `SecretKeyFactory.generateSecret(KeySpec)` method accepts a standard `PBEKeySpec` and returns a standard `SecretKey` - a single input, single output contract that maps cleanly onto the SPI model.
+
+#### 3.2 Scrypt Benchmark
+
+Scrypt (RFC 7914) is a memory-hard KDF designed to be expensive to parallelize on custom hardware (ASICs/FPGAs). It achieves this by requiring a large amount of memory proportional to the cost parameter `N`. The algorithm also takes a block size parameter `r` (which determines the size of the internal mixing function) and a parallelization parameter `p` (which controls independent mixing lanes).
+
+
+
+Source: [`src/main/java/com/benchmark/KdfBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/KdfBenchmark.java)
+
+```java
+public static class Scrypt extends KdfBenchmark {
+
+ // ... parameters ...
+ @Benchmark
+ public void deriveKey(Blackhole bh) throws Exception {
+ SecretKeyFactory skf = SecretKeyFactory.getInstance("SCRYPT", provider);
+
+ KeySpec spec;
+ // Fixed parameters: r=8, p=1, keyLen=256
+ int r = 8;
+ int p = 1;
+ int keyLen = 256;
+
+ if ("BC".equalsIgnoreCase(providerName)) {
+ spec = new org.bouncycastle.jcajce.spec.ScryptKeySpec(password, salt, N, r, p, keyLen);
+ } else {
+ // OpenSSL Jostle provider
+ spec = new org.openssl.jostle.jcajce.spec.ScryptKeySpec(password, salt, N, r, p, keyLen);
+ }
+ bh.consume(skf.generateSecret(spec));
+ }
+}
+```
+
+The benchmark parameterizes the cost parameter `N` at two values: 16,384 (2^14) and 32,768 (2^15). The block size `r = 8` and parallelization `p = 1` are fixed at standard values. The derived key length is 256 bits.
+
+Unlike PBKDF2, the Scrypt benchmark requires provider-specific conditional branching. Both providers require a custom `ScryptKeySpec` class to carry the Scrypt-specific parameters (`N`, `r`, `p`), and the standard JCA `KeySpec` hierarchy does not include a Scrypt-aware specification or atleast I am not aware of.
+
+The two `ScryptKeySpec` classes reside in different packages (`org.bouncycastle.jcajce.spec` vs `org.openssl.jostle.jcajce.spec`) and cannot be used interchangeably, even though they are structurally identical.
+
+**Bouncy Castle:**
+
+Source: [`org/bouncycastle/jcajce/spec/ScryptKeySpec.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/spec/ScryptKeySpec.java#L10)
+
+
+
+**Jostle:**
+
+Source: [`org/openssl/jostle/jcajce/spec/ScryptKeySpec.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/spec/ScryptKeySpec.java#L12)
+
+
+
+### 4. PqcBenchmark.java - PQ Operations
+
+The PQC benchmark subclass introduces `KeyPair` and raw `byte[]` data as shared state. Each PQC inner class benchmarks three operations: key generation, signing/encapsulation, and verification/decapsulation. The ML-DSA and SLH-DSA classes use the standard `Signature` API. The ML-KEM class requires provider-specific code.
+
+#### 4.1. ML-DSA Benchmark
+
+ML-DSA (Module-Lattice-Based Digital Signature Algorithm, FIPS 204) is one of the three NIST-standardized post-quantum algorithms. Both providers implement it using the standard `java.security.Signature` API, which makes the benchmark code entirely provider-agnostic. The `Signature` API defines a contract where `sign()` returns a `byte[]` and `verify()` accepts a `byte[]`. These return types are part of the standard `java.security.SignatureSpi` interface. Since ML-DSA is a pure signature scheme that produces and verifies byte arrays, it maps perfectly onto this existing contract. No provider-specific types are needed.
+
+
+
+Source: [`src/main/java/com/benchmark/PqcBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/PqcBenchmark.java)
+
+```java
+public static class MlDsa extends PqcBenchmark {
+ @Param({
+ "ML-DSA-44",
+ "ML-DSA-65",
+ "ML-DSA-87"
+ })
+ public String algorithm;
+
+ private byte[] signature;
+
+ @Setup(Level.Trial)
+ public void setup() throws Exception {
+ initProvider();
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm, provider);
+ keyPair = kpg.generateKeyPair();
+
+ data = new byte[1024];
+ new SecureRandom().nextBytes(data);
+
+ Signature sig = Signature.getInstance(algorithm, provider);
+ sig.initSign(keyPair.getPrivate());
+ sig.update(data);
+ signature = sig.sign();
+ }
+
+ @Benchmark
+ public void sign(Blackhole bh) throws Exception {
+ Signature sig = Signature.getInstance(algorithm, provider);
+ sig.initSign(keyPair.getPrivate());
+ sig.update(data);
+ bh.consume(sig.sign());
+ }
+
+ @Benchmark
+ public void verify(Blackhole bh) throws Exception {
+ Signature sig = Signature.getInstance(algorithm, provider);
+ sig.initVerify(keyPair.getPublic());
+ sig.update(data);
+ bh.consume(sig.verify(signature));
+ }
+}
+```
+
+This code works identically for both providers. The only difference is the internal execution path: Jostle's `MLDSASignatureSpi` calls through JNI to OpenSSL's ML-DSA implementation, while Bouncy Castle's `SignatureSpi$MLDSA` executes a pure-Java implementation.
+
+**Jostle** registers ML-DSA signatures by directly mapping algorithm names to an SPI class backed by native OpenSSL.
+
+Source: [`org/openssl/jostle/jcajce/provider/ProvMLDSA.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/provider/ProvMLDSA.java#L48)
+
+
+
+The `MLDSASignatureSpi` class extends `java.security.SignatureSpi` and implements `engineSign()` and `engineVerify()` by calling native methods through the `NISelector.MLDSAServiceNI` interface.
+
+**Bouncy Castle** uses its `$Mappings` pattern to register variant-specific signature classes.
+
+Source: [`org/bouncycastle/jcajce/provider/asymmetric/MLDSA.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.java#L46)
+
+
+
+Because both providers produce and consume standard `byte[]` signatures through the standard `SignatureSpi` contract, the benchmark code requires no conditional branching or provider-specific imports for ML-DSA.
+
+#### 4.2 ML-KEM Benchmark
+
+ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism, FIPS 203) presents a fundamentally different challenge from ML-DSA. Unlike signature schemes, a KEM produces two outputs: a shared secret and an encapsulation (ciphertext). The standard JCA `KeyGenerator.generateKey()` method returns a single `SecretKey` object and has no mechanism to return the second output (the encapsulation byte array alongside the key). This section explains how each provider works around this limitation and why the benchmark code requires provider-specific branches.
+
+
+
+Source: [`src/main/java/com/benchmark/PqcBenchmark.java`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/src/main/java/com/benchmark/PqcBenchmark.java)
+
+```java
+// encapsulation benchmark
+@Benchmark
+public void encaps(Blackhole bh) throws Exception {
+ if ("Jostle".equalsIgnoreCase(providerName)) {
+ KeyGenerator kg = KeyGenerator.getInstance("ML-KEM", provider);
+ kg.init(KEMGenerateSpec.builder()
+ .withPublicKey(keyPair.getPublic())
+ .withKeySizeInBits(256)
+ .withAlgorithmName("AES")
+ .build());
+ bh.consume(kg.generateKey());
+ } else {
+ // Bouncy Castle provider
+ KeyGenerator kg = KeyGenerator.getInstance("ML-KEM", provider);
+ kg.init(new org.bouncycastle.jcajce.spec.KEMGenerateSpec(keyPair.getPublic(), "AES"),
+ new SecureRandom());
+ bh.consume(kg.generateKey());
+ }
+}
+```
+
+Both providers work around this by returning a composite object: a custom subclass of `SecretKey` that bundles the shared secret and the encapsulation together. The caller must cast the returned `SecretKey` to the provider-specific subclass to access the encapsulation.
+
+Both providers define nearly identical classes for this purpose:
+
+**Bouncy Castle:**
+
+Source: [`org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/SecretKeyWithEncapsulation.java#L10)
+
+
+
+
+**Jostle:**
+
+Source: [`org/openssl/jostle/jcajce/SecretKeyWithEncapsulation.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/SecretKeyWithEncapsulation.java#L17)
+
+
+
+#### 4.3 Provider-Specific Encaps/Decaps
+
+Beyond the return type issue, each provider also requires its own `AlgorithmParameterSpec` to initialize the `KeyGenerator`. Jostle defines `KEMGenerateSpec` and `KEMExtractSpec` with a builder pattern. Bouncy Castle defines its own `KEMGenerateSpec` and `KEMExtractSpec` classes in its `jcajce.spec` package.
+
+
+
+**Bouncy Castle's encapsulation:** [`org/bouncycastle/jcajce/spec/KEMGenerateSpec.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/spec/KEMGenerateSpec.java#L11)
+
+**Jostle's encapsulation:** [`org/openssl/jostle/jcajce/spec/KEMGenerateSpec.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/spec/KEMGenerateSpec.java#L16)
+
+**Bouncy Castle's decapsulation:** [`org/bouncycastle/jcajce/spec/KEMExtractSpec.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/spec/KEMExtractSpec.java#L11)
+
+**Jostle's decapsulation:** [`org/openssl/jostle/jcajce/spec/KEMExtractSpec.java`](https://github.com/openssl-projects/openssl-jostle/blob/main/jostle/src/main/java/org/openssl/jostle/jcajce/spec/KEMExtractSpec.java#L18)
+
+Under the hood, Jostle's `MLKEMKeyGenerator.engineGenerateKey()` calls native OpenSSL functions for the actual KEM operation.
+
+#### 4.4 Bouncy Castle's Alternative Approaches to ML-KEM
+
+##### 4.4.1 Cipher-Based Key Wrapping
+
+In addition to the `KeyGenerator` workaround, Bouncy Castle also registers ML-KEM as a `Cipher` implementation that supports key wrapping. This allows provider-agnostic code to use the standard `Cipher.wrap()`/`Cipher.unwrap()` API.
+
+Source: [`org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java#L44)
+
+
+
+The `MLKEMCipherSpi.engineWrap()` method performs KEM encapsulation internally and concatenates the encapsulation with the wrapped key into a single `byte[]`.
+
+Source: [`org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMCipherSpi.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMCipherSpi.java#L238)
+
+
+
+This approach hides the two-output problem inside the `Cipher` byte array protocol. However, Jostle does not provide a `Cipher`-based ML-KEM implementation, so this approach cannot be used for cross-provider benchmarking. Besides, the `Cipher` path introduces additional key wrapping overhead that would conflate the KEM measurement with symmetric encryption cost.
+
+##### 4.4.2 Standard KEM API (JEP 452)
+
+Java 21 introduced `javax.crypto.KEM` (JEP 452), a standard API designed specifically for key encapsulation mechanisms. This API resolves the two-output problem cleanly:
+
+```java
+KEM kem = KEM.getInstance("ML-KEM", provider);
+KEM.Encapsulator encapsulator = kem.newEncapsulator(publicKey);
+KEM.Encapsulated encapsulated = encapsulator.encapsulate();
+
+SecretKey sharedSecret = encapsulated.key();
+byte[] encapsulation = encapsulated.encapsulation();
+```
+
+The `KEM.Encapsulated` return type carries both the shared secret and the encapsulation. The `KEM.Decapsulator` provides a symmetric `decapsulate(byte[])` method that recovers the shared secret from an encapsulation.
+
+Bouncy Castle implements this standard API through `MLKEMSpi`, a class that implements `javax.crypto.KEMSpi`. It is registered in the JDK17+ multi-release overlay of the Bouncy Castle JAR.
+
+Source: [`prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMSpi.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMSpi.java#L15)
+
+
+
+The KEM service registration appears only in the JDK17+ overlay of `MLKEM.java`:
+
+Source: [`prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java`](https://github.com/bcgit/bc-java/blob/main/prov/src/main/jdk17/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java#L56)
+
+
+
+This registration is absent from the base version of `MLKEM.java` (in `prov/src/main/java/`), which only registers `KeyFactory`, `KeyPairGenerator`, `KeyGenerator`, and `Cipher` services.
+
+**Jostle** does not implement `javax.crypto.KEMSpi` at all. The `src/main/java21/` source directory in the Jostle project is empty. ML-KEM is only accessible through the `KeyGenerator` + `KEMGenerateSpec`/`KEMExtractSpec` pattern.
+
+The benchmark uses the `KeyGenerator` path because it is the only API supported by both providers, enabling a direct side-by-side comparison of the raw KEM primitive.
+
+## Build Configuration
+
+### Gradle Setup
+
+The project uses the `me.champeau.jmh` Gradle plugin (version 0.7.3) to integrate JMH into the build lifecycle.
+
+
+
+Source: [`build.gradle`](https://github.com/ngkore/jostle-bc-crypto-benchmarking/blob/main/build.gradle)
+
+```groovy
+plugins {
+ id 'java'
+ id 'me.champeau.jmh' version '0.7.3'
+}
+
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(25)
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'org.openjdk.jmh:jmh-core:1.37'
+ annotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
+}
+```
+
+The provider JARs (`bcprov-jdk18on-1.84-SNAPSHOT.jar` and `openssl-jostle-1.0-SNAPSHOT.jar`) are loaded from the `libs/` directory. JMH 1.37 is used for benchmark annotation processing and runtime.
+
+### JVM Arguments
+
+The JMH block configures JVM arguments that are passed to the forked benchmark process.
+
+```groovy
+jvmArgs = [
+ '--enable-native-access=ALL-UNNAMED',
+ '-Dorg.openssl.jostle.loader.load_name_0=interface_jni',
+ '-XX:+UseG1GC',
+ '-XX:MaxGCPauseMillis=200',
+ '-XX:+IgnoreUnrecognizedVMOptions',
+ '--add-opens=java.base/java.lang=ALL-UNNAMED',
+ '--add-opens=java.base/sun.misc=ALL-UNNAMED',
+ '-Djmh.separateClasspathJAR=true',
+ '-Djava.io.tmpdir=/tmp'
+]
+```
+
+`--enable-native-access=ALL-UNNAMED` permits the Jostle provider to call native methods without module access warnings. The `loader.load_name_0` property tells the Jostle native loader which shared library to load. `--add-opens` flags are required because JMH internally accesses non-exported JDK internals for its benchmarking infrastructure. The G1 garbage collector with a 200ms pause target provides consistent GC behavior across benchmark runs.
+
+### Result Output
+
+Benchmark results are written in JSON format to `results/results.json`. This file is consumed by the project's web-based visualizer: [https://ngkore.github.io/jostle-bc-crypto-benchmarking/](https://ngkore.github.io/jostle-bc-crypto-benchmarking/).
+
+```groovy
+resultFormat = 'JSON'
+resultsFile = project.file("${project.projectDir}/results/results.json")
+```
diff --git a/repository-conventions.md b/repository-conventions.md
index 929029f..2b91a0e 100644
--- a/repository-conventions.md
+++ b/repository-conventions.md
@@ -1,60 +1,297 @@
# Repository Conventions
-This document defines the standard conventions for writing and organizing Markdown content, blog posts, and related assets in this repository.
+This document defines the standard conventions for writing and organizing Markdown content in this repository.
## File Naming
- File names must be written in **lowercase**.
- Use **dash (-)** to separate words.
-- Exclude **common or filler words** such as `a`, `the`, `is`, etc.
-- File names should directly reflect the **blog title** (excluding the above common words).
-- Example:
- - Blog title: `The Future of Quantum Security`
- - File name: `future-of-quantum-security.md`
+- Exclude common or filler words such as `a`, `the`, `is`, etc.
+- File names should directly reflect the content title.
+- Examples:
+ - Title: `The Future of Quantum Security` → `future-of-quantum-security.md`
+ - Title: `OAI RAN Deployment` → `oai-ran.md`
-## Rules
+## Directory Structure
-- Each **main section** (e.g., `ai-ml`) has:
- - Its own `index.md`
+- Each main section (e.g., `ai-ml`, `pqc`, `ebpf`, `tutorials`) has:
+ - Its own `index.md` with a `toctree` directive
- An `images/` directory for images used in that section
-- Each **subsection** (e.g., `devops` under `ai-ml`) has:
+- Each subsection has:
- Its own subdirectory
- - An `images/` folder inside it
+ - Its own `index.md`
+ - Images in the parent `images/` directory under a subdirectory
- Do not mix images across sections or subsections.
-- If a blog belongs to a **series**, place it under a dedicated subdirectory.
-- Whenever a new Markdown file is added:
- - Add its filename (without `.md` extension) in the respective `index.md` file.
- - Maintain order by publication or logical grouping.
+- If content belongs to a series or category, place it under a dedicated subdirectory.
-### Example
-
-**Series:** Devops under AI-ML
+### Example Structure
```
-ai-ml/
+section-name/
├── index.md
├── file-1.md
├── file-2.md
├── images/
│ ├── image-1.png
│ └── image-2.png
-├── devops/
-│ ├── index.md
-│ ├── file-1.md
-│ ├── file-2.md
-│ └── images/
-│ ├── image-1.png
-│ └── image-2.png
+└── subsection/
+ ├── index.md
+ ├── file-1.md
+ └── images/
+ └── image-1.png
+```
+
+## Metadata Format
+
+Every Markdown file should include standardized metadata at the top:
+
+```markdown
+# Document Title
+
+**Author:** [Author Name](https://linkedin.com/in/username/)
+
+**Published:** Month Day, Year
+
+Brief introduction or description of the content.
+```
+
+For multiple authors:
+
+```markdown
+**Author:** [Name 1](link), [Name 2](link) & [Name 3](link)
+```
+
+## Repository Links
+
+When referencing code repositories, implementation guides, or related projects, include them in a note block at the beginning of the document:
+
+```markdown
+> **Note:**
+> Follow this repository ([github/username/repo-name](https://github.com/username/repo-name)) for implementation details.
```
## Formatting and Style
-- Use **Prettier** for consistent Markdown formatting.
+### General Guidelines
+
+- Use Prettier for consistent Markdown formatting.
- Avoid unnecessary emoji or icons.
- Keep language concise and technical.
-- Use code blocks (` ``` `) for directory structures, code samples, or examples.
-- Headings should be simple and consistent:
- - `#` for main section
- - `##` for subsections
- - `###` for details within subsections
-- **Do not use bold styling for headings.** Headings should remain plain text for clean rendering and readability.
+- Use code blocks for directory structures, code samples, or examples.
+- Do not use bold styling for headings.
+
+### Header Hierarchy
+
+Headers must follow consecutive levels - no jumps allowed:
+
+**Correct:**
+
+```markdown
+# Main Title (H1)
+
+## Section (H2)
+
+### Subsection (H3)
+
+#### Detail (H4)
+```
+
+**Incorrect:**
+
+```markdown
+# Main Title (H1)
+
+### Subsection (H3) ← Skipped H2, will cause Sphinx warning
+```
+
+**Rules:**
+
+- `#` (H1) for document title - use once at the top
+- `##` (H2) for main sections
+- `###` (H3) for subsections
+- `####` (H4) for detailed points
+- Never jump levels (H2 to H4 or H1 to H3)
+
+### Code Blocks
+
+Use appropriate language identifiers for syntax highlighting:
+
+````text
+```python
+# Python code
+```
+
+```javascript
+// JavaScript code
+```
+
+```yaml
+# YAML configuration
+```
+
+```json
+# JSON data
+```
+
+```groovy
+// Gradle build files
+```
+
+```bash
+# Shell commands/scripts
+```
+
+```console
+# Terminal output with commands
+```
+
+```diff
+# Patch/diff files
+```
+
+```c
+# C code
+```
+
+```cpp
+// C++ code
+```
+````
+
+**Important distinctions:**
+
+- Use `console` for terminal output that includes commands and their output
+- Use `bash` for shell scripts or commands only
+- Use `groovy` for Gradle build files (not `gradle`)
+- Use `diff` for patch files (not `patch`)
+
+**Examples:**
+
+Terminal output:
+
+```console
+ubuntu@server:~$ kubectl get pods
+NAME READY STATUS RESTARTS AGE
+nginx-6d4cf56db6-xyz 1/1 Running 0 2d
+```
+
+Shell script:
+
+```bash
+#!/bin/bash
+make clean
+make all
+```
+
+Configuration file:
+
+```yaml
+apiVersion: v1
+kind: Pod
+metadata:
+ name: nginx
+```
+
+### Horizontal Lines
+
+Do not use horizontal lines (`---`) in Markdown files. They can cause rendering issues in Sphinx documentation.
+
+
+
+**Incorrect:**
+
+```markdown
+## Section 1
+
+---
+
+## Section 2
+```
+
+**Correct:**
+
+```markdown
+## Section 1
+
+Content here.
+
+## Section 2
+```
+
+## Git Commits and Sign-off
+
+All commits must include a Developer Certificate of Origin (DCO) sign-off to certify that you have the right to submit the code under the project's license.
+
+### Adding Sign-off
+
+Add the `-s` or `--signoff` flag when creating commits:
+
+```bash
+git commit -s -m "Add new feature"
+```
+
+This automatically adds a `Signed-off-by` line with your name and email:
+
+```text
+Signed-off-by: Your Name
+```
+
+### Commit Message Format
+
+- Use clear, descriptive commit messages
+- Start with a verb in imperative mood (add, fix, update, remove)
+- Keep the subject line under 72 characters
+- Add details in the commit body if needed
+
+**Example:**
+
+```text
+Add PQC implementation guide
+
+This commit adds a comprehensive guide for implementing
+post-quantum cryptography in 5G core networks.
+
+Signed-off-by: Your Name
+```
+
+## Index Files and Navigation
+
+When adding new Markdown files:
+
+1. Add to the respective `index.md` file:
+
+ ````markdown
+ ```{toctree}
+ :maxdepth: 2
+
+ new-file-name
+ ```
+ ````
+
+2. Maintain logical order by publication date, category, or logical flow
+
+3. Use appropriate maxdepth:
+ - `:maxdepth: 1` for flat lists
+ - `:maxdepth: 2` for hierarchical navigation
+
+## Image Organization
+
+### Naming Conventions
+
+- Use lowercase with dashes: `image-name.png`
+- Be descriptive: `oai-ran-deployment-flow.png` not `diagram1.png`
+- Include category/section prefix: `section-topic-description.png`
+
+### Supported Formats
+
+- Diagrams/Screenshots: `.png` (preferred), `.jpg`
+- Architecture diagrams: `.png`, `.svg`
+- Web images: `.webp` (for optimization)
+
+### Image References
+
+Always use relative paths from the document location:
+
+```markdown
+
+```
diff --git a/tutorials/images/bouncy-castle/find-builds.png b/tutorials/images/bouncy-castle/find-builds.png
new file mode 100644
index 0000000..0ed9c35
Binary files /dev/null and b/tutorials/images/bouncy-castle/find-builds.png differ
diff --git a/tutorials/images/iosmcn/iosmcn-oam.png b/tutorials/images/iosmcn/iosmcn-oam.png
new file mode 100644
index 0000000..89de586
Binary files /dev/null and b/tutorials/images/iosmcn/iosmcn-oam.png differ
diff --git a/tutorials/images/iosmcn/iosmcn-ran.png b/tutorials/images/iosmcn/iosmcn-ran.png
new file mode 100644
index 0000000..f8c9fdb
Binary files /dev/null and b/tutorials/images/iosmcn/iosmcn-ran.png differ
diff --git a/tutorials/images/jostle/build-clean-skip-tests.png b/tutorials/images/jostle/build-clean-skip-tests.png
new file mode 100644
index 0000000..8c56f2a
Binary files /dev/null and b/tutorials/images/jostle/build-clean-skip-tests.png differ
diff --git a/tutorials/images/jostle/cmake-verify.png b/tutorials/images/jostle/cmake-verify.png
new file mode 100644
index 0000000..4a671e9
Binary files /dev/null and b/tutorials/images/jostle/cmake-verify.png differ
diff --git a/tutorials/images/jostle/compile-java-gradlew.png b/tutorials/images/jostle/compile-java-gradlew.png
new file mode 100644
index 0000000..f92fec0
Binary files /dev/null and b/tutorials/images/jostle/compile-java-gradlew.png differ
diff --git a/tutorials/images/jostle/dumpinfo-util.png b/tutorials/images/jostle/dumpinfo-util.png
new file mode 100644
index 0000000..281d685
Binary files /dev/null and b/tutorials/images/jostle/dumpinfo-util.png differ
diff --git a/tutorials/images/jostle/java-verify.png b/tutorials/images/jostle/java-verify.png
new file mode 100644
index 0000000..854168f
Binary files /dev/null and b/tutorials/images/jostle/java-verify.png differ
diff --git a/tutorials/images/oai/cn5g/basic-deployment-01.png b/tutorials/images/oai/cn5g/basic-deployment-01.png
new file mode 100644
index 0000000..68a35db
Binary files /dev/null and b/tutorials/images/oai/cn5g/basic-deployment-01.png differ
diff --git a/tutorials/images/oai/cn5g/basic-deployment-02.png b/tutorials/images/oai/cn5g/basic-deployment-02.png
new file mode 100644
index 0000000..3e2d64b
Binary files /dev/null and b/tutorials/images/oai/cn5g/basic-deployment-02.png differ
diff --git a/tutorials/images/oai/cn5g/oai-5gc-deployment.png b/tutorials/images/oai/cn5g/oai-5gc-deployment.png
new file mode 100644
index 0000000..8c2b867
Binary files /dev/null and b/tutorials/images/oai/cn5g/oai-5gc-deployment.png differ
diff --git a/tutorials/images/oai/ntn/ntn-arch.png b/tutorials/images/oai/ntn/ntn-arch.png
new file mode 100644
index 0000000..74d86d6
Binary files /dev/null and b/tutorials/images/oai/ntn/ntn-arch.png differ
diff --git a/tutorials/images/oai/ntn/ntn-geo-diff.png b/tutorials/images/oai/ntn/ntn-geo-diff.png
new file mode 100644
index 0000000..69e4dcc
Binary files /dev/null and b/tutorials/images/oai/ntn/ntn-geo-diff.png differ
diff --git a/tutorials/images/oai/oai-f1ap-pq-ipsec.png b/tutorials/images/oai/oai-f1ap-pq-ipsec.png
new file mode 100644
index 0000000..d214f04
Binary files /dev/null and b/tutorials/images/oai/oai-f1ap-pq-ipsec.png differ
diff --git a/tutorials/images/oai/oai-f1ap-split.png b/tutorials/images/oai/oai-f1ap-split.png
new file mode 100644
index 0000000..9c330d6
Binary files /dev/null and b/tutorials/images/oai/oai-f1ap-split.png differ
diff --git a/tutorials/images/oai/oai-ngap-split.png b/tutorials/images/oai/oai-ngap-split.png
new file mode 100644
index 0000000..f85c8ae
Binary files /dev/null and b/tutorials/images/oai/oai-ngap-split.png differ
diff --git a/tutorials/images/oai/oai-o1-adapter.png b/tutorials/images/oai/oai-o1-adapter.png
new file mode 100644
index 0000000..4781c15
Binary files /dev/null and b/tutorials/images/oai/oai-o1-adapter.png differ
diff --git a/tutorials/images/oai/ran/oai-ran-deployment-flow.png b/tutorials/images/oai/ran/oai-ran-deployment-flow.png
new file mode 100644
index 0000000..20323d3
Binary files /dev/null and b/tutorials/images/oai/ran/oai-ran-deployment-flow.png differ
diff --git a/tutorials/images/oai/ran/yaml-cpp-error.png b/tutorials/images/oai/ran/yaml-cpp-error.png
new file mode 100644
index 0000000..b091f94
Binary files /dev/null and b/tutorials/images/oai/ran/yaml-cpp-error.png differ
diff --git a/tutorials/images/osc-oam/kafka-datafile-collector-err.png b/tutorials/images/osc-oam/kafka-datafile-collector-err.png
new file mode 100644
index 0000000..5574f1a
Binary files /dev/null and b/tutorials/images/osc-oam/kafka-datafile-collector-err.png differ
diff --git a/tutorials/images/osc-oam/make-infrastructure-err.png b/tutorials/images/osc-oam/make-infrastructure-err.png
new file mode 100644
index 0000000..6fcb5ee
Binary files /dev/null and b/tutorials/images/osc-oam/make-infrastructure-err.png differ
diff --git a/tutorials/images/osc-oam/onap-cluster.png b/tutorials/images/osc-oam/onap-cluster.png
new file mode 100644
index 0000000..6b6b2c9
Binary files /dev/null and b/tutorials/images/osc-oam/onap-cluster.png differ
diff --git a/tutorials/images/osc-oam/schemaRef-err.png b/tutorials/images/osc-oam/schemaRef-err.png
new file mode 100644
index 0000000..c9de12d
Binary files /dev/null and b/tutorials/images/osc-oam/schemaRef-err.png differ
diff --git a/tutorials/images/srsran/srsran-f1ap-split.png b/tutorials/images/srsran/srsran-f1ap-split.png
new file mode 100644
index 0000000..f9b67f4
Binary files /dev/null and b/tutorials/images/srsran/srsran-f1ap-split.png differ
diff --git a/tutorials/images/srsran/srsran-open5gs.png b/tutorials/images/srsran/srsran-open5gs.png
new file mode 100644
index 0000000..8bb1a9a
Binary files /dev/null and b/tutorials/images/srsran/srsran-open5gs.png differ
diff --git a/tutorials/index.md b/tutorials/index.md
new file mode 100644
index 0000000..1be1af3
--- /dev/null
+++ b/tutorials/index.md
@@ -0,0 +1,21 @@
+# Tutorials
+
+```{toctree}
+:maxdepth: 2
+
+oai/index
+```
+
+```{toctree}
+:maxdepth: 1
+
+osc-oam
+```
+
+```{toctree}
+:maxdepth: 2
+
+iosmcn/index
+srsran/index
+pqc/index
+```
diff --git a/tutorials/iosmcn/index.md b/tutorials/iosmcn/index.md
new file mode 100644
index 0000000..ae6d2f9
--- /dev/null
+++ b/tutorials/iosmcn/index.md
@@ -0,0 +1,9 @@
+# IOSMCN
+
+```{toctree}
+:maxdepth: 1
+
+iosmcn-ran
+iosmcn-o1-adapter
+iosmcn-oam
+```
diff --git a/tutorials/iosmcn/iosmcn-o1-adapter.md b/tutorials/iosmcn/iosmcn-o1-adapter.md
new file mode 100644
index 0000000..bce9bda
--- /dev/null
+++ b/tutorials/iosmcn/iosmcn-o1-adapter.md
@@ -0,0 +1,405 @@
+# IOSMCN O1 Adapter
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/) & [Ronak Kanthaliya](https://www.linkedin.com/in/ronak-kanthaliya-127003194/)
+
+**Published:** November 19, 2025
+
+Please refer to [OAI O1 Adapter repository](https://gitlab.eurecom.fr/oai/o1-adapter) for the official documentation.
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/IOSMCN-OAI](https://github.com/ngkore/IOSMCN-OAI). Please refer to the original repository for the most up-to-date information.
+
+## 1. System Preparation
+
+### Clone repository
+
+```bash
+git clone https://github.com/ios-mcn/ios-mcn-releases.git
+cd ios-mcn-releases/Agartala/v0.4.0/RAN/source-code/
+tar -xzf ios-mcn-ran-0.4.0.iosmcn.ran.tar.gz
+mv ios-mcn-ran-0.4.0.iosmcn.ran ~/ran
+cd ~/ran/o1-adapter/
+```
+
+### Set Execute Permissions
+
+Set execute permissions for all scripts and executables.
+
+```bash
+chmod -R +x .
+```
+
+## 2. Update Configuration
+
+Update configuration before building as this is common for deployment methods — **docker**, **source**, or **Makefile**.
+
+### For docker or source-based builds:
+
+Modify `docker/config/config.json`:
+
+1. Set host IP address and telnet server IP address to your host machine’s IP address.
+2. Align telnet ports
+ - `docker/scripts/servertest.py` uses 9090
+ - `config.json` defaults to 9091
+ - Change `config.json` to 9090
+3. Update the `ves.url` parameter to `http://:8080/eventListener/v7` to connect with the SMO VES Collector (IOSMCN based).
+4. Update the `docker/scripts/servertest.py` to include the missing O1 configuration from CU. Either copy it from `docker/scripts/cu_servertest.py` or use this [servertest.py](https://github.com/ngkore/IOSMCN-OAI/blob/main/servertest.py).
+
+### For Makefile-based builds:
+
+1. Add missing `"gnb-cu-id": 0` under the `info` section of `integration/config/config.json.template`:
+
+ ```json
+ "info": {
+ "gnb-du-id": 0,
+ "gnb-cu-id": 0, // this line to be added
+ "cell-local-id": 0,
+ "node-id": "gNB-Eurecom-5GNRBox-00001",
+ "location-name": "MountPoint 05, Rack 234-17, Room 234, 2nd Floor, Körnerstraße 7, 10785 Berlin, Germany, Europe, Earth, Solar-System, Universe",
+ "managed-by": "ManagementSystem=O-RAN-SC-ONAP-based-SMO",
+ "managed-element-type": "NodeB",
+ "model": "nr-softmodem",
+ "unit-type": "gNB"
+ }
+ ```
+
+2. Update the `integration/.env` file as the values got further propagated to `integration/config/config.json`.
+ - Rename the incorrect variable from `OAI_OAI_TELNET_HOST` to `OAI_TELNET_HOST`
+ - Add a new variable `VES_PORT` and set a default value of `8080`.
+ - `VES_PORT=8080`
+ - Change `VES_COLLECTOR_URL` value from `https://${VES_FQDN}/eventListener/v7` to `http://${VES_IP}:${VES_PORT}/eventListener/v7`
+
+3. Change the `ports` parameter to `9090` in `integration/docker-compose-telnet.yaml`.
+
+## 3. Deployment Method 1: Docker-Based
+
+### 3.1 Build the Adapter Container
+
+Supported flags for `build-adapter.sh`:
+
+| Flag | Description |
+| ------------ | ------------------------------------------------------------------------ |
+| `--dev` | Build development container (debug tools included; not production-ready) |
+| `--adapter` | Build production adapter container |
+| `--no-cache` | Disable docker build cache |
+
+Build the adapter:
+
+```bash
+./build-adapter.sh --adapter
+```
+
+> **Tip:** Rebuild after config changes using the same command.
+
+### 3.2 Start Telnet Server (Separate Terminal)
+
+The adapter uses a telnet-based interface to communicate with the RAN.
+Install dependencies:
+
+```bash
+sudo apt update
+sudo apt install python3-pip
+pip3 install telnetlib3
+```
+
+Run the telnet test server:
+
+```bash
+cd ran/o1-adapter/docker/scripts/
+python3 servertest.py
+```
+
+### 3.3 Start the gNB Adapter
+
+```bash
+./start-adapter.sh --adapter
+```
+
+At this point, if the configuration is correct, the adapter will be listening for NETCONF clients and ready to communicate with the gNB’s telnet server.
+
+## 4. Deployment Method 2: Source-Based Build
+
+### 4.1 System Setup
+
+```bash
+sudo apt update && sudo apt upgrade -y
+sudo apt install -y tzdata build-essential git cmake pkg-config unzip wget \
+ libpcre2-dev zlib1g-dev libssl-dev autoconf libtool python3-pip
+
+sudo apt install -y --no-install-recommends psmisc unzip wget openssl \
+ openssh-client vsftpd openssh-server
+
+pip3 install telnetlib3
+```
+
+### 4.2 NETCONF User Preparation
+
+Create a dedicated system user:
+
+```bash
+sudo adduser --system netconf
+echo "netconf:netconf!" | sudo chpasswd
+```
+
+SSH directory:
+
+```bash
+sudo mkdir -p /home/netconf/.ssh
+sudo chmod 700 /home/netconf/.ssh
+```
+
+### 4.3 Configure OpenSSH for netconf User
+
+Edit `/etc/ssh/sshd_config`:
+
+```bash
+sudo vim /etc/ssh/sshd_config
+```
+
+Append:
+
+```
+Match User netconf
+ ChrootDirectory /
+ X11Forwarding no
+ AllowTcpForwarding no
+ ForceCommand internal-sftp -d /ftp
+```
+
+Restart SSH:
+
+```bash
+sudo systemctl restart ssh
+```
+
+### 4.4 Configure vsftpd
+
+Prepare directories:
+
+```bash
+sudo mkdir -p /ftp
+sudo chown -R netconf:nogroup /ftp
+sudo mkdir -p /var/run/vsftpd/empty
+sudo mkdir -p /run/sshd
+```
+
+Edit `/etc/vsftpd.conf`:
+
+```bash
+sudo vim /etc/vsftpd.conf
+```
+
+Ensure the following settings are present:
+
+```
+listen=YES
+listen_ipv6=NO
+anonymous_enable=NO
+local_enable=YES
+write_enable=YES
+local_umask=022
+dirmessage_enable=YES
+use_localtime=YES
+xferlog_enable=YES
+connect_from_port_20=YES
+xferlog_std_format=YES
+chroot_local_user=YES
+allow_writeable_chroot=YES
+secure_chroot_dir=/var/run/vsftpd/empty
+pam_service_name=vsftpd
+rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
+rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
+ssl_enable=NO
+userlist_enable=YES
+userlist_file=/etc/vsftpd.userlist
+userlist_deny=NO
+```
+
+Add `netconf` to `/etc/vsftpd.userlist`:
+
+```bash
+sudo vim /etc/vsftpd.userlist
+```
+
+Insert:
+
+```
+netconf
+```
+
+### 4.5 Prepare O1 Adapter Source Tree
+
+The adapter expects configuration inside `src/`:
+
+```bash
+cp -r docker/config/ src/
+```
+
+### 4.6 Install NETCONF Dependencies
+
+```bash
+cd o1-adapter/docker/scripts/
+sudo ./netconf_dep_install.sh
+sudo ldconfig
+```
+
+Run hostkey merge utilities:
+
+```bash
+sudo /usr/local/share/netopeer2/merge_hostkey.sh
+sudo /usr/local/share/netopeer2/merge_config.sh
+```
+
+Fetch and install YANG models:
+
+```bash
+mkdir -p yang
+./get-yangs.sh
+sudo ./install-yangs.sh
+```
+
+### 4.7 Build the gNB Adapter
+
+```bash
+cd ../../src/
+./build.sh
+```
+
+To rebuild after config changes:
+
+```bash
+rm gnb-adapter
+./build.sh
+```
+
+Set environment variables:
+
+```bash
+export TERM=xterm-256color
+```
+
+### 4.8 Start Supporting Processes
+
+#### Terminal 1: NETCONF Server
+
+```bash
+sudo netopeer2-server -d -v3 -t 60
+```
+
+#### Terminal 2: Telnet Test Server
+
+```bash
+cd ran/o1-adapter/docker/scripts/
+python3 servertest.py
+# Optional CU test:
+# python3 cu_servertest.py
+```
+
+#### Terminal 3: gNB Adapter
+
+```bash
+cd ran/o1-adapter/src/
+sudo ./gnb-adapter
+```
+
+## 5. Deployment Method 3: Makefile-Based Deployment
+
+Through `Makefile`, we can deploy the **four different combinations** depending on what you want to integrate with:
+
+1. **Adapter alone** (no gNB, no SMO)
+2. **Adapter + telnet test server** (fake gNB)
+3. **Adapter + SMO** (real O-RAN SMO integration, but still needs a telnet server or real gNB)
+4. **Adapter + SMO + telnet test server** (full O-RAN environment with fake gNB)
+
+### 5.1 Launch Adapter Using Makefile
+
+#### Standalone Adapter
+
+Build the gNB Adapter container:
+
+```bash
+make run-o1-oai-adapter
+```
+
+Start the gNB Adapter:
+
+```bash
+./start-adapter.sh --adapter
+```
+
+Install dependencies for telnet server:
+
+```bash
+sudo apt update
+sudo apt install python3-pip
+pip3 install telnetlib3
+```
+
+Run the telnet test server on a separate terminal:
+
+```bash
+cd ran/o1-adapter/docker/scripts/
+python3 servertest.py
+```
+
+#### Adapter + Telnet Test Server
+
+Build the gNB Adapter and telnet server containers:
+
+```bash
+make run-o1-adapter-telnet
+```
+
+Start the gNB Adapter:
+
+```bash
+./start-adapter.sh --adapter
+```
+
+> **Important:** The below methods for SMO are not tested and still expected to fail. See [iosmcn-osc-oam.md](https://github.com/ngkore/IOSMCN-OAI/blob/main/iosmcn-osc-oam.md) for more details on SMO deployment and integration.
+
+#### Adapter + SMO
+
+```bash
+make run-o1-oai-adapter-smo
+```
+
+#### Adapter + SMO + Telnet Server
+
+```bash
+make run-o1-oai-adapter-smo-telnet
+```
+
+### 5.2 Teardown
+
+```bash
+make teardown
+```
+
+## 6. Testing the gNB Adapter with OAI gNB
+
+### 6.1 Start gNB Adapter
+
+Docker or makefile-based:
+
+```bash
+./start-adapter.sh --adapter
+```
+
+Source:
+
+```bash
+cd o1-adapter/src/
+sudo ./gnb-adapter
+```
+
+### 6.2 Start OAI gNB (RFsim + O1 Telnet)
+
+```bash
+sudo ./nr-softmodem \
+ -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf \
+ --rfsim \
+ --gNBs.[0].min_rxtxtime 6 \
+ --telnetsrv \
+ --telnetsrv.listenport 9090 \
+ --telnetsrv.shrmod o1
+```
diff --git a/tutorials/iosmcn/iosmcn-oam.md b/tutorials/iosmcn/iosmcn-oam.md
new file mode 100644
index 0000000..0ff5e4e
--- /dev/null
+++ b/tutorials/iosmcn/iosmcn-oam.md
@@ -0,0 +1,297 @@
+# IOSMCN OSC OAM
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/) & [Ronak Kanthaliya](https://www.linkedin.com/in/ronak-kanthaliya-127003194/)
+
+**Published:** November 19, 2025
+
+Please refer to [OSC OAM repository](https://gerrit.o-ran-sc.org/r/admin/repos/oam) for the official documentation.
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/IOSMCN-OAI](https://github.com/ngkore/IOSMCN-OAI). Please refer to the original repository for the most up-to-date information.
+
+## 1. Repository Setup
+
+```bash
+git clone https://github.com/ios-mcn/ios-mcn-releases.git
+cd ios-mcn-releases/Agartala/v0.4.0/SMO/source-code
+tar -xf iosmcn.agartala.v0.4.0.smo.source.tar
+mv iosmcn.agartala.v0.4.0.smo.source ~/smo/
+cd ~/smo/
+```
+
+Extract SMO components:
+
+```bash
+for f in *.tar.gz; do tar -xzf "$f"; done
+
+mkdir tar_files
+mv *.tar.gz tar_files/
+```
+
+## 2. Documentation Viewer (Optional)
+
+The SMO repo includes Sphinx-based documentation. To view it locally, set up a Docker-based Sphinx viewer.
+
+```bash
+cd ~/ios-mcn-releases/Agartala/v0.4.0/SMO
+```
+
+Create `Dockerfile`:
+
+```Dockerfile
+FROM alpine:latest
+WORKDIR /
+RUN mkdir -p /Sphinx/build
+
+RUN apk add --no-cache python3 py3-pip make git
+RUN pip3 install git+https://github.com/sphinx-doc/sphinx --break-system-packages && \
+ pip3 install sphinx-autobuild --break-system-packages
+
+ADD ./documentation/requirements-docs.txt /Sphinx
+
+RUN pip3 install -r /Sphinx/requirements-docs.txt --break-system-packages
+
+CMD sphinx-autobuild -b html -n --host 0.0.0.0 --port 80 /Sphinx/source /Sphinx/build
+```
+
+Create `docker-compose.yaml`:
+
+```yaml
+version: "3"
+
+services:
+ service_doc:
+ container_name: service_doc
+ build: ./
+ volumes:
+ - ./documentation:/Sphinx/source
+ - ./build:/Sphinx/build
+ ports:
+ - 9999:80
+```
+
+### Launch the documentation service
+
+```bash
+docker compose up -d
+```
+
+Access documentation at:
+
+```
+http://:9999
+```
+
+Stop the viewer:
+
+```bash
+docker compose down
+```
+
+## 3. OAM Component Deployment
+
+This is the docker based deployment method for OAM component.
+
+1. Go to the OAM directory:
+
+```bash
+cd ~/smo/oam-0.4.0.iosmcn.smo.oam/
+chmod -R +x .
+```
+
+2. Install dependencies:
+
+```bash
+sudo apt install openjdk-25-jre-headless jq
+```
+
+Check `keytool`:
+
+```bash
+keytool --help
+```
+
+3. Identify hostname and IP:
+
+```bash
+ubuntu@ubuntu:~$ hostname
+ubuntu
+ubuntu@ubuntu:~$ hostname -I
+10.228.98.131 172.17.0.1 192.168.70.129 172.18.0.1
+```
+
+From this:
+
+- `` = `ubuntu`
+- `` = `10.228.98.131`
+- `` = `.` = `ubuntu.iosmcn.org` (domain `iosmcn.org` in this example, you can use any domain as long as it is a local deployment)
+
+4. Update `/etc/hosts`:
+
+```bash
+sudo vim /etc/hosts
+```
+
+Add this template (generic form):
+
+```
+127.0.0.1 localhost
+127.0.1.1
+
+# SMO OAM development system
+
+ gateway.
+ identity.
+ messages.
+ kafka-bridge.
+ odlux.oam.
+ flows.oam.
+ tests.oam.
+ controller.dcn.
+ ves-collector.dcn.
+ minio.
+ redpanda.
+ nrtcp.
+```
+
+example:
+
+```
+127.0.0.1 localhost
+127.0.1.1 ubuntu
+
+# SMO OAM development system
+10.228.98.131 ubuntu.iosmcn.org
+10.228.98.131 gateway.ubuntu.iosmcn.org
+10.228.98.131 identity.ubuntu.iosmcn.org
+10.228.98.131 messages.ubuntu.iosmcn.org
+10.228.98.131 kafka-bridge.ubuntu.iosmcn.org
+10.228.98.131 odlux.oam.ubuntu.iosmcn.org
+10.228.98.131 flows.oam.ubuntu.iosmcn.org
+10.228.98.131 tests.oam.ubuntu.iosmcn.org
+10.228.98.131 controller.dcn.ubuntu.iosmcn.org
+10.228.98.131 ves-collector.dcn.ubuntu.iosmcn.org
+10.228.98.131 minio.ubuntu.iosmcn.org
+10.228.98.131 redpanda.ubuntu.iosmcn.org
+10.228.98.131 nrtcp.ubuntu.iosmcn.org
+```
+
+5. Update `.env` to correct the images path and versions:
+
+```bash
+sed -i -e 's/ios-mcn-smo/ios-mcn/g' -e 's/1.2.2/1.2.1/g' .env
+```
+
+6. Run environment adaptation:
+
+```bash
+# python3 adapt_to_environment -i -d
+python3 adapt_to_environment -i 10.228.98.131 -d ubuntu.iosmcn.org
+```
+
+7. Bring up OAM stack
+
+```bash
+./docker-setup.sh
+```
+
+This orchestrates the OAM-related Docker services using your `/etc/hosts` mappings.
+
+8. Access OAM services
+
+| Service | Username | Password | URL |
+| ------------------------ | -------- | ------------------------------------------- | ----------------------- |
+| Grafana | admin | admin | `http://:3000` |
+| InfluxDB | admin | mySuP3rS3cr3tT0keN | `http://:8086` |
+| Redpanda | - | - | `http://:8780` |
+| ODLUX | admin | Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U | `http://:8085` |
+| Non-RT RIC Control Panel | - | - | `http://:8088` |
+
+### O1-Adapter Integration
+
+To connect the OAI O1-Adapter with SMO OAM, we need to adjust the VES URL. However, these changes are already incorporated in the [iosmcn-oai-o1-adapter](https://github.com/ngkore/IOSMCN-OAI/blob/main/iosmcn-oai-o1-adapter.md), but for clarity, here are the steps again for each deployment method. Do the following changes to match VES Configurations.
+
+```yaml
+# VES Collector Configuration
+IP:
+Port: 8080
+Version: v7
+Protocol: http
+Username: sample1
+Password: sample1
+```
+
+**For Docker-Based Deployment Method:**
+
+Update the `ves.url` parameter in `o1-adapter/docker/config/config.json` to `http://:8080/eventListener/v7`.
+
+**For Source-Based Deployment Method:**
+
+Update the `ves.url` parameter in `o1-adapter/src/config/config.json` to `http://:8080/eventListener/v7`.
+
+**For Makefile-Based Deployment Method:**
+
+Update the `o1-adapter/integration/.env` file:
+
+- Add a new variable `VES_PORT` and set a default value of `8080`.
+ - `VES_PORT=8080`
+- Change `VES_COLLECTOR_URL` value from `https://${VES_FQDN}/eventListener/v7` to `http://${VES_IP}:${VES_PORT}/eventListener/v7`
+
+## 4. FOCoM Component Deployment (optional)
+
+1. Navigate to FOCoM:
+
+```bash
+cd ../focom-0.4.0.iosmcn.smo.focom/
+```
+
+2. Install required tools:
+
+```bash
+sudo apt update
+sudo apt install git curl make net-tools pipx python3-venv
+```
+
+3. Install Ansible via `pipx`:
+
+```bash
+pipx install --include-deps ansible
+pipx ensurepath
+echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
+source ~/.bashrc
+```
+
+4. Enter the Docker automation directory:
+
+```bash
+cd docker/
+```
+
+5. Create `docker.yaml`:
+
+```yaml
+- hosts: all
+ become: true
+ roles:
+ - ansible-role-docker
+```
+
+6. Create `inventory.ini`:
+
+```ini
+[servers]
+localhost ansible_connection=local
+```
+
+7. Run the Docker installation role
+
+```bash
+ansible-playbook -i inventory.ini docker.yaml
+```
+
+This prepares Docker on the host using the Ansible role.
+
+## 5. Near-RT RIC and Unified Dashboard
+
+Will be added soon.
diff --git a/tutorials/iosmcn/iosmcn-ran.md b/tutorials/iosmcn/iosmcn-ran.md
new file mode 100644
index 0000000..c529c9e
--- /dev/null
+++ b/tutorials/iosmcn/iosmcn-ran.md
@@ -0,0 +1,165 @@
+# IOSMCN RAN
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/) & [Ronak Kanthaliya](https://www.linkedin.com/in/ronak-kanthaliya-127003194/)
+
+**Published:** November 19, 2025
+
+Please refer to [OAI](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_nrUE.md?ref_type=heads) for the official documentation.
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/IOSMCN-OAI](https://github.com/ngkore/IOSMCN-OAI). Please refer to the original repository for the most up-to-date information.
+
+## System Preparation
+
+### Install General Dependencies
+
+Install the necessary build tools and libraries for OAI.
+
+```bash
+sudo apt update
+sudo apt install -y autoconf automake build-essential ccache cmake cpufrequtils \
+ doxygen ethtool g++ git inetutils-tools libboost-all-dev libncurses-dev \
+ libusb-1.0-0 libusb-1.0-0-dev libusb-dev python3-dev python3-mako \
+ python3-numpy python3-requests python3-scipy python3-setuptools python3-ruamel.yaml \
+ libcap-dev libblas-dev liblapacke-dev libatlas-base-dev
+```
+
+### Install Scope Dependencies
+
+Required for the `nrscope` visualization tool.
+
+```bash
+sudo apt install -y libforms-dev libforms-bin
+```
+
+### Install `yaml-cpp` from source
+
+> **Important:** Do **not** install `yaml-cpp` using `sudo apt install libyaml-cpp-dev` as it is known to cause build failures (see below). Build it from source instead.
+
+
+
+```bash
+cd
+git clone https://github.com/jbeder/yaml-cpp.git
+cd yaml-cpp
+mkdir build && cd build
+cmake .. -DYAML_BUILD_SHARED_LIBS=ON
+make -j$(nproc)
+sudo make install
+sudo ldconfig
+cd
+rm -r yaml-cpp/
+```
+
+### Install UHD (Only for USRP Hardware)
+
+If you are planning to test it in **RF Simulator mode only**, then you can skip this. For **USRP Hardware**, this is mandatory. Either follow the [UHD Build Guide](https://files.ettus.com/manual/page_build_guide.html) or use the commands below:
+
+```bash
+git clone https://github.com/EttusResearch/uhd.git ~/uhd
+cd ~/uhd
+git checkout v4.7.0.0
+cd host
+mkdir build
+cd build
+cmake ../
+make -j $(nproc)
+# make test # Optional
+sudo make install
+sudo ldconfig
+sudo uhd_images_downloader
+```
+
+## Build IOSMCN OAI RAN
+
+### Clone Repository
+
+```bash
+git clone https://github.com/ios-mcn/ios-mcn-releases.git
+cd ios-mcn-releases/Agartala/v0.4.0/RAN/source-code/
+tar -xzf ios-mcn-ran-0.4.0.iosmcn.ran.tar.gz
+mv ios-mcn-ran-0.4.0.iosmcn.ran ~/ran
+cd ~/ran/openairinterface5g/
+```
+
+### Set Execute Permissions
+
+Set execute permissions for all scripts and executables.
+
+```bash
+chmod -R +x .
+```
+
+### Install dependencies
+
+```bash
+cd cmake_targets/
+./build_oai -I
+```
+
+### Build gNB and nrUE
+
+This command builds both the gNB and UE with support for the OAI Scope and Ninja build system.
+
+```bash
+# replace SIMU with USRP if you want to build it for USRP hardware
+./build_oai -w SIMU --ninja --nrUE --gNB --build-lib "nrscope" -C
+
+# to build telnet management server - needed to communicate with O1 adapter
+./build_oai --build-lib telnetsrv
+```
+
+## Configuration Changes
+
+Edit the gNB configuration file located at `openairinterface5g/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf` and make the following changes:
+
+- Change the `gNB_ID` to `0xe00`.
+- Remove th `sd` value from the `plmn_list` section. (remove `sd = 0x000000;`).
+- Change `amf_ip_address` to `192.168.70.132`.
+- Change the `GNB_IPV4_ADDRESS_FOR_NG_AMF` and `GNB_IPV4_ADDRESS_FOR_NGU` to `192.168.70.129/24`.
+
+## Run gNB (RFsim Mode)
+
+```bash
+cd ~/ran/openairinterface5g/cmake_targets/ran_build/build
+
+sudo ./nr-softmodem \
+ -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf \
+ --rfsim \
+ --gNBs.[0].min_rxtxtime 6
+```
+
+Append the following flags to add telnet server support:
+
+```bash
+--telnetsrv \
+--telnetsrv.listenport 9090 \
+--telnetsrv.shrmod o1
+```
+
+> **Note:** For USRP models, please refer [ngkore/OAI-RAN](https://github.com/ngkore/OAI-RAN) repository.
+
+## Run nrUE (RFsim Mode)
+
+Run this command on the same host as the OAI gNB.
+
+```bash
+cd ~/ran/openairinterface5g/cmake_targets/ran_build/build
+
+sudo ./nr-uesoftmodem \
+ -r 106 \
+ --numerology 1 \
+ --band 78 \
+ -C 3619200000 \
+ --rfsim \
+ --uicc0.imsi 001010000000001
+```
+
+### End-to-End Connectivity Test
+
+Ping test from the UE host to the CN5G
+
+```bash
+ping 192.168.70.135 -I oaitun_ue1
+```
diff --git a/tutorials/oai/index.md b/tutorials/oai/index.md
new file mode 100644
index 0000000..e9ca462
--- /dev/null
+++ b/tutorials/oai/index.md
@@ -0,0 +1,13 @@
+# OAI
+
+```{toctree}
+:maxdepth: 1
+
+oai-cn5g
+oai-ran
+oai-ngap-split
+oai-f1ap-split
+oai-o1-adapter
+oai-nr-5g-ntn
+oai-f1ap-pq-ipsec
+```
diff --git a/tutorials/oai/oai-cn5g.md b/tutorials/oai/oai-cn5g.md
new file mode 100644
index 0000000..942bacb
--- /dev/null
+++ b/tutorials/oai/oai-cn5g.md
@@ -0,0 +1,193 @@
+# OAI CN5G
+
+**Author:** [Aditya Koranga](https://www.linkedin.com/in/aditya-koranga/) & [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** March 09, 2023
+
+This repository provides guidance for deploying the OpenAirInterface (OAI) 5G Core (CN5G).
+
+
+
+For the official documentation, please refer to the [OAI 5G Core Deployment Guide](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md?ref_type=heads).
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI-CN5G](https://github.com/ngkore/OAI-CN5G). Please refer to the original repository for the most up-to-date information.
+
+## 1\. System Preparation
+
+**Perform these steps regardless of which deployment method you choose.**
+
+### 1.1 Hardware Requirements
+
+Ensure your machine meets the following specifications:
+
+| Component | Minimum Specification | Recommended Specification |
+| :---------- | :-------------------- | ------------------------- |
+| **OS** | Ubuntu 22.04 LTS | Ubuntu 22.04 LTS |
+| **CPU** | 4 cores | 8 cores x86_64 @ 3.5 GHz |
+| **RAM** | 16 GB | 32 GB |
+| **Network** | 1 Interface | 2 Interfaces |
+
+### 1.2 Install Pre-requisites & Docker
+
+Update your system and install the necessary tools and Docker engine.
+
+```bash
+# 1. Update and install basic tools
+sudo apt update
+sudo apt install -y git net-tools putty unzip ca-certificates curl
+
+# 2. Add Docker's official GPG key
+sudo install -m 0755 -d /etc/apt/keyrings
+sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+sudo chmod a+r /etc/apt/keyrings/docker.asc
+
+# 3. Add the repository to Apt sources
+echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+
+# 4. Install Docker packages
+sudo apt update
+sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-compose
+
+# 5. Add current user to docker group (avoids using sudo for docker)
+sudo usermod -a -G docker $(whoami)
+```
+
+> **Tip:** Log out and back in to apply group changes or run `su - $USER` to start a new session.
+
+### 1.3 Enable IP Forwarding
+
+This is required for the core network functions to route traffic correctly.
+
+```bash
+sudo sysctl net.ipv4.conf.all.forwarding=1
+sudo iptables -P FORWARD ACCEPT
+```
+
+## 2\. Deployment Options
+
+Choose **one** of the following methods.
+
+### Option A: Quick Start (Docker Compose)
+
+Use this method for a fast, pre-packaged tutorial setup.
+
+**1. Download Configuration Files**
+
+```bash
+wget -O ~/oai-cn5g.zip https://gitlab.eurecom.fr/oai/openairinterface5g/-/archive/develop/openairinterface5g-develop.zip?path=doc/tutorial_resources/oai-cn5g
+unzip ~/oai-cn5g.zip
+mv ~/openairinterface5g-develop-doc-tutorial_resources-oai-cn5g/doc/tutorial_resources/oai-cn5g ~/oai-cn5g
+rm -r ~/openairinterface5g-develop-doc-tutorial_resources-oai-cn5g ~/oai-cn5g.zip
+```
+
+**2. Pull Images & Start**
+
+```bash
+cd ~/oai-cn5g
+docker compose pull
+docker compose up -d
+```
+
+**Stop the Network**
+
+```bash
+cd ~/oai-cn5g
+docker compose down
+```
+
+### Option B: Advanced Source Deployment (python wrapper)
+
+Use this method if you need to patch configurations, develop features, or use the OAI python wrapper scripts.
+
+
+
+**1. Clone the Repository**
+
+```bash
+git clone https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-fed.git
+cd oai-cn5g-fed/
+git checkout v2.0.1
+```
+
+**2. Sync Components**
+
+This pulls the submodule references for the specific network functions.
+
+```bash
+./scripts/syncComponents.sh
+```
+
+**3. Apply Configuration Patches**
+
+You need to update the `nrf` config and the `mysql` database initialization to ensure consistent user profiles.
+
+**Method A: Apply via Git (Recommended)**
+
+If you have the patch file ready:
+
+```bash
+git apply patch-files/oai-cn5g-fed.patch
+```
+
+**Method B: Manual Edit**
+
+If you want to manually edit the files, make the following changes:
+
+```diff
+diff --git a/docker-compose/conf/basic_nrf_config.yaml b/docker-compose/conf/basic_nrf_config.yaml
+index c55f7c9..acbf4d2 100644
+--- a/docker-compose/conf/basic_nrf_config.yaml
++++ b/docker-compose/conf/basic_nrf_config.yaml
+@@ -222,7 +222,7 @@ upf:
+ support_features:
+ enable_bpf_datapath: no # If "on": BPF is used as datapath else simpleswitch is used, DEFAULT= off
+ enable_snat: yes # If "on": Source natting is done for UE, DEFAULT= off
+- remote_n6_gw: localhost # Dummy host since simple-switch does not use N6 GW
++ remote_n6_gw: 127.0.0.1 # Dummy host since simple-switch does not use N6 GW
+ smfs:
+ - host: oai-smf # To be used for PFCP association in case of no-NRF
+ upf_info:
+diff --git a/docker-compose/database/oai_db2.sql b/docker-compose/database/oai_db2.sql
+index 35cb957..f854dc1 100755
+--- a/docker-compose/database/oai_db2.sql
++++ b/docker-compose/database/oai_db2.sql
+@@ -77,6 +77,7 @@ CREATE TABLE `AccessAndMobilitySubscriptionData` (
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ INSERT INTO `AccessAndMobilitySubscriptionData` (`ueid`, `servingPlmnid`, `nssai`) VALUES
++('208950000000031', '20895','{\"defaultSingleNssais\": [{\"sst\": 1, \"sd\": \"0xffffff\"}]}'),
+ ('208950000000125', '20895','{\"defaultSingleNssais\": [{\"sst\": 1, \"sd\": \"1\"}]}'),
+ ('208950000000126', '20895','{\"defaultSingleNssais\": [{\"sst\": 1, \"sd\": \"1\"}]}'),
+ ('208950000000127', '20895','{\"defaultSingleNssais\": [{\"sst\": 1, \"sd\": \"1\"}]}'),
+@@ -311,7 +312,7 @@ CREATE TABLE `SessionManagementSubscriptionData` (
+ --
+
+ INSERT INTO `SessionManagementSubscriptionData` (`ueid`, `servingPlmnid`, `singleNssai`, `dnnConfigurations`) VALUES
+-('208950000000031', '20895', '{\"sst\": 222, \"sd\": \"123\"}','{\"default\":{\"pduSessionTypes\":{ \"defaultSessionType\": \"IPV4\"},\"sscModes\": {\"defaultSscMode\": \"SSC_MODE_1\"},\"5gQosProfile\": {\"5qi\": 6,\"arp\":{\"priorityLevel\": 1,\"preemptCap\": \"NOT_PREEMPT\",\"preemptVuln\":\"NOT_PREEMPTABLE\"},\"priorityLevel\":1},\"sessionAmbr\":{\"uplink\":\"100Mbps\", \"downlink\":\"100Mbps\"},\"staticIpAddress\":[{\"ipv4Addr\": \"12.1.1.4\"}]}}');
++('208950000000031', '20895', '{\"sst\": 1, \"sd\": \"0xffffff\"}','{\"oai\":{\"pduSessionTypes\":{ \"defaultSessionType\": \"IPV4\"},\"sscModes\": {\"defaultSscMode\": \"SSC_MODE_1\"},\"5gQosProfile\": {\"5qi\": 6,\"arp\":{\"priorityLevel\": 1,\"preemptCap\": \"NOT_PREEMPT\",\"preemptVuln\":\"NOT_PREEMPTABLE\"},\"priorityLevel\":1},\"sessionAmbr\":{\"uplink\":\"100Mbps\", \"downlink\":\"100Mbps\"},\"staticIpAddress\":[{\"ipv4Addr\": \"12.1.1.4\"}]}}');
+ INSERT INTO `SessionManagementSubscriptionData` (`ueid`, `servingPlmnid`, `singleNssai`, `dnnConfigurations`) VALUES
+ ('208950000000032', '20895', '{\"sst\": 222, \"sd\": \"123\"}','{\"default\":{\"pduSessionTypes\":{ \"defaultSessionType\": \"IPV4\"},\"sscModes\": {\"defaultSscMode\": \"SSC_MODE_1\"},\"5gQosProfile\": {\"5qi\": 6,\"arp\":{\"priorityLevel\": 1,\"preemptCap\": \"NOT_PREEMPT\",\"preemptVuln\":\"NOT_PREEMPTABLE\"},\"priorityLevel\":1},\"sessionAmbr\":{\"uplink\":\"100Mbps\", \"downlink\":\"100Mbps\"}}}');
+ -- --------------------------------------------------------
+```
+
+**4. Deploy the Core**
+
+Use the Python wrapper script to start the "Basic" scenario (AMF, SMF, UPF, NRF).
+
+```bash
+cd docker-compose
+python3 core-network.py --type start-basic --scenario 1
+```
+
+
+
+
+**Stop the Core**
+
+```bash
+python3 core-network.py --type stop-basic --scenario 1
+```
+
+> **Warning:** Do not abruptly power off your VM. Sudden shutdowns can corrupt NRF registration state and cause inconsistent network-function discovery. Always stop the core properly before shutting down the machine.
+
+Follow the [ngkore/OAI-RAN](https://github.com/ngkore/OAI-RAN) guide to build and configure the RAN components (gNB and nrUE).
diff --git a/tutorials/oai/oai-f1ap-pq-ipsec.md b/tutorials/oai/oai-f1ap-pq-ipsec.md
new file mode 100644
index 0000000..fae7b2f
--- /dev/null
+++ b/tutorials/oai/oai-f1ap-pq-ipsec.md
@@ -0,0 +1,263 @@
+# OAI F1AP PQ-IPSec
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/) & [Aditya Koranga](https://www.linkedin.com/in/aditya-koranga/)
+
+**Published:** December 16, 2025
+
+This repository contains instructions and configuration files for establishing a Post-Quantum (PQ) secure IPsec tunnel using StrongSwan between an OpenAirInterface (OAI) Central Unit (CU) and Distributed Unit (DU) over the F1AP interface (Split Option 2).
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI-F1AP-PQ-IPSec](https://github.com/ngkore/OAI-F1AP-PQ-IPSec). Please refer to the original repository for the most up-to-date information.
+
+**Related Documentation**
+
+- **Core Network:** Refer to [ngkore/OAI-CN5G](https://github.com/ngkore/OAI-CN5G) for OAI Core Network deployment (Option A).
+- **RAN Configuration:** Refer to [ngkore/OAI_F1AP_Split](https://github.com/ngkore/OAI_F1AP_Split) for building and configuring the gNB and nrUE in disaggregated mode on different servers.
+
+## Overview
+
+This solution implements an IPsec VPN in tunnel mode using StrongSwan 6.0.2 linked against OpenSSL 3.6.0 on Ubuntu 22.04. It supports configurable authentication via X.509 certificates or Pre-Shared Keys (PSK).
+
+
+
+The implementation supports two specific security modes:
+
+1. **Classical Mode:**
+ - Authentication: RSA-4096 certificates.
+ - Key Exchange: Standard RSA key exchange groups.
+
+2. **Post-Quantum Mode:**
+ - Authentication: RSA-4096 certificates (for identity) or PSK.
+ - Key Exchange: ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) groups.
+
+> **Note:** StrongSwan v6.0.2 does not currently support Post-Quantum certificates (e.g., ML-DSA). Support is currently under development in the StrongSwan experimental branches.
+
+## Prerequisites
+
+- **Operating System:** Ubuntu 22.04 LTS.
+- **Privileges:** Root or sudo access is required for all commands.
+- **Hardware:** Two separate physical or virtual machines (one acting as the CU/Server, one as the DU/Client).
+
+## System Preparation
+
+Perform the following steps on **both** the Server (CU) and Client (DU) machines.
+
+### 1\. Install Build Dependencies
+
+Update the package list and install the necessary development tools and libraries.
+
+```bash
+sudo apt-get update
+sudo apt-get install -y \
+ build-essential git wget curl autoconf automake libtool pkg-config \
+ gettext bison flex gperf libgmp-dev libcurl4-openssl-dev \
+ libsystemd-dev libpam0g-dev libldap2-dev libsqlite3-dev \
+ libmysqlclient-dev libpq-dev libxml2-dev libjson-c-dev libcap-dev \
+ libiptc-dev libnm-dev libxtables-dev libip4tc-dev libip6tc-dev \
+ libnetfilter-conntrack-dev iproute2 iputils-ping net-tools tcpdump \
+ python3 vim checkinstall zlib1g-dev perl-modules-5.34 perl-doc
+```
+
+### 2\. Build and Install OpenSSL 3.6.x
+
+Download and compile the specific OpenSSL version required for PQ support.
+
+```bash
+cd /tmp
+wget https://github.com/openssl/openssl/releases/download/openssl-3.6.0/openssl-3.6.0.tar.gz
+tar -xzf openssl-3.6.0.tar.gz
+cd openssl-3.6.0
+
+./config --prefix=/usr/local --openssldir=/usr/local/ssl
+make -j$(nproc)
+sudo make install
+sudo ldconfig
+```
+
+Verify the installation using the local library path:
+
+```bash
+LD_LIBRARY_PATH=/usr/local/lib64 openssl version
+```
+
+> **Warning:** Do not modify the global system configuration (e.g., `/etc/ld.so.conf.d/`) to force the system to use OpenSSL 3.6.0 globally. This will break system tools such as SSH and APT that rely on the default system OpenSSL version.
+
+### 3\. Build and Install StrongSwan 6.0.x
+
+Compile StrongSwan with specific flags enabled for systemd, OpenSSL, and Machine Learning (ML) algorithm support.
+
+```bash
+cd /tmp
+wget --no-check-certificate https://download.strongswan.org/strongswan-6.0.2.tar.bz2
+tar xjf strongswan-6.0.2.tar.bz2
+cd strongswan-6.0.2
+
+PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig" \
+CPPFLAGS="-I/usr/local/include" \
+LDFLAGS="-L/usr/local/lib64 -L/usr/local/lib -Wl,-rpath,/usr/local/lib64 -Wl,-rpath,/usr/local/lib" \
+./configure \
+ --prefix=/usr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
+ --with-systemdsystemunitdir=/lib/systemd/system \
+ --disable-defaults \
+ --enable-charon --enable-systemd --enable-vici --enable-swanctl \
+ --enable-ikev2 --enable-openssl --enable-ml --enable-nonce \
+ --enable-random --enable-pem --enable-x509 --enable-pkcs1 \
+ --enable-pkcs8 --enable-pki --enable-pubkey --enable-socket-default \
+ --enable-kernel-netlink --enable-updown --enable-resolve --enable-silent-rules
+
+make -j$(nproc)
+sudo make install
+sudo ldconfig
+```
+
+### 4\. Create Configuration Directories
+
+Create the necessary directory structure for `swanctl`.
+
+```bash
+sudo mkdir -p /etc/swanctl/{conf.d,x509ca,x509,private,rsa,ecdsa,pkcs8,pkcs12,pubkey}
+sudo chmod 700 /etc/swanctl/{private,rsa,ecdsa,pkcs8,pkcs12}
+sudo chown -R root:root /etc/swanctl
+```
+
+### 5\. Enable IP Forwarding
+
+Configure the kernel to allow IP forwarding and disable redirects.
+
+```bash
+sudo sysctl -w net.ipv4.ip_forward=1
+sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
+sudo sysctl -w net.ipv4.conf.all.send_redirects=0
+
+# Persist settings
+echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
+sudo sysctl -p
+```
+
+### 6\. PKI Generation
+
+This repository includes a script to generate the required Public Key Infrastructure (PKI). Clone it on both machines but run this on the server only, and then distribute the resulting files (`/pki/`) to client.
+
+```bash
+git clone https://github.com/ngkore/OAI-F1AP-PQ-IPSec
+cd OAI-F1AP-PQ-IPSec/
+chmod +x generate-pki.sh
+./generate-pki.sh
+```
+
+To share the generated files with the client, you can use `scp` or any secure file transfer method.
+
+```bash
+scp -r /pki/ @:/home//OAI-F1AP-PQ-IPSec//
+```
+
+> **Note:**
+>
+> - `IPSEC_MODE`: Set to `classical` or `pq-support`.
+> - `AUTH_MODE`: Set to `certs` for certificate-based authentication or `psk` for pre-shared key authentication.
+
+## Server Configuration (CU)
+
+### 1\. Install Certificates (Certificate Mode Only)
+
+Transfer the generated files to the server's `swanctl` directories.
+
+```bash
+sudo cp /pki/cacerts/ca-cert.pem /etc/swanctl/x509ca/
+sudo cp /pki/certs/server-cert.pem /etc/swanctl/x509/
+sudo cp /pki/private/server-key.pem /etc/swanctl/private/
+sudo cp /pki/certs/client-cert.pem /etc/swanctl/x509/
+sudo cp /pki/private/client-key.pem /etc/swanctl/private/
+```
+
+### 2\. Configure Swanctl
+
+Edit the `server.conf` file to match your physical network addresses (`local_addrs` and `remote_addrs`), then install it.
+
+```bash
+sudo cp /conf//server.conf /etc/swanctl/swanctl.conf
+```
+
+### 3\. Network Interface Setup
+
+Create a dummy interface to serve as the tunnel endpoint.
+
+```bash
+sudo ip link add name tunnel0 type dummy
+sudo ip addr add 10.1.0.1/32 dev tunnel0
+sudo ip link set tunnel0 up
+```
+
+### 4\. Start Service
+
+```bash
+sudo systemctl enable strongswan
+sudo systemctl start strongswan
+sudo swanctl --load-all
+```
+
+## Client Configuration (DU)
+
+### 1\. Install Certificates (Certificate Mode Only)
+
+```bash
+sudo cp /pki/cacerts/ca-cert.pem /etc/swanctl/x509ca/
+sudo cp /pki/certs/server-cert.pem /etc/swanctl/x509/
+sudo cp /pki/private/server-key.pem /etc/swanctl/private/
+sudo cp /pki/certs/client-cert.pem /etc/swanctl/x509/
+sudo cp /pki/private/client-key.pem /etc/swanctl/private/
+```
+
+### 2\. Configure Swanctl
+
+Edit the `client.conf` file to match your physical network addresses (`local_addrs` and `remote_addrs`), then install it.
+
+```bash
+sudo cp /conf//client.conf /etc/swanctl/swanctl.conf
+```
+
+### 3\. Start Service
+
+```bash
+sudo systemctl enable strongswan
+sudo systemctl start strongswan
+sudo swanctl --load-all
+```
+
+> **Note:** The tunnel should establish automatically upon loading the configuration. If it does not, force initiation with: `sudo swanctl --initiate --child tunnel-to-corp`
+
+## Verification and OAI Integration
+
+### Verify IPsec Status
+
+On both nodes, check the Security Associations (SAs):
+
+```bash
+sudo swanctl --list-sas
+```
+
+_Expected Output:_ Status should indicate `ESTABLISHED` and `INSTALLED`.
+
+### Connectivity Test
+
+From the Client (DU), ping the Server's (CU) tunnel IP:
+
+```bash
+ping -c 3 10.1.0.1
+```
+
+### OAI Configuration Update
+
+To utilize the encrypted tunnel, you must update the OAI configuration files:
+
+1. **CU Configuration:** Bind the F1 interface to `10.1.0.1` (the `tunnel0` interface IP).
+2. **DU Configuration:** Configure the remote CU address as `10.1.0.1`.
+
+This ensures F1AP traffic flows through the `tunnel0` interface, where it will be intercepted and encrypted by the StrongSwan policies.
+
+## Tutorial Video
+
+[Implementing PQ-IPSec between OpenAirInterface (OAI) CU-DU using StrongSwan and OpenSSL](https://youtu.be/HZgdzAZV6s4?si=Cyn1zjQtXhqRvpLi)
diff --git a/tutorials/oai/oai-f1ap-split.md b/tutorials/oai/oai-f1ap-split.md
new file mode 100644
index 0000000..2595350
--- /dev/null
+++ b/tutorials/oai/oai-f1ap-split.md
@@ -0,0 +1,258 @@
+# OAI F1AP Split
+
+**Author:** [Shankar Malik](https://www.linkedin.com/in/evershalik/)
+
+**Published:** November 30, 2025
+
+This guide explains how to deploy and run the OAI gNB Central Unit (CU) and Distributed Unit (DU) in **F1 split mode**.
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI_F1AP_Split](https://github.com/ngkore/OAI_F1AP_Split). Please refer to the original repository for the most up-to-date information.
+
+You can run the CU/DU setup in two ways:
+
+1. **On the same server**
+2. **On different servers**
+
+For a complete end-to-end (E2E) test, you will need:
+
+- UE (OAI nrUE)
+- gNB DU
+- gNB CU
+- OAI 5G Core (CN5G)
+
+Before starting, ensure the OAI 5G Core is installed and configured:
+
+**OAI CN5G Setup**
+Follow: [https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md)
+
+Once the core is running, proceed to deploying the CU and DU.
+
+For more details on F1 interface configuration, refer to: [https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/F1AP/F1-design.md#how-to-run](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/F1AP/F1-design.md#how-to-run)
+
+## 1. Running CU and DU on the Same Server
+
+
+Expand steps
+
+### Prerequisites: Build UHD from Source (Optional – only for real SDR testing)
+
+If you want to test with real radios (SDRs) instead of RFsim, build UHD:
+
+```bash
+# https://files.ettus.com/manual/page_build_guide.html
+sudo apt install -y autoconf automake build-essential ccache cmake cpufrequtils doxygen ethtool g++ git inetutils-tools libboost-all-dev libncurses-dev libusb-1.0-0 libusb-1.0-0-dev libusb-dev python3-dev python3-mako python3-numpy python3-requests python3-scipy python3-setuptools python3-ruamel.yaml
+
+git clone https://github.com/EttusResearch/uhd.git ~/uhd
+cd ~/uhd
+git checkout v4.8.0.0
+cd host
+mkdir build
+cd build
+cmake ../
+make -j $(nproc)
+make test # optional
+sudo make install
+sudo ldconfig
+sudo uhd_images_downloader
+```
+
+### Build OAI gNB and nrUE
+
+```bash
+git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
+cd ~/openairinterface5g
+git checkout develop
+
+cd ~/openairinterface5g/cmake_targets
+./build_oai -I
+
+sudo apt install -y libforms-dev libforms-bin
+
+cd ~/openairinterface5g/cmake_targets
+./build_oai -w SIMU --ninja --nrUE --gNB --build-lib "nrscope" -C
+```
+
+> **Note:** Use `-w USRP` instead of `-w SIMU` if building for real SDR testing.
+
+### Configure CU and DU
+
+Patch files (Same server):
+[https://github.com/ngkore/OAI_CU_DU_Split/tree/main/patch_files/same-server](https://github.com/ngkore/OAI_CU_DU_Split/tree/main/patch_files/same-server)
+
+```bash
+cd ~/openairinterface5g
+wget https://raw.githubusercontent.com/ngkore/OAI_CU_DU_Split/refs/heads/main/patch_files/same-server/cu.patch
+wget https://raw.githubusercontent.com/ngkore/OAI_CU_DU_Split/refs/heads/main/patch_files/same-server/du.patch
+git apply cu.patch
+git apply du.patch
+```
+
+### Run CU and DU
+
+**Run CU**
+
+```bash
+cd ~/openairinterface5g
+sudo cmake_targets/ran_build/build/nr-softmodem -O ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf
+```
+
+**Run DU**
+
+```bash
+cd ~/openairinterface5g
+sudo cmake_targets/ran_build/build/nr-softmodem -O ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf --rfsim
+```
+
+_Remove `--rfsim` if running with USRP._
+
+### Run OAI nrUE
+
+**With USRP B210**
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ue-fo-compensation -E --uicc0.imsi 001010000000001
+```
+
+**With RFsim**
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --uicc0.imsi 001010000000001 --rfsim
+```
+
+### E2E Connectivity Test (RFsim Mode)
+
+UE → CN5G
+
+```bash
+ping 192.168.70.135 -I oaitun_ue1
+```
+
+UE → Internet
+
+```bash
+ping 8.8.8.8 -I oaitun_ue1
+```
+
+
+
+## 2. Running CU and DU on Different Servers
+
+
+Expand steps
+
+### Topology
+
+- **Server 1:** CN5G + gNB CU
+- **Server 2:** gNB DU + UE
+
+### Build UHD (Optional, only on DU server)
+
+Same instructions as above. Only required for real SDR testing.
+
+### Build OAI gNB and nrUE on _both_ servers
+
+```bash
+git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
+cd ~/openairinterface5g
+git checkout develop
+
+cd ~/openairinterface5g/cmake_targets
+./build_oai -I
+sudo apt install -y libforms-dev libforms-bin
+
+cd ~/openairinterface5g/cmake_targets
+./build_oai -w SIMU --ninja --nrUE --gNB --build-lib "nrscope" -C
+```
+
+_Use `-w USRP` on DU server if using SDR._
+
+### Configure CU and DU
+
+Patch files:
+[https://github.com/ngkore/OAI_CU_DU_Split/tree/main/patch_files/different-servers](https://github.com/ngkore/OAI_CU_DU_Split/tree/main/patch_files/different-servers)
+
+```bash
+cd ~/openairinterface5g
+
+# On CU server
+wget https://raw.githubusercontent.com/ngkore/OAI_CU_DU_Split/refs/heads/main/patch_files/different-servers/cu.patch
+git apply cu.patch
+
+# On DU server
+wget https://raw.githubusercontent.com/ngkore/OAI_CU_DU_Split/refs/heads/main/patch_files/different-servers/du.patch
+git apply du.patch
+```
+
+Update IPs as shown in the patch:
+
+**CU config**
+
+```bash
+local_s_address = "CU_Server_IP";
+remote_s_address = "DU_Server_IP";
+```
+
+**DU config**
+
+```bash
+local_n_address = "DU_Server_IP";
+remote_n_address = "CU_Server_IP";
+```
+
+### Run CU and DU
+
+**CU server**
+
+```bash
+cd ~/openairinterface5g
+sudo cmake_targets/ran_build/build/nr-softmodem -O ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf
+```
+
+**DU server**
+
+```bash
+cd ~/openairinterface5g
+sudo cmake_targets/ran_build/build/nr-softmodem -O ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf --rfsim
+```
+
+_Remove `--rfsim` if running with USRP._
+
+### Run OAI nrUE (on DU server)
+
+**With USRP B210**
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ue-fo-compensation -E --uicc0.imsi 001010000000001
+```
+
+**With RFsim**
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --uicc0.imsi 001010000000001 --rfsim
+```
+
+### E2E Connectivity Test (RFsim Mode)
+
+UE → CN5G
+
+```bash
+ping 192.168.70.135 -I oaitun_ue1
+```
+
+UE → Internet
+
+```bash
+ping 8.8.8.8 -I oaitun_ue1
+```
+
+
+
+## Logs
+
+Sample logs for CU and DU are available [here](https://github.com/ngkore/OAI_F1AP_Split/tree/main/logs)
diff --git a/tutorials/oai/oai-ngap-split.md b/tutorials/oai/oai-ngap-split.md
new file mode 100644
index 0000000..8fc8422
--- /dev/null
+++ b/tutorials/oai/oai-ngap-split.md
@@ -0,0 +1,208 @@
+# OAI NGAP Split
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/) & [Shankar Malik](https://www.linkedin.com/in/evershalik/)
+
+**Published:** January 01, 2026
+
+This document outlines the procedures for deploying the OpenAirInterface (OAI) 5G Core Network and gNodeB (gNB) on separate servers (disaggregated mode).
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI_NGAP_Split](https://github.com/ngkore/OAI_NGAP_Split). Please refer to the original repository for the most up-to-date information.
+
+## 1. Prerequisites
+
+Before proceeding, ensure the following independent setups are complete:
+
+- **Server 1 (Core):** Deploy the OAI 5G Core by following the instructions at [ngkore/OAI-CN5G](https://github.com/ngkore/OAI-CN5G).
+- **Server 2 (RAN):** Deploy the OAI gNB by following the instructions at [ngkore/OAI-RAN](https://github.com/ngkore/OAI-RAN).
+
+> **Note:** Ensure that the configurations are synchronized between the Core and gNB configuration files as per their respective repository instructions.
+
+## 2. Network Configuration
+
+To enable communication between the physically separated gNB and 5G Core, you must update the gNB configuration and establishing a route to the Core's internal Docker network.
+
+### Step 2.1: Update gNB Configuration
+
+Locate and edit the gNB configuration file on the RAN server.
+**File Path:** `/openairinterface5g/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf` (or your specific configuration file).
+
+
+
+Update the **Network Interfaces** section to bind to the RAN Server's physical IP address:
+
+```diff
+diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf
+index 7f750d7259..d6479024f3 100644
+--- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf
++++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf
+@@ -160,8 +160,8 @@ gNBs =
+
+ NETWORK_INTERFACES :
+ {
+- GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
+- GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
++ GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.156.217.169/24";
++ GNB_IPV4_ADDRESS_FOR_NGU = "10.156.217.169/24";
+ GNB_PORT_FOR_S1U = 2152; # Spec 2152
+ };
+```
+
+### Step 2.2: Configure Static Routing (RAN Server)
+
+The gNB needs to know how to reach the AMF container (subnet `192.168.70.0/24`) which resides inside the Core server.
+
+
+
+Run the following command on the **RAN VM**:
+
+```bash
+# Syntax: sudo ip route add via dev
+sudo ip route add 192.168.70.0/24 via 10.156.217.189 dev ens1
+```
+
+**Verification:**
+
+Attempt to ping the AMF container from the RAN VM:
+
+```bash
+ping 192.168.70.132
+```
+
+- **If the ping succeeds:** Proceed to **Section 4**.
+- **If the ping fails:** Proceed to **Section 3** to troubleshoot firewall issues.
+
+## 3. Firewall Troubleshooting and Resolution
+
+If network connectivity fails between the gNB and the Core, the issue is typically caused by strict firewall rules on the **Core Server** that block external traffic from reaching the internal Docker network.
+
+### Step 3.1: Diagnosis
+
+To identify if packets are being blocked, inspect the active firewall rules on the **Core VM** by running:
+
+```bash
+sudo nft list ruleset
+```
+
+Review the output for `drop` rules in the `filter` and `raw` tables. A typical blocking configuration will look like this:
+
+```bash
+ubuntu@ubuntu-01:~/oai-cn5g$ sudo nft list ruleset
+...
+table ip filter {
+ chain DOCKER {
+ # The Isolation Rule
+ iifname != "docker0" oifname "docker0" counter packets 0 bytes 0 drop
+ iifname != "oai-cn5g" oifname "oai-cn5g" counter packets 0 bytes 0 drop
+ }
+...
+table ip raw {
+ chain PREROUTING {
+ type filter hook prerouting priority raw; policy accept;
+ # The Explicit IP Drop Rules
+ iifname != "oai-cn5g" ip daddr 192.168.70.131 counter packets 0 bytes 0 drop
+ iifname != "oai-cn5g" ip daddr 192.168.70.132 counter packets 3 bytes 252 drop
+ iifname != "oai-cn5g" ip daddr 192.168.70.133 counter packets 0 bytes 0 drop
+ ...
+ }
+}
+```
+
+### Understanding the Blocking Rules:
+
+1. **Network Isolation Rule (`table ip filter`)**
+
+ > `iifname != "oai-cn5g" oifname "oai-cn5g" counter packets 0 bytes 0 drop`
+
+- If a packet attempts to exit to the OAI network (`oifname "oai-cn5g"`) but did **not** originate from inside that same network (`iifname != "oai-cn5g"`), drop it.
+- This rule prevents external traffic (coming from your physical interface, e.g., `ens2`) from talking to the containers.
+
+2. **Explicit IP Drop Rules (`table ip raw`)**
+
+ > `iifname != "oai-cn5g" ip daddr 192.168.70.x counter packets 0 bytes 0 drop`
+
+- If the ingress interface is not `oai-cn5g` and the destination IP matches a specific container (e.g., `192.168.70.132`), drop the packet immediately.
+- You can confirm this is the cause by looking at the counter: `counter packets 3 bytes 252 drop`. The non-zero packet count confirms that your ICMP/Ping requests were actively rejected by this rule.
+
+### Step 3.2: Resolution
+
+We cannot simply delete the Docker isolation rule as it may be recreated. Instead, we can bypass it and flush the specific blocking rules in the raw table.
+
+
+
+**1. Bypass Docker Isolation**
+
+Add a rule to the `DOCKER-USER` chain to explicitly accept traffic from your physical interface (`ens2`) destined for the OAI network.
+
+```bash
+# Syntax: sudo iptables -I DOCKER-USER -i -o oai-cn5g -j ACCEPT
+sudo iptables -I DOCKER-USER -i ens2 -o oai-cn5g -j ACCEPT
+```
+
+**2. Flush Blocking Rules in the Raw Table**
+
+The `PREROUTING` chain in the `raw` table contains explicit drop rules for the container IPs.
+
+_Option A: Delete All (Recommended)_
+
+This command will delete every rule inside the `PREROUTING` chain of the `raw` table.
+
+```bash
+sudo nft flush chain ip raw PREROUTING
+```
+
+_Option B: Delete One by One (Granular)_
+
+If you prefer to delete only specific rules (e.g. only for the AMF and UPF IPs):
+
+1. List rules with their handle IDs:
+
+```bash
+sudo nft -a list table ip raw
+```
+
+2. Delete the specific rule using its handle (e.g., handle 8):
+
+```bash
+# syntax: sudo nft delete rule ip raw PREROUTING handle
+sudo nft delete rule ip raw PREROUTING handle 8
+```
+
+> **Important:** These rules are managed by the OAI-CN5G wrapper scripts or Docker. If you restart the OAI core containers, these rules may reappear, and you will need to re-apply these fixes.
+
+## 4. Execution and Testing
+
+Try to ping the AMF container from the RAN VM again. Once network connectivity is verified, proceed to run the OAI components.
+
+### Step 4.1: Run the gNB (RFsimulator Mode)
+
+Use the RFsimulator for initial validation before deploying with real radio hardware.
+
+
+
+On the **RAN Server**:
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --rfsim
+```
+
+### Step 4.2: Run the OAI nrUE
+
+To test traffic flow, run the OAI-nrUE on the same host as the gNB.
+
+
+
+On the **RAN Server** (new terminal window):
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --uicc0.imsi 001010000000001 --rfsim
+```
+
+If successful, the UE should attach to the network, and you will see signaling logs on both the AMF (Core Server) and gNB (RAN Server). Now, you can test with real radios and UE as per [ngkore/OAI-RAN](https://github.com/ngkore/OAI-RAN).
+
+## Tutorial Video
+
+[OpenAirInterface 5G Core and gNB Disaggregated Deployment (NGAP Split)](https://youtu.be/W1YAWWhsOco?si=nfC_VbSzK1pSnTIa)
diff --git a/tutorials/oai/oai-nr-5g-ntn.md b/tutorials/oai/oai-nr-5g-ntn.md
new file mode 100644
index 0000000..7d7e12c
--- /dev/null
+++ b/tutorials/oai/oai-nr-5g-ntn.md
@@ -0,0 +1,113 @@
+# OAI NR-5G NTN
+
+**Author:** [Shankar Malik](https://www.linkedin.com/in/evershalik/)
+
+**Published:** December 07, 2025
+
+This repository provides a simple, repeatable guide for deploying and testing **Non-Terrestrial Network (NTN)** support in OpenAirInterface (OAI) using RFsimulated GEO and LEO satellite scenarios.
+
+
+
+Official reference documentation: [https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/RUNMODEM.md#how-to-run-a-ntn-configuration](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/RUNMODEM.md#how-to-run-a-ntn-configuration)
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI-5G-NR-NTN](https://github.com/ngkore/OAI-5G-NR-NTN). Please refer to the original repository for the most up-to-date information.
+
+## Overview
+
+This setup runs in **simulated NTN mode** using RFsimulator.
+For a complete NTN End-to-End (E2E) test, you will need:
+
+- OAI nrUE
+- RFsimulator (NTN channel simulation)
+- OAI gNB
+- OAI 5G Core (CN5G)
+
+Two satellite orbit modes are supported:
+
+1. **GEO (Geostationary Orbit)**
+2. **LEO (Low Earth Orbit)** - coming soon
+
+## Prerequisites
+
+Before proceeding, ensure the **OAI 5G Core (CN5G)** is installed and running.
+
+Setup guide:
+[https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_CN5G.md)
+
+## 1. GEO Satellite Simulation
+
+
+Click to expand steps
+
+### Step 1 - Build OAI gNB and nrUE
+
+```bash
+git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
+cd ~/openairinterface5g
+git checkout develop
+
+cd ~/openairinterface5g/cmake_targets
+./build_oai -I
+
+sudo apt install -y libforms-dev libforms-bin
+
+cd ~/openairinterface5g/cmake_targets
+./build_oai -w SIMU --ninja --nrUE --gNB --build-lib "nrscope" -C
+```
+
+### Step 2 - Apply NTN Configuration
+
+Configuration details reference:
+[https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/RUNMODEM.md#gnb](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/RUNMODEM.md#gnb)
+
+Download and apply the required patch:
+
+```bash
+cd ~/openairinterface5g
+wget https://raw.githubusercontent.com/ngkore/OAI_NTN_RFSim/refs/heads/main/patch_files/ntn-geo.patch
+git apply ntn-geo.patch
+```
+
+This patch includes all required NTN parameter changes:
+
+
+
+### Step 3 - Run the gNB and UE
+
+#### Run OAI gNB
+
+```bash
+cd cmake_targets
+sudo ./ran_build/build/nr-softmodem -O ../ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf --rfsim --rfsimulator.prop_delay 238.74
+```
+
+#### Run OAI nrUE (FDD, 5MHz BW, 15 kHz SCS)
+
+```bash
+cd cmake_targets
+sudo ./ran_build/build/nr-uesoftmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --band 254 -C 2488400000 --CO -873500000 -r 25 --numerology 0 --ssb 60 --rfsim --rfsimulator.prop_delay 238.74
+```
+
+### Step 4 - Test End-to-End Connectivity
+
+#### UE → CN5G
+
+```bash
+ping 192.168.70.135 -I oaitun_ue1
+```
+
+
+
+## 2. LEO Satellite Simulation
+
+Coming soon.
+
+## Logs
+
+Sample logs for GEO simulation are available [here](https://github.com/ngkore/OAI-5G-NR-NTN/tree/main/logs)
+
+> **Note:** This guide focuses on simulation with RFsimulator. For real SDR NTN testing, additional configuration steps will be added later. Tested on OAI `develop` branch.
+
+## Tutorial Video
+
+[Non-Terrestrial Networks Tutorial Guide](https://youtu.be/lkfi7Ve4IV4?si=MT_ODfYkD09eqjLl)
diff --git a/tutorials/oai/oai-o1-adapter.md b/tutorials/oai/oai-o1-adapter.md
new file mode 100644
index 0000000..c48853a
--- /dev/null
+++ b/tutorials/oai/oai-o1-adapter.md
@@ -0,0 +1,367 @@
+# OAI O1 Adapter
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** December 24, 2025
+
+This repository contains the [OAI O1 Adapter](https://gitlab.eurecom.fr/oai/o1-adapter) deployment guide for integrating OAI gNB with an O-RAN-SC OAM components via the O1 interface.
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI-O1-Adapter](https://github.com/ngkore/OAI-O1-Adapter). Please refer to the original repository for the most up-to-date information.
+
+## Pre-requisites
+
+Install Docker:
+
+```bash
+# 1. Update and install basic tools
+sudo apt update
+sudo apt install -y git net-tools putty unzip ca-certificates curl make
+
+# 2. Add Docker's official GPG key
+sudo install -m 0755 -d /etc/apt/keyrings
+sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+sudo chmod a+r /etc/apt/keyrings/docker.asc
+
+# 3. Add the repository to Apt sources
+echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+
+# 4. Install Docker packages
+sudo apt update
+sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-compose
+
+# 5. Add current user to docker group (avoids using sudo for docker)
+sudo usermod -a -G docker $(whoami)
+```
+
+> **Tip:** Log out and back in to apply group changes or run `su - $USER` to start a new session.
+
+## 1. Repository Preparation
+
+```bash
+git clone https://gitlab.eurecom.fr/oai/o1-adapter.git
+cd o1-adapter
+```
+
+## 2. Update Configuration
+
+Update configuration before building as this is common for deployment methods — **docker**, **source**, or **Makefile**.
+
+### For docker, source and makefile-based builds:
+
+Modify `docker/config/config.json`:
+
+1. Set host IP address (`host` under `network` section) and telnet server IP address (`host` under `telnet` section) to host IP.
+2. Update the `ves.url` parameter to `http://:/eventListener/v7` to connect with the SMO VES Collector.
+
+### For Makefile-based builds only:
+
+1. Update the `integration/.env` file as the values got further propagated to `integration/config/config.json`.
+ - Rename the incorrect variable from `OAI_OAI_TELNET_HOST` to `OAI_TELNET_HOST`
+ - Add a new variable `VES_PORT` and set the value to from OSC OAM deployment.
+ - `VES_PORT=`
+ - Change `VES_COLLECTOR_URL` value from `https://${VES_FQDN}/eventListener/v7` to `http://${VES_IP}:${VES_PORT}/eventListener/v7`
+ - Update `OAI_TELNET_PORT` value to `9091`
+
+> **Note:** You can get the value of `` from the [ngkore/OSC-OAM](https://github.com/ngkore/OSC-OAM?tab=readme-ov-file#exposing-ves-collector-for-oai-o1-adapter).
+
+## 3. Deployment Method 1: Docker-Based
+
+### 3.1 Build the Adapter Container
+
+Supported flags for `build-adapter.sh`:
+
+| Flag | Description |
+| ------------ | ------------------------------------------------------------------------ |
+| `--dev` | Build development container (debug tools included; not production-ready) |
+| `--adapter` | Build production adapter container |
+| `--no-cache` | Disable docker build cache |
+
+Build the adapter:
+
+```bash
+./build-adapter.sh --adapter
+```
+
+> **Tip:** Rebuild after config changes using the same command.
+
+### 3.2 Start Telnet Server (Separate Terminal)
+
+The adapter uses a telnet-based interface to communicate with the RAN.
+Install dependencies:
+
+```bash
+sudo apt update
+sudo apt install python3-pip
+pip3 install telnetlib3
+```
+
+Run the telnet test server:
+
+```bash
+cd o1-adapter/docker/scripts/
+python3 servertest.py
+```
+
+### 3.3 Start the gNB Adapter
+
+```bash
+./start-adapter.sh --adapter
+```
+
+At this point, if the configuration is correct, the adapter will be listening for NETCONF clients and ready to communicate with the gNB’s telnet server.
+
+## 4. Deployment Method 2: Source-Based Build
+
+### 4.1 System Setup
+
+```bash
+sudo apt update && sudo apt upgrade -y
+sudo apt install -y tzdata build-essential git cmake pkg-config unzip wget \
+ libpcre2-dev zlib1g-dev libssl-dev autoconf libtool python3-pip
+
+sudo apt install -y --no-install-recommends psmisc unzip wget openssl \
+ openssh-client vsftpd openssh-server
+
+pip3 install telnetlib3
+```
+
+### 4.2 NETCONF User Preparation
+
+Create a dedicated system user:
+
+```bash
+sudo adduser --system netconf
+echo "netconf:netconf!" | sudo chpasswd
+```
+
+SSH directory:
+
+```bash
+sudo mkdir -p /home/netconf/.ssh
+sudo chmod 700 /home/netconf/.ssh
+```
+
+### 4.3 Configure OpenSSH for netconf User
+
+Edit `/etc/ssh/sshd_config`:
+
+```bash
+sudo vim /etc/ssh/sshd_config
+```
+
+Append:
+
+```
+Match User netconf
+ ChrootDirectory /
+ X11Forwarding no
+ AllowTcpForwarding no
+ ForceCommand internal-sftp -d /ftp
+```
+
+Restart SSH:
+
+```bash
+sudo systemctl restart ssh
+```
+
+### 4.4 Configure vsftpd
+
+Prepare directories:
+
+```bash
+sudo mkdir -p /ftp
+sudo chown -R netconf:nogroup /ftp
+sudo mkdir -p /var/run/vsftpd/empty
+sudo mkdir -p /run/sshd
+```
+
+Edit `/etc/vsftpd.conf`:
+
+```bash
+sudo vim /etc/vsftpd.conf
+```
+
+Ensure the following settings are present:
+
+```
+listen=YES
+listen_ipv6=NO
+anonymous_enable=NO
+local_enable=YES
+write_enable=YES
+local_umask=022
+dirmessage_enable=YES
+use_localtime=YES
+xferlog_enable=YES
+connect_from_port_20=YES
+xferlog_std_format=YES
+chroot_local_user=YES
+allow_writeable_chroot=YES
+secure_chroot_dir=/var/run/vsftpd/empty
+pam_service_name=vsftpd
+rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
+rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
+ssl_enable=NO
+userlist_enable=YES
+userlist_file=/etc/vsftpd.userlist
+userlist_deny=NO
+```
+
+Add `netconf` to `/etc/vsftpd.userlist`:
+
+```bash
+sudo vim /etc/vsftpd.userlist
+```
+
+Insert:
+
+```
+netconf
+```
+
+### 4.5 Prepare O1 Adapter Source Tree
+
+The adapter expects configuration inside `src/`:
+
+```bash
+cp -r docker/config/ src/
+```
+
+### 4.6 Install NETCONF Dependencies
+
+```bash
+cd o1-adapter/docker/scripts/
+sudo ./netconf_dep_install.sh
+sudo ldconfig
+```
+
+Run hostkey merge utilities:
+
+```bash
+sudo /usr/local/share/netopeer2/merge_hostkey.sh
+sudo /usr/local/share/netopeer2/merge_config.sh
+```
+
+Fetch and install YANG models:
+
+```bash
+mkdir -p yang
+./get-yangs.sh
+sudo ./install-yangs.sh
+```
+
+### 4.7 Build the gNB Adapter (Source)
+
+```bash
+cd ../../src/
+./build.sh
+```
+
+Rebuild after config changes:
+
+```bash
+rm gnb-adapter
+./build.sh
+```
+
+### 4.8 Start Supporting Processes
+
+#### Terminal 1: NETCONF Server
+
+```bash
+sudo netopeer2-server -d -v3 -t 60
+```
+
+#### Terminal 2: Telnet Test Server
+
+```bash
+cd o1-adapter/docker/scripts/
+python3 servertest.py
+```
+
+#### Terminal 3: gNB Adapter
+
+```bash
+export TERM=xterm-256color
+cd o1-adapter/src/
+sudo ./gnb-adapter
+```
+
+## 5. Deployment Method 3: Makefile-Based Deployment
+
+Through `Makefile`, we can deploy the **four different combinations** depending on what you want to integrate with:
+
+1. **Adapter alone** (no gNB, no SMO)
+2. **Adapter + telnet test server** (fake gNB)
+3. **Adapter + SMO** (real O-RAN SMO integration, but still needs a telnet server or real gNB)
+4. **Adapter + SMO + telnet test server** (full O-RAN environment with fake gNB)
+
+### 5.1 Launch Adapter Using Makefile
+
+#### Standalone Adapter
+
+```bash
+make run-o1-oai-adapter
+```
+
+#### Adapter + Telnet Test Server
+
+```bash
+make run-o1-adapter-telnet
+```
+
+> **Important:** The below methods for SMO are outdated and still expected to fail. See [ngkore/OSC-OAM](https://github.com/ngkore/OSC-OAM) for more details on SMO deployment and integration.
+
+#### Adapter + SMO
+
+```bash
+make run-o1-oai-adapter-smo
+```
+
+#### Adapter + SMO + Telnet Server
+
+```bash
+make run-o1-oai-adapter-smo-telnet
+```
+
+### 5.2 Teardown
+
+```bash
+make teardown
+```
+
+## 6. Testing the gNB Adapter with OAI gNB
+
+Start NETCONF server (only for source-based builds). Skip this step if using docker or Makefile based adapter.
+
+```bash
+sudo netopeer2-server -d -v3 -t 60
+```
+
+### 6.3 Start gNB Adapter
+
+Docker or Makefile-based deployment:
+
+```bash
+./start-adapter.sh --adapter
+```
+
+Source build deployment:
+
+```bash
+cd src/
+sudo ./gnb-adapter
+```
+
+### 6.4 Start OAI gNB (RFsim + O1 Telnet)
+
+Add following flags to gNB command to enable O1 telnet server on port `9091`:
+
+```bash
+ --telnetsrv \
+ --telnetsrv.listenport 9091 \
+ --telnetsrv.shrmod o1
+```
diff --git a/tutorials/oai/oai-ran.md b/tutorials/oai/oai-ran.md
new file mode 100644
index 0000000..26c3900
--- /dev/null
+++ b/tutorials/oai/oai-ran.md
@@ -0,0 +1,255 @@
+# OAI RAN
+
+**Author:** [Shivank Chaudhary](https://www.linkedin.com/in/shivank1128/), [Aditya Koranga](https://www.linkedin.com/in/aditya-koranga/) & [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** August 16, 2024
+
+This guide covers the deployment of the OpenAirInterface (OAI) Radio Access Network (RAN), including gNB and nrUE.
+
+
+
+For the official documentation, please refer to the [OAI RAN Deployment Guide](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/doc/NR_SA_Tutorial_OAI_nrUE.md?ref_type=heads). This guide assumes you have already deployed the **OAI 5G Core**. Follow this [ngkore/OAI-CN5G](https://github.com/ngkore/OAI-CN5G) repository for core deployment instructions.
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OAI-RAN](https://github.com/ngkore/OAI-RAN). Please refer to the original repository for the most up-to-date information.
+
+## 1\. System Preparation
+
+### 1.1 Install General Dependencies
+
+Install the necessary build tools and libraries for OAI.
+
+```bash
+sudo apt update
+sudo apt install -y autoconf automake build-essential ccache cmake cpufrequtils \
+ doxygen ethtool g++ git inetutils-tools libboost-all-dev libncurses-dev \
+ libusb-1.0-0 libusb-1.0-0-dev libusb-dev python3-dev python3-mako \
+ python3-numpy python3-requests python3-scipy python3-setuptools python3-ruamel.yaml \
+ libcap-dev libblas-dev liblapacke-dev libatlas-base-dev
+```
+
+### 1.2 Install Scope Dependencies
+
+Required for the `nrscope` visualization tool.
+
+```bash
+sudo apt install -y libforms-dev libforms-bin
+```
+
+### 1.3 Install `yaml-cpp` (Source Build Required)
+
+> **Important:** Do **not** install `yaml-cpp` using `sudo apt install libyaml-cpp-dev` as it is known to cause build failures (see below). Build it from source instead.
+
+
+
+```bash
+cd
+git clone https://github.com/jbeder/yaml-cpp.git
+cd yaml-cpp
+mkdir build && cd build
+cmake .. -DYAML_BUILD_SHARED_LIBS=ON
+make -j$(nproc)
+sudo make install
+sudo ldconfig
+cd
+rm -r yaml-cpp/
+```
+
+### 1.4 Install UHD (Only for USRP Hardware)
+
+If you are using **RF Simulator only**, you may skip this. For **USRP Hardware**, this is mandatory. Either follow the [UHD Build Guide](https://files.ettus.com/manual/page_build_guide.html) or use the commands below:
+
+```bash
+git clone https://github.com/EttusResearch/uhd.git ~/uhd
+cd ~/uhd
+git checkout v4.7.0.0
+cd host
+mkdir build
+cd build
+cmake ../
+make -j $(nproc)
+# make test # Optional
+sudo make install
+sudo ldconfig
+sudo uhd_images_downloader
+```
+
+## 2\. Build OAI RAN
+
+### 2.1 Clone Repository
+
+```bash
+git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
+cd ~/openairinterface5g
+# git checkout -f v2.1.0
+git checkout develop
+```
+
+### 2.2 Install OAI dependencies
+
+```bash
+cd ~/openairinterface5g/cmake_targets
+./build_oai -I
+```
+
+### 2.3 Build OAI gNB and nrUE
+
+This command builds both the gNB and UE with support for the OAI Scope and Ninja build system.
+
+```bash
+cd ~/openairinterface5g/cmake_targets
+./build_oai -w USRP --ninja --nrUE --gNB --build-lib "nrscope" -C
+```
+
+> **Note:** If you used docker compose to deploy OAI 5G Core (option A from ngkore/OAI-CN5G) then you likely do not need to make any changes. But if you used python wrapper to deploy OAI 5G (option B from ngkore/OAI-CN5G), then follow the step 5.
+
+## 3\. Run OAI gNB
+
+### USRP B210
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 -E --continuous-tx
+```
+
+### USRP N300
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf --gNBs.[0].min_rxtxtime 6 --usrp-tx-thread-config 1
+```
+
+### USRP X300
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf --gNBs.[0].min_rxtxtime 6 --usrp-tx-thread-config 1 -E --continuous-tx
+```
+
+### RFsimulator
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --rfsim
+```
+
+### RFsimulator in FR2
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.u3.32prb.usrpx410.conf --rfsim
+```
+
+## 4\. Run OAI nrUE
+
+### USRP B210
+
+> **Note:** This should be run in a second Ubuntu 22.04 host, other than gNB. It only applies when running OAI gNB with USRP B210.
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ue-fo-compensation -E --uicc0.imsi 001010000000001
+```
+
+### RFsimulator
+
+> **Note:** This should be run on the same host as the OAI gNB. It only applies when running OAI gNB with RFsimulator.
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --uicc0.imsi 001010000000001 --rfsim
+```
+
+### RFsimulator in FR2
+
+> **Note:** This should be run on the same host as the OAI gNB. It only applies when running OAI gNB with RFsimulator in FR2.
+
+```bash
+cd ~/openairinterface5g/cmake_targets/ran_build/build
+sudo ./nr-uesoftmodem -r 32 --numerology 3 --band 257 -C 27533280000 --uicc0.imsi 001010000000001 --ssb 72 --rfsim
+```
+
+#### End-to-End Connectivity Test
+
+Ping test from the UE host to the CN5G
+
+```bash
+ping 192.168.70.135 -I oaitun_ue1
+```
+
+## 5\. Steps to follow if using python wrapper for OAI 5G Core deployment
+
+### 5.1 Configuration Changes
+
+Apply the patch file:
+
+```bash
+git apply patch-files/openairinterface5g.patch
+```
+
+Or manually do these changes for consistent user profile across 5G Core and RAN:
+
+```diff
+diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf
+index 7f750d7259..4ae4fddca3 100644
+--- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf
++++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf
+@@ -10,8 +10,8 @@ gNBs =
+ gNB_name = "gNB-OAI";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+- tracking_area_code = 1;
+- plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
++ tracking_area_code = 0xa000;
++ plmn_list = ({ mcc = 208; mnc = 95; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0xffffff; }) });
+
+ nr_cellid = 12345678L;
+
+diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf
+index 4cea93ea46..a6e099c89f 100644
+--- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf
++++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf
+@@ -1,10 +1,10 @@
+ uicc0 = {
+-imsi = "2089900007487";
+-key = "fec86ba6eb707ed08905757b1bb44b8f";
+-opc= "C42449363BBAD02B66D16BC975D77CC1";
++imsi = "208950000000031";
++key = "0C0A34601D4F07677303652C0462535B";
++opc= "63bfa50ee6523365ff14c1f45f88737d";
+ dnn= "oai";
+ nssai_sst=1;
+-nssai_sd=1;
++nssai_sd=0xffffff;
+ }
+
+ position0 = {
+```
+
+### 5.2 Run gNB (RF Simulator Mode)
+
+Navigate to the build directory and run the gNB in RFsimulator mode:
+
+```bash
+cd openairinterface5g/cmake_targets/ran_build/build
+
+sudo ./nr-softmodem \
+ -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf \
+ -E \
+ --rfsim \
+ --rfsimulator.serveraddr server \
+ --gNBs.[0].min_rxtxtime 6
+```
+
+### 5.3 Run NR UE (RFsim Mode)
+
+```bash
+sudo ./nr-uesoftmodem \
+ -r 106 \
+ --numerology 1 \
+ --band 78 \
+ -C 3619200000 \
+ --rfsim \
+ --rfsimulator.serveraddr 127.0.0.1 \
+ -E \
+ -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf
+```
diff --git a/tutorials/osc-oam.md b/tutorials/osc-oam.md
new file mode 100644
index 0000000..99f9bbb
--- /dev/null
+++ b/tutorials/osc-oam.md
@@ -0,0 +1,459 @@
+# O-RAN-SC OAM
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** December 25, 2025
+
+This project deploys [O-RAN-SC (OSC) OAM](https://gerrit.o-ran-sc.org/r/admin/repos/oam) on a single-node Kubernetes cluster (kubeone). It includes a metric stack (InfluxDB and Grafana) to visualize performance data provided by 3GPP FTP files from network devices. Additionally, a test network function deployment is available to verify the function of NETCONF call-home and the O1 interface.
+
+
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/OSC-OAM](https://github.com/ngkore/OSC-OAM). Please refer to the original repository for the most up-to-date information.
+
+## Prerequisites
+
+### Hardware Requirements
+
+Ensure your machine meets the following specifications:
+
+| Components | Minimum Specifications | Recommended Specifications |
+| :---------- | :--------------------- | :------------------------- |
+| **OS** | Ubuntu 22.04 LTS | Ubuntu 22.04 LTS |
+| **CPU** | 4 vCPU | 8 vCPU |
+| **RAM** | 20 GB | 32 GB |
+| **Storage** | 50 GB HDD | SSD with at least 50 GB |
+
+### Software Requirements
+
+Install the `make` utility:
+
+```bash
+sudo apt update
+sudo apt install make
+```
+
+### System Access
+
+Passwordless sudo access is required for the installation scripts.
+
+1. Create a sudoers file for your user:
+
+ ```bash
+ sudo vim /etc/sudoers.d/20-kubeone-user
+ ```
+
+2. Add the following line (replace `` with your actual username):
+
+ ```
+ ALL=(ALL) NOPASSWD:ALL
+ ```
+
+## Installation Guide
+
+### 1\. Initial Setup
+
+Clone the repository and install the project prerequisites.
+
+```bash
+git clone "https://gerrit.o-ran-sc.org/r/oam"
+cd oam/solution_k8s
+make install-prereqisites
+```
+
+### 2\. Network and SSH Configuration
+
+The deployment script requires specific network settings and a running SSH agent to communicate with the local node.
+
+**Configure Deployment Variables**
+
+Run the template preparation command. When prompted for IP addresses, press **Enter** to use your current Host IP for both Internal and External IPs.
+
+```bash
+make prepare-templates
+```
+
+**Configure Local DNS**
+
+When asked for FQDN of the server, use the default `smo.o-ran-sc.org`. To resolve this locally, edit your hosts file:
+
+```bash
+sudo vim /etc/hosts
+```
+
+Add the following line:
+
+```
+127.0.0.1 smo.o-ran-sc.org
+```
+
+Alternatively, if you prefer to use your current hostname formatted as an FQDN (e.g., `ubuntu.local`), ensure it is mapped in `/etc/hosts` if you encounter connection issues.
+
+**Configure SSH Keys**
+
+Kubeone requires SSH access to the node using an identity file.
+
+```bash
+# Generate a new SSH keypair if you do not have one (Press Enter for defaults)
+ssh-keygen
+
+# Start the SSH Agent
+eval $(ssh-agent -s)
+
+# Add the private key to the agent
+ssh-add ~/.ssh/id_rsa
+
+# Authorize the key locally
+cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
+
+# Verify SSH connectivity (this should not ask for a password)
+ssh ubuntu@
+```
+
+### 3\. Kubernetes Infrastructure Deployment
+
+Deploy the single-node Kubernetes cluster.
+
+```bash
+make create-infrastructure
+```
+
+**Verification**
+
+If the script fails (e.g. see below image), ensure the node is ready and essential pods (such as cert-manager) are running. If they are not ready, run `make create-infrastructure` again after they are up.
+
+
+
+```bash
+kubectl get nodes
+kubectl get pods -n cert-manager
+```
+
+### 4\. Configuration Overrides
+
+It is necessary to override the default MariaDB image because older images have moved from the `bitnami` repository to `bitnamilegacy`.
+
+
+
+Open the override configuration file:
+
+```bash
+vim application/override/onap-override-o-ran-sc-$ONAP_VERSION.yaml
+```
+
+Make the following changes:
+
+1. Add `mariadbImage: bitnamilegacy/mariadb:11.1.2` under the `global` section.
+2. Add `image: bitnamilegacy/mariadb:11.1.2` under the `mariadb-galera` section.
+
+> **Note:** The default image tag is typically 10.5.8, but used 11.1.2 at the time of deployment.
+
+### 5\. SMO Application Deployment
+
+Deploy the SMO application stack, which includes ONAP components, SDNC, and MariaDB.
+
+```bash
+make deploy-smo
+```
+
+**Verification**
+
+The deployment may take time as it pulls several large images. Open a separate terminal to monitor the status:
+
+```bash
+kubectl get pods -A
+```
+
+```bash
+ubuntu@ubuntu:~/o1-adapter$ kubectl get pods -A
+NAMESPACE NAME READY STATUS RESTARTS AGE
+cert-manager cert-manager-5bd57786d4-vcdxb 1/1 Running 0 3h32m
+cert-manager cert-manager-cainjector-57657d5754-94vl4 1/1 Running 0 3h32m
+cert-manager cert-manager-webhook-7d9f8748d4-j956r 1/1 Running 0 3h32m
+ingress-nginx ingress-nginx-admission-create-f8wnm 0/1 Completed 0 3h32m
+ingress-nginx ingress-nginx-admission-patch-crqqj 0/1 Completed 3 3h32m
+ingress-nginx ingress-nginx-controller-65d6d978b-87kpg 1/1 Running 0 3h32m
+kube-system calico-kube-controllers-6b78c44475-v86sb 1/1 Running 0 3h32m
+kube-system canal-wvvb7 2/2 Running 0 3h32m
+kube-system coredns-5cd5577cc9-7gq6t 1/1 Running 0 3h32m
+kube-system coredns-5cd5577cc9-tcr26 1/1 Running 0 3h32m
+kube-system etcd-ubuntu 1/1 Running 0 3h33m
+kube-system kube-apiserver-ubuntu 1/1 Running 0 3h33m
+kube-system kube-controller-manager-ubuntu 1/1 Running 0 3h32m
+kube-system kube-proxy-x7brv 1/1 Running 0 3h32m
+kube-system kube-scheduler-ubuntu 1/1 Running 0 3h33m
+kube-system metrics-server-74fbf9b9b4-xcd5j 1/1 Running 0 3h32m
+kube-system node-local-dns-gf7t6 1/1 Running 0 3h32m
+mariadb-operator mariadb-operator-844954c944-pnclh 1/1 Running 0 3h25m
+mariadb-operator mariadb-operator-cert-controller-769886898b-fqxc2 1/1 Running 0 3h25m
+mariadb-operator mariadb-operator-webhook-6768b6799d-2sgxz 1/1 Running 0 3h25m
+onap mariadb-galera-0 1/1 Running 0 3h22m
+onap onap-chartmuseum-7bc565d46-jnt47 1/1 Running 0 3h21m
+onap onap-dcae-datafile-collector-54fb56f757-ckfmf 1/1 Running 0 3h21m
+onap onap-dcae-ms-healthcheck-d7cf866bb-2ntqz 1/1 Running 0 3h21m
+onap onap-dcae-pm-mapper-9f5b6fc74-qggjd 1/1 Running 0 3h21m
+onap onap-dcae-ves-collector-5f57dcb588-9f4d8 1/1 Running 0 3h21m
+onap onap-dcae-ves-mapper-5dcc6dd778-h2fr2 1/1 Running 0 3h21m
+onap onap-dmaap-dr-mariadb-init-config-job-vrrd7 0/1 Completed 0 3h21m
+onap onap-dmaap-dr-node-0 1/1 Running 0 3h21m
+onap onap-dmaap-dr-prov-66bf788f8-8scnn 1/1 Running 0 3h21m
+onap onap-message-router-0 2/2 Running 0 111m
+onap onap-robot-5c748c57d9-5dp5m 1/1 Running 0 3h21m
+onap onap-sdnc-0 1/1 Running 0 3h21m
+onap onap-sdnc-sdnrdb-init-job-p4bjt 0/1 Completed 0 3h21m
+onap onap-sdnc-web-6cf9547dbd-hlqdd 1/1 Running 0 3h21m
+onap onap-strimzi-entity-operator-5b46c475d4-ln8nb 2/2 Running 0 3h22m
+onap onap-strimzi-kafka-0 1/1 Running 0 3h22m
+onap onap-strimzi-zookeeper-0 1/1 Running 0 3h23m
+strimzi-system strimzi-cluster-operator-585f6fd995-dfts5 1/1 Running 2 (119m ago) 3h26m
+```
+
+> **Note:** Test cases may fail initially if the pods are not fully ready. Ensure all pods are in the `Running` or `Completed` state before proceeding.
+
+**Verifying 5G Bulk PM Use Case**
+
+Before running the functional test cases, you must verify the connectivity between the Datafile Collector (DFC) and the Message Router.
+
+1. **Check Datafile Collector Logs**
+
+ Check the logs for the DFC pod to ensure it is receiving notifications:
+
+ ```bash
+ kubectl logs -n onap -l app=dcae-datafile-collector
+ ```
+
+2. **Troubleshoot Message Router Connectivity**
+
+ If the logs show an error indicating that DFC failed to receive notification events from the DMaaP Message Router (see image below), steps must be taken to restore connectivity.
+
+ 
+ - **Fix:** Restart the Message Router pod:
+
+ ```bash
+ kubectl delete pod onap-message-router-0 -n onap
+ ```
+
+ - **Verify Fix:** After the pod restarts, check the DFC logs again. You should see a new error stating "Failed to download file". This is the expected behavior; it indicates that the DFC is now successfully communicating with the Message Router and attempting to fetch files.
+
+3. **Run the Test Script**
+
+ Once connectivity is confirmed, run the 5G Bulk PM Use Case functionality test. This script spins up a temporary SFTP server to complete the test.
+
+ ```bash
+ ./application/patch-pmbulk.sh
+ ```
+
+To access the SDNR web interface, first do the port forwarding to system `8181`:
+
+```bash
+kubectl port-forward -n onap svc/sdnc-web 8181:8080 --address 0.0.0.0
+```
+
+then use the following URL to access the SDNC GUI:
+
+```
+http://:8181/odlux/index.html
+```
+
+Use the default credentials:
+
+```
+Username: admin
+Password: admin
+```
+
+**General Troubleshooting**
+
+If the deployment fails or pods get stuck (e.g., `ImagePullBackOff` or `CreateContainerConfigError`), perform a clean cleanup before retrying. This ensures no stale Helm caches conflict with the new deployment.
+
+1. Undeploy the existing SMO deployment:
+
+ ```bash
+ make undeploy-smo
+ ```
+
+2. Clear the Helm cache (required if you modified override files):
+
+ ```bash
+ rm -rf /home/ubuntu/.local/share/helm/plugins/deploy/cache
+ ```
+
+## Additional Components
+
+### Metric Stack
+
+You can deploy a metric stack to visualize PM data provided by network functions (CU, DU, RU) via O1 file-ready notifications. This stack includes:
+
+- PM microservice (consumes 3GPP PM data from Kafka and writes to InfluxDB)
+- InfluxDB
+- Grafana
+
+To deploy:
+
+```bash
+make deploy-metric
+```
+
+You have to first port-forward the Grafana and InfluxDB services to local system to access its web UI using these command:
+
+- For Grafana:
+
+ ```bash
+ kubectl port-forward -n metric svc/onap-grafana 8082:80 --address 0.0.0.0
+ ```
+
+- For InfluxDB:
+
+ ```bash
+ kubectl port-forward -n metric svc/onap-influxdb2 8083:80 --address 0.0.0.0
+ ```
+
+Use the following URLs to access the Grafana and InfluxDB Dashboard:
+
+- For Grafana:
+
+ ```
+ http://:8082
+ ```
+
+- For InfluxDB:
+
+ ```
+ http://:8083
+ ```
+
+Use the following credentials:
+
+- For Grafana:
+
+ ```
+ Username: admin
+ Password: admin1234!
+ ```
+
+- For InfluxDB:
+
+ ```
+ Username: admin
+ Password: bFapG3k7H4OaBfqgcuL2szLhwMN4BnAL
+ ```
+
+To remove the metric stack:
+
+```bash
+make undeploy-metric
+```
+
+### Network Simulation
+
+For verification purposes, two network functions can be deployed:
+
+- **O-DU:** Connected via PNF Registration Request to the VES-Collector.
+- **O-RU:** Connected via call-home to the Controller.
+
+To deploy:
+
+```bash
+make deploy-test-nfs
+```
+
+You can check their connection status on SDNC GUI.
+
+To remove:
+
+```bash
+cd solution_k8s/tests/network-simulation/
+./stop-network-simulation.sh
+```
+
+To get the deployment details, run:
+
+```bash
+make get-deploy-info
+```
+
+## Exposing VES Collector for OAI O1 Adapter
+
+By default, the VES Collector service is configured as `ClusterIP`, which restricts network traffic to within the Kubernetes cluster. To allow an external OAI O1 Adapter (running on the host OS) to communicate with the collector, you must expose the service using `NodePort`.
+
+> **Note:** Follow the [ngkore/OAI-O1-Adapter](https://github.com/ngkore/OAI-O1-Adapter) to deploy the O1 Adapter on your host machine.
+
+**Step 1: Patch the Service**
+
+Run the following command to change the service type from `ClusterIP` to `NodePort`. This exposes the service on a specific port on the Ubuntu VM's IP address.
+
+```bash
+kubectl patch svc dcae-ves-collector -n onap -p '{"spec": {"type": "NodePort"}}'
+```
+
+**Step 2: Retrieve the External Port**
+
+Once configured as `NodePort`, Kubernetes assigns a random port in the _30000-32767_ range. Run the following command to identify it:
+
+```console
+ubuntu@ubuntu:~/oam/solution_k8s$ kubectl get svc -n onap dcae-ves-collector
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+dcae-ves-collector NodePort 10.103.167.13 8080:30648/TCP 140m
+```
+
+In this example, `30648` is the external port required to connect your OAI O1 Adapter.
+
+The final VES URL for O1 Adapter will be:
+
+```
+http://:/eventListener/v7
+```
+
+### Troubleshooting: VES Collector Schema Validation
+
+**Issue (on running the OAI O1 Adapter):**
+
+The O1 Adapter connects to the VES collector successfully (indicated by a Code 202 response for `pnfRegistration`), but the Collector rejects specific file readiness notifications with an HTTP 400 error.
+
+
+
+This occurs due to a 3GPP version mismatch:
+
+1. The OAI Adapter sends an event using the 3GPP Release 18 schema (`NotifyFileReady`).
+2. The ONAP VES Collector attempts to validate this against its internal library, which only supports schemas up to Release 16.
+3. The validation fails, resulting in the message rejection.
+
+**Temporary Solution: Disable Schema Validation**
+
+As we are trying in a lab environment, the most effective fix is to disable schema validation in the VES Collector. This forces the Collector to accept the JSON payload regardless of the schema version.
+
+**Step 1: Edit the Configuration**
+
+Edit the VES collector ConfigMap:
+
+```bash
+kubectl edit configmap -n onap onap-dcae-ves-collector-application-config-configmap
+```
+
+Locate the setting `collector.externalSchema.checkflag` within the configuration. Change the value from `1` (enabled) to `0` (disabled):
+
+```yaml
+collector.externalSchema.checkflag: 0
+```
+
+**Step 2: Apply Changes**
+
+For the configuration change to take effect, restart the VES collector pod:
+
+```bash
+kubectl delete pod -n onap -l app=dcae-ves-collector
+```
+
+**Step 3: Retest**
+
+Once the pod is back running, run the adapter script again to verify the fix:
+
+```bash
+./start-adapter.sh --adapter
+
+# or
+# sudo ./gnb-adapter
+```
+
+## Tutorial Video
+
+[O-RAN Software Community SMO/OAM Deployment on K8s | SDNC + VES + Simulated DU/RU](https://youtu.be/LhNYVw7YrSA?si=5r-PuLc5HwinxYKl)
diff --git a/tutorials/pqc/bouncy-castle-java.md b/tutorials/pqc/bouncy-castle-java.md
new file mode 100644
index 0000000..b1e0ea7
--- /dev/null
+++ b/tutorials/pqc/bouncy-castle-java.md
@@ -0,0 +1,138 @@
+# Bouncy Castle (Java)
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** January 25, 2026
+
+This document provides a formal, technical procedure for deploying and building the [Bouncy Castle](https://github.com/bcgit/bc-java) Crypto APIs on Ubuntu 22.04 using OpenJDK 25.
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/bouncy-castle-java](https://github.com/ngkore/bouncy-castle-java). Please refer to the original repository for the most up-to-date information.
+
+## 1. Environment Setup
+
+### 1.1. Install OpenJDK 25
+
+The build process requires a modern Java Development Kit. Follow these steps to manually install OpenJDK 25 and configure the system path.
+
+```bash
+# Download the OpenJDK Java 25 binary
+wget https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz
+
+# Create installation directory and extract
+sudo mkdir -p /opt/java
+sudo tar -xzf openjdk-25.0.1_linux-x64_bin.tar.gz -C /opt/java
+sudo mv /opt/java/jdk-25.0.1 /opt/java/jdk-25
+```
+
+### 1.2. Configure Environment Variables
+
+To ensure the system recognizes the new Java version, update your shell profile (e.g., `~/.bashrc`).
+
+```bash
+export JAVA_HOME=/opt/java/jdk-25
+export PATH=$PATH:$JAVA_HOME/bin
+
+# (Optional) Multi-JDK Testing: Define paths for older JVMs to test compatibility
+# export BC_JDK8=/path/to/java8
+# export BC_JDK11=/path/to/java11
+# export BC_JDK17=/path/to/java17
+# export BC_JDK25=/path/to/java25
+```
+
+Apply changes: `source ~/.bashrc`
+
+## 2. Repository Acquisition
+
+Bouncy Castle relies on a separate repository for regression test data. Both must be cloned into the same parent directory.
+
+```bash
+mkdir bc-workspace && cd bc-workspace
+git clone https://github.com/bcgit/bc-java.git
+git clone https://github.com/bcgit/bc-test-data.git
+```
+
+## 3. Build Process
+
+Navigate to the project root and use the Gradle wrapper. The build supports skipping tests for rapid deployment.
+
+```bash
+cd bc-java
+chmod +x gradlew
+./gradlew clean build
+
+# to skip tests
+./gradlew clean build -x test
+```
+
+## 4. Artifact Management
+
+### 4.1. Locate Generated Files
+
+Upon a successful build, each module generates artifacts in its respective `build/libs` directory. Use the following command to list all binaries:
+
+```bash
+find . -name "*.jar" | grep "build/libs"
+```
+
+
+
+### 4.2. Understanding Artifact Types
+
+Each module produces three distinct JAR types:
+
+- `*.jar`: The primary compiled binary required for production use.
+- `*-sources.jar`: Contains the source code for IDE debugging and reference.
+- `*-javadoc.jar`: Contains the API documentation.
+
+### 4.3. Module Reference Table
+
+| Module | Purpose | Artifact Path | Output JAR (v1.84) |
+| ------------ | ---------------------------------- | ------------------ | ---------------------------------- |
+| **Provider** | Main JCE/JCA crypto provider | `prov/build/libs/` | `bcprov-jdk18on-1.84-SNAPSHOT.jar` |
+| **Core** | Lightweight low-level crypto API | `core/build/libs/` | `bccore-jdk18on-1.84-SNAPSHOT.jar` |
+| **PKIX** | Certificates (X.509), CMS, PKCS#12 | `pkix/build/libs/` | `bcpkix-jdk18on-1.84-SNAPSHOT.jar` |
+| **TLS** | TLS/SSL & JSSE provider | `tls/build/libs/` | `bctls-jdk18on-1.84-SNAPSHOT.jar` |
+| **Util** | ASN.1 and PKIX utility classes | `util/build/libs/` | `bcutil-jdk18on-1.84-SNAPSHOT.jar` |
+| **PG** | OpenPGP implementation | `pg/build/libs/` | `bcpg-jdk18on-1.84-SNAPSHOT.jar` |
+
+## 5. Verification
+
+### 5.1. Artifact Integrity (Optional)
+
+If utilizing pre-compiled artifacts from [Maven Central](https://www.bouncycastle.org/download/bouncy-castle-java/#latest), verify them against the Bouncy Castle public key using GnuPG.
+
+```bash
+# Dearmor the public key
+gpg -o bc_maven_public_key.gpg --dearmor bc_maven_public_key.asc
+
+# Verify the JAR signature
+gpg --no-default-keyring --keyring ./bc_maven_public_key.gpg --verify .jar.asc .jar
+```
+
+### 5.2. Functional Deployment Test
+
+To verify the installation on OpenJDK 25, compile and run the following Java snippet.
+
+Save this code as `VerifyDeployment.java`:
+
+```java
+import java.security.Security;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+public class VerifyDeployment {
+ public static void main(String[] args) {
+ BouncyCastleProvider bc = new BouncyCastleProvider();
+ Security.addProvider(bc);
+ System.out.println("Bouncy Castle Provider Version: " + bc.getVersionStr());
+ System.out.println("Registration Status: " + (Security.getProvider("BC") != null ? "Success" : "Failed"));
+ }
+}
+```
+
+Execute the following commands to link the provider JAR and verify the build:
+
+```bash
+export BC_JAR="./prov/build/libs/bcprov-jdk18on-1.84-SNAPSHOT.jar"
+javac -cp "$BC_JAR" VerifyDeployment.java
+java -cp "$BC_JAR:." VerifyDeployment
+```
diff --git a/tutorials/pqc/cupqc.md b/tutorials/pqc/cupqc.md
new file mode 100644
index 0000000..ac1abfd
--- /dev/null
+++ b/tutorials/pqc/cupqc.md
@@ -0,0 +1,110 @@
+# cuPQC
+
+**Authors:** [Lakshya Chopra](https://www.linkedin.com/in/lakshyachopraa/), [Satyam Dubey](https://www.linkedin.com/in/satyam-dubey-142598258/) & [Aditya Koranga](https://www.linkedin.com/in/aditya-koranga/)
+
+**Published:** December 08, 2024
+
+**Note:** This documentation is a replica of the README available at the [github/ngkore/cuPQC](https://github.com/ngkore/cuPQC). Please refer to the original repository for the most up-to-date information.
+
+## Prerequisites
+
+To utilize cuPQC, the following environment requirements must be met:
+
+### System Requirements
+
+- CPU: x86_64 architecture
+- GPU: NVIDIA GPU with architecture 70, 75, 80, 86, 89, or 90
+- Recommended Hardware: NVIDIA H100 (Development tested on RTX A4000)
+
+### Software Requirements
+
+- CUDA Toolkit: Version 12.4 or newer
+- Host Compiler: C++17 support required (g++/gcc 11 or newer)
+- Build Tools: CMake 3.20+ (optional)
+
+### CUDA Installation (Ubuntu 22.04)
+
+```bash
+wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
+sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
+wget https://developer.download.nvidia.com/compute/cuda/12.6.3/local_installers/cuda-repo-ubuntu2204-12-6-local_12.6.3-560.35.05-1_amd64.deb
+sudo dpkg -i cuda-repo-ubuntu2204-12-6-local_12.6.3-560.35.05-1_amd64.deb
+sudo cp /var/cuda-repo-ubuntu2204-12-6-local/cuda-*-keyring.gpg /usr/share/keyrings/
+sudo apt-get update
+sudo apt-get -y install cuda-toolkit-12-6
+
+```
+
+### Post-Installation Setup
+
+Add the following to your `.bashrc` file to make changes permanent:
+
+```bash
+export PATH=/usr/local/cuda-12.6/bin${PATH:+:${PATH}}
+export LD_LIBRARY_PATH=/usr/local/cuda-12.6/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
+
+```
+
+Verify the installation:
+
+```bash
+nvcc --version
+
+```
+
+### Resources
+
+cuPQC benchmarking video: [https://youtu.be/mdnXKroR1wo?si=yiTMTUy1MIzl20_7](https://youtu.be/mdnXKroR1wo?si=yiTMTUy1MIzl20_7)
+
+## Build Instructions
+
+1. Clone the repository and navigate to the examples directory:
+
+```bash
+cd cuPQC/examples/
+
+```
+
+2. Build using the provided Makefile:
+
+```bash
+make
+
+```
+
+Alternatively, build manually using `nvcc`:
+
+```bash
+nvcc -dlto -arch=native -std=c++17 -O3 -L../lib/ -lcupqc -lcuhash -o -I../include/ -I../include/cupqc
+
+```
+
+Note: Ensure the `-arch` flag matches your GPU's compute capability. If programs fail to execute, delete the binary and rebuild.
+
+## Benchmarking
+
+Tests were performed on an NVIDIA GH200.
+
+### ML-KEM 512 Performance
+
+Build and run the benchmark:
+
+```bash
+nvcc -dlto -arch=native -std=c++17 -O3 -L../lib/ -lcupqc -o v4_max_bench v4_max_bench.cu -I../include/ -I../include/cupqc
+./v4_max_bench
+
+```
+
+Results:
+
+- Keygen: ~20 million ops/sec
+- Encapsulation: ~18.5 million ops/sec
+- Decapsulation: ~18 million ops/sec
+
+### Additional Benchmarks
+
+- ML-KEM 768: `./mlkem768_bench`
+- ML-KEM 1024: `./mlkem1024_bench`
+- ML-DSA 44: `./bench_mldsa`
+- ML-DSA 65: `./mldsa65_bench`
+- ML-DSA 87: `./mldsa87_bench`
diff --git a/tutorials/pqc/index.md b/tutorials/pqc/index.md
new file mode 100644
index 0000000..45ab074
--- /dev/null
+++ b/tutorials/pqc/index.md
@@ -0,0 +1,10 @@
+# PQC
+
+```{toctree}
+:maxdepth: 1
+
+cupqc
+openssl-jostle
+bouncy-castle-java
+jostle-bc-crypto-benchmarking-tool
+```
diff --git a/tutorials/pqc/jostle-bc-crypto-benchmarking-tool.md b/tutorials/pqc/jostle-bc-crypto-benchmarking-tool.md
new file mode 100644
index 0000000..fb110a1
--- /dev/null
+++ b/tutorials/pqc/jostle-bc-crypto-benchmarking-tool.md
@@ -0,0 +1,166 @@
+# Jostle Bouncy Castle Crypto Benchmarking Tool
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** January 28, 2026
+
+JMH benchmarking suite comparing cryptographic performance between [Bouncy Castle (BC)](https://github.com/bcgit/bc-java) and [OpenSSL Jostle](https://github.com/openssl-projects/openssl-jostle) providers.
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/jostle-bc-crypto-benchmarking](https://github.com/ngkore/jostle-bc-crypto-benchmarking). Please refer to the original repository for the most up-to-date information.
+
+## Supported Algorithms
+
+- **Symmetric**: AES, ARIA, Camellia, SM4 (AEAD, Block, Stream modes)
+- **KDF**: PBKDF2 (SHA2, SHA3, SM3) and Scrypt
+- **Post-Quantum**: ML-DSA, ML-KEM, SLH-DSA (NIST variants)
+
+## Prerequisites
+
+### Java 25+
+
+Download from the OpenJDK [download page](https://jdk.java.net/25/).
+
+```bash
+wget https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz
+
+# Create directory for Java installation
+sudo mkdir -p /opt/java
+
+# Extract the tar.gz file to /opt/java
+sudo tar -xzf openjdk-25.0.1_linux-x64_bin.tar.gz -C /opt/java
+
+# Rename for easier access (optional)
+sudo mv /opt/java/jdk-25.0.1 /opt/java/jdk-25
+```
+
+Configure `JAVA_HOME` and `PATH` variables:
+
+```bash
+# Add to ~/.bashrc
+export JAVA_HOME=/opt/java/jdk-25
+export PATH=$PATH:$JAVA_HOME/bin
+
+# Apply changes
+source ~/.bashrc
+```
+
+### Node.js 20+ (for Visualizer)
+
+Required only if you want to run the visualizer locally.
+
+```bash
+# Using nvm (recommended)
+curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
+source ~/.bashrc
+nvm install 20
+nvm use 20
+```
+
+## Setup
+
+### 1. Provider JARs
+
+Place the following provider JARs in the `libs/` directory:
+
+- `bcprov-jdk18on-*.jar` (from Bouncy Castle)
+- `openssl-jostle-*.jar` (from Jostle provider)
+
+> **Note:** Follow [ngkore/jostle](https://github.com/ngkore/jostle) and [ngkore/bouncy-castle-java](https://github.com/ngkore/bouncy-castle-java) for instructions on how to build the Jostle and Bouncy Castle provider.
+
+### 2. Native Library Paths
+
+The Jostle provider requires OpenSSL and its own JNI libraries. Edit `scripts/run_benchmarks.sh` and update the `DEFAULT_NATIVE_LIB_PATH` variable to match your environment:
+
+```bash
+# Example in scripts/run_benchmarks.sh
+DEFAULT_NATIVE_LIB_PATH="/path/to/openssl/libssl.so:/path/to/jostle/libinterface_jni.so"
+```
+
+If you don't know the paths, use the `find` command:
+
+**OpenSSL (look for libssl.so):**
+
+```bash
+find /usr -name "libssl.so*" 2>/dev/null
+# Or typical local install:
+find /home -name "libssl.so*" 2>/dev/null
+```
+
+Output:
+
+```bash
+ubuntu@vm01:~$ find /home -name "libssl.so*" 2>/dev/null
+/home/ubuntu/openssl-3.6.0/libssl.so.3
+/home/ubuntu/openssl-3.6.0/libssl.so
+/home/ubuntu/openssl_3_6/lib64/libssl.so.3
+/home/ubuntu/openssl_3_6/lib64/libssl.so
+```
+
+**Jostle (look for libinterface_jni.so):**
+
+```bash
+find / -name "libinterface_jni.so" 2>/dev/null
+```
+
+Output:
+
+```bash
+ubuntu@vm01:~$ find / -name "libinterface_jni.so" 2>/dev/null
+/home/ubuntu/bouncy-jostle/openssl-jostle/jostle/src/main/resources/native/linux/x86_64/libinterface_jni.so
+/home/ubuntu/bouncy-jostle/openssl-jostle/jostle/bin/main/native/linux/x86_64/libinterface_jni.so
+/home/ubuntu/bouncy-jostle/openssl-jostle/jostle/build/resources/main/native/linux/x86_64/libinterface_jni.so
+/home/ubuntu/bouncy-jostle/openssl-jostle/interface/libinterface_jni.so
+```
+
+Use `/home/ubuntu/openssl_3_6/lib64:/home/ubuntu/bouncy-jostle/openssl-jostle/jostle/build/resources/main/native/linux/x86_64` for the `DEFAULT_NATIVE_LIB_PATH`.
+
+## Running Benchmarks
+
+Use the provided script to execute benchmarks. It handles configuration, native paths, and logging.
+
+```bash
+./scripts/run_benchmarks.sh
+```
+
+### Outputs
+
+- **JSON Results**: `build/results/jmh/results.json` (for analysis)
+- **Execution Log**: `build/results/jmh/benchmark_run.log` (terminal output)
+
+### Configuration
+
+Configuration is centrally managed in `scripts/run_benchmarks.sh`.
+
+| Parameter | Description | Default |
+| ------------ | ------------------------------------------ | --------- |
+| `WARMUP` | Number of warmup iterations | `2` |
+| `ITERATIONS` | Number of measurement iterations | `3` |
+| `TIME` | Duration of each iteration | `"5s"` |
+| `FORK` | Number of fresh JVM forks | `1` |
+| `MODE` | JMH Mode (`thrpt`, `avgt`, `sample`, `ss`) | `"thrpt"` |
+
+## Visualizer
+
+A React-based web application for visualizing benchmark results with interactive charts and tables.
+
+### Local Development
+
+```bash
+cd visualizer
+npm install
+npm run dev
+```
+
+The visualizer automatically loads results from `results/results.json` via symlink. After running benchmarks, start the visualizer to see the comparison between BC and Jostle performance.
+
+### Production Build
+
+```bash
+npm run build
+```
+
+The built files will be in `visualizer/dist/`.
+
+### Live Demo
+
+The visualizer is automatically deployed to GitHub Pages when changes are pushed to `visualizer/` or `results/` on the main branch. Access the visualizer at: [https://ngkore.github.io/jostle-bc-crypto-benchmarking/](https://ngkore.github.io/jostle-bc-crypto-benchmarking/)
diff --git a/tutorials/pqc/openssl-jostle.md b/tutorials/pqc/openssl-jostle.md
new file mode 100644
index 0000000..0aa4b1d
--- /dev/null
+++ b/tutorials/pqc/openssl-jostle.md
@@ -0,0 +1,213 @@
+# OpenSSL Jostle
+
+**Author:** [Shubham Kumar](https://www.linkedin.com/in/chmodshubham/)
+
+**Published:** November 08, 2025
+
+[OpenSSL Jostle](https://github.com/openssl-projects/openssl-jostle) is a Java provider that wraps OpenSSL native library features into a standard Java JCA/JCE Provider. This guide covers the deployment process on a Linux system.
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/jostle](https://github.com/ngkore/jostle). Please refer to the original repository for the most up-to-date information.
+
+## Prerequisites
+
+The deployment requires Java 25 to build, though the final jar supports Java 1.8 to Java 25. You will also need CMake version 3.31 or higher.
+
+## Deployment Steps
+
+### 1. System Preparation
+
+Update system packages and install build dependencies:
+
+```bash
+sudo apt update && sudo apt upgrade
+sudo apt install -y build-essential checkinstall zlib1g-dev libssl-dev
+sudo apt install -y perl-modules perl-doc
+```
+
+### 2. Build OpenSSL Library 3.5+
+
+OpenSSL Library 3.5+ source bundle can be downloaded from [OpenSSL Downloads](https://openssl-library.org/source/).
+
+```bash
+wget https://github.com/openssl/openssl/releases/download/openssl-3.6.0/openssl-3.6.0.tar.gz
+tar -xzf openssl-3.6.0.tar.gz
+cd openssl-3.6.0/
+```
+
+Configure and build OpenSSL with a local prefix:
+
+```bash
+## installing it to a local directory, openssl_3_6 instead of system-wide installation
+./Configure --prefix=`pwd`/../openssl_3_6
+make clean
+make -j$(nproc)
+make install_sw
+```
+
+Set the OPENSSL_PREFIX environment variable:
+
+```bash
+cd ../openssl_3_6/
+export OPENSSL_PREFIX=`pwd`
+echo "${OPENSSL_PREFIX}"
+cd ..
+```
+
+### 3. Install OpenJDK Java 25
+
+Download the OpenJDK Java 25 [download page](https://jdk.java.net/25/).
+
+```bash
+wget https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz
+
+# Create directory for Java installation
+sudo mkdir -p /opt/java
+
+# Extract the tar.gz file to /opt/java
+sudo tar -xzf openjdk-25.0.1_linux-x64_bin.tar.gz -C /opt/java
+
+# Rename for easier access (optional)
+sudo mv /opt/java/jdk-25.0.1 /opt/java/jdk-25
+```
+
+**Set Environment Variables**
+
+Configure `JAVA_HOME` and `PATH` variables for your system:
+
+```bash
+# Edit your bash profile
+vim ~/.bashrc
+
+# Add these lines at the end of the file:
+export JAVA_HOME=/opt/java/jdk-25
+export PATH=$PATH:$JAVA_HOME/bin
+
+# Apply the changes
+source ~/.bashrc
+```
+
+Check that Java 25 is installed correctly:
+
+```bash
+java --version
+javac --version
+echo $JAVA_HOME
+```
+
+
+
+For system-wide installation (all users), edit `/etc/environment`:
+
+```bash
+sudo vim /etc/environment
+
+# Add this line:
+JAVA_HOME="/opt/java/jdk-25"
+PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/java/jdk-25/bin"
+```
+
+### 4. Install CMake
+
+Download and install CMake 4.1.2 (or any version >= 3.31 will work), see https://github.com/Kitware/CMake/releases/:
+
+```bash
+wget https://github.com/Kitware/CMake/releases/download/v4.1.2/cmake-4.1.2-linux-x86_64.tar.gz
+tar -xzf cmake-4.1.2-linux-x86_64.tar.gz
+sudo cp -r cmake-4.1.2-linux-x86_64 /opt/cmake
+sudo ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
+cmake --version
+```
+
+
+
+### 5. Build OpenSSL JOSTLE
+
+Clone the repository:
+
+```bash
+git clone https://github.com/openssl-projects/openssl-jostle.git
+cd openssl-jostle/
+```
+
+Generate Java headers:
+
+```bash
+./gradlew clean compileJava
+```
+
+
+
+Enable operations testing support (optional):
+
+```bash
+export JOSTLE_OPS_TEST=true
+
+# To unset
+unset JOSTLE_OPS_TEST
+```
+
+Build the interface libraries:
+
+```bash
+./interface/build.sh
+```
+
+The command will compile the C/C++ interface layer that bridges Java and OpenSSL. This script uses `CMAKE` and the previously set `OPENSSL_PREFIX` to find OpenSSL libraries. It generates both JNI (Java Native Interface) and FFI (Foreign Function Interface) bindings. The compiled libraries are automatically copied to `jostle/src/main/resources/native/`.
+
+Build the final jar:
+
+```bash
+./gradlew clean build
+
+## To skip tests during build
+./gradlew clean build -x test
+```
+
+
+
+Complete build output (without skipping tests) can be found in [gradle-build.log](https://github.com/ngkore/jostle/blob/main/gradlew-build.log).
+
+### 6. Verify Installation
+
+Run the DumpInfo utility to verify successful deployment:
+
+```bash
+java --module-path jostle/build/libs/openssl-jostle-1.0-SNAPSHOT.jar \
+--enable-native-access=org.openssl.jostle.prov \
+--module org.openssl.jostle.prov/org.openssl.jostle.util.DumpInfo
+```
+
+
+
+## Build Output
+
+The compiled jar file will be located at:
+
+```
+openssl-jostle/jostle/build/libs/openssl-jostle-1.0-SNAPSHOT.jar
+
+```
+
+## Interface Selection
+
+JOSTLE defaults to FFI interface when available. To force JNI interface:
+
+```bash
+java -Dorg.openssl.jostle.loader.interface=JNI \
+--module-path jostle/build/libs/openssl-jostle-1.0-SNAPSHOT.jar \
+--enable-native-access=org.openssl.jostle.prov \
+--module org.openssl.jostle.prov/org.openssl.jostle.util.DumpInfo
+```
+
+Interface Options:
+
+| Option | Description |
+| :----- | :------------------------------------------------------------- |
+| `auto` | Automatically detect and use FFI if available, otherwise JNI |
+| `ffi` | Force FFI interface only |
+| `jni` | Force JNI interface only |
+| `none` | Do not extract interface libraries (for custom configurations) |
+
+## Tutorial Video
+
+[The OpenSSL Jostle Project Deployment Guide](https://youtu.be/VjXnHwPAvic?si=CDVw6I6v6pZvQ0eN)
diff --git a/tutorials/srsran/index.md b/tutorials/srsran/index.md
new file mode 100644
index 0000000..dbc5e17
--- /dev/null
+++ b/tutorials/srsran/index.md
@@ -0,0 +1,8 @@
+# srsRAN
+
+```{toctree}
+:maxdepth: 1
+
+srsran-f1ap-split
+srsran-open5gs
+```
diff --git a/tutorials/srsran/srsran-f1ap-split.md b/tutorials/srsran/srsran-f1ap-split.md
new file mode 100644
index 0000000..cb8ab74
--- /dev/null
+++ b/tutorials/srsran/srsran-f1ap-split.md
@@ -0,0 +1,291 @@
+# srsRAN F1AP Split
+
+**Author:** [Shankar Malik](https://www.linkedin.com/in/evershalik/)
+
+**Published:** January 16, 2026
+
+This repository documents a complete **srsRAN F1AP CU–DU split deployment** using a **virtual RF setup (ZeroMQ)**.
+The deployment follows a **3GPP-aligned CU/DU architecture** and is suitable for **learning, experimentation, and research**.
+
+
+
+**End-to-End 5G Deployment using Open5GS + srsRAN (CU/DU Split) + srsUE**
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/srsRAN_F1AP_Split](https://github.com/ngkore/srsRAN_F1AP_Split). Please refer to the original repository for the most up-to-date information.
+
+## Key Components
+
+- **Open5GS** – 5G Core Network (Docker-based)
+- **srsRAN Project** – 5G gNB split into:
+ - **CU (CU-CP + CU-UP)**
+ - **DU**
+
+- **srsRAN 4G (srsUE)** – UE implementation
+- **ZeroMQ** – Virtual RF interface (no SDR required)
+
+## High-Level Architecture
+
+```
+ +-----------------------------+
+ | VM 1 |
+ | |
+ | Open5GS 5GC (Docker) |
+ | + |
+ | srsRAN CU (CU-CP + CU-UP) |
+ +-------------+---------------+
+ |
+ F1AP / F1U
+ |
+ +-------------v---------------+
+ | VM 2 |
+ | |
+ | srsRAN DU |
+ | + |
+ | srsUE (ZMQ, netns) |
+ +-----------------------------+
+```
+
+## Reference Documentation
+
+- **srsRAN CU–DU Split (Official Docs)**
+ [https://docs.srsran.com/projects/project/en/latest/tutorials/source/cu_du_split/source/index.html#o-ran-cu-du-split](https://docs.srsran.com/projects/project/en/latest/tutorials/source/cu_du_split/source/index.html#o-ran-cu-du-split)
+
+## System Requirements
+
+### Hardware
+
+| Resource | Recommendation |
+| -------- | -------------- |
+| CPU | 8 cores |
+| RAM | 12 GB |
+| Disk | 40+ GB |
+
+### Operating System
+
+- Ubuntu **22.04.1 LTS**
+
+### Deployment Model
+
+- **VM 1** → Open5GS Core + srsCU
+- **VM 2** → srsDU + srsUE
+
+## Software Components
+
+- Open5GS (Docker-based)
+- srsRAN Project (latest)
+- srsRAN 4G **v23.11 or later**
+- ZeroMQ
+
+## Prerequisites (VM1 & VM2)
+
+Install the following packages **on both VMs**:
+
+```bash
+sudo apt update
+sudo apt install -y \
+ git net-tools build-essential cmake \
+ libfftw3-dev libmbedtls-dev \
+ libboost-program-options-dev \
+ libconfig++-dev libsctp-dev \
+ libyaml-cpp-dev libgtest-dev \
+ libzmq3-dev
+```
+
+## Build srsRAN Project (CU & DU)
+
+> **Note:** Perform this step on **both VM1 and VM2**
+
+```bash
+cd ~
+git clone https://github.com/srsran/srsRAN_Project.git
+cd srsRAN_Project
+mkdir build && cd build
+
+cmake ../ -DENABLE_EXPORT=ON -DENABLE_ZEROMQ=ON
+make -j$(nproc)
+```
+
+## VM 1 - Open5GS Core + srsCU
+
+### Install Docker (VM1 only)
+
+```bash
+sudo apt install -y ca-certificates curl
+sudo install -m 0755 -d /etc/apt/keyrings
+sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
+ -o /etc/apt/keyrings/docker.asc
+sudo chmod a+r /etc/apt/keyrings/docker.asc
+
+echo "deb [arch=$(dpkg --print-architecture) \
+signed-by=/etc/apt/keyrings/docker.asc] \
+https://download.docker.com/linux/ubuntu \
+$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
+sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+
+sudo apt update
+sudo apt install -y docker-ce docker-ce-cli \
+ containerd.io docker-buildx-plugin docker-compose-plugin
+```
+
+Add your user to Docker group:
+
+```bash
+sudo usermod -aG docker $(whoami)
+newgrp docker
+```
+
+### Deploy Open5GS 5G Core
+
+```bash
+cd ~/srsRAN_Project/docker
+docker compose up --build 5gc -d
+```
+
+Verify:
+
+```bash
+docker ps
+```
+
+### Configure srsCU (VM1)
+
+#### CU Configuration (`cu.yml`)
+
+Minimum required configuration:
+
+```yaml
+cu_cp:
+ amf:
+ addr: 10.53.1.2
+ port: 38412
+ bind_addr: 10.53.1.1
+ supported_tracking_areas:
+ - tac: 7
+ plmn_list:
+ - plmn: "00101"
+ tai_slice_support_list:
+ - sst: 1
+
+ f1ap:
+ bind_addr: 127.0.10.1 # CU IP
+
+cu_up:
+ f1u:
+ socket:
+ - bind_addr: 127.0.10.1
+```
+
+> **Note:** The `amf` parameters are specific to the docker configuration of the core. If you are running Open5GS via the local setup, your configuration will be different. For **local Open5GS**, use:
+
+```yaml
+amf:
+ addr: 127.0.1.100
+ bind_addr: 127.0.1.1
+```
+
+### Run srsCU
+
+```bash
+cd ~/
+wget https://raw.githubusercontent.com/ngkore/srsRAN_F1AP_Split/refs/heads/main/config/cu.yml
+cd ~/srsRAN_Project/build/apps/cu
+sudo ./srscu -c ~/cu.yml
+```
+
+## VM 2 — srsDU + srsUE
+
+### Build srsRAN 4G (srsUE)
+
+```bash
+cd ~
+git clone https://github.com/srsRAN/srsRAN_4G.git
+cd srsRAN_4G
+mkdir build && cd build
+
+cmake ../ -DENABLE_EXPORT=ON -DENABLE_ZEROMQ=ON
+make -j$(nproc)
+```
+
+### Configure srsDU (VM2)
+
+#### DU Configuration (`du.yml`)
+
+```yaml
+f1ap:
+ cu_cp_addr: 127.0.10.1
+ bind_addr: 127.0.10.2
+
+f1u:
+ socket:
+ - bind_addr: 127.0.10.2
+
+ru_sdr:
+ device_driver: zmq
+ device_args: tx_port=tcp://127.0.0.1:2000,rx_port=tcp://127.0.0.1:2001,base_srate=23.04e6
+ srate: 23.04
+ tx_gain: 75
+ rx_gain: 75
+
+cell_cfg:
+ dl_arfcn: 368500
+ band: 3
+ channel_bandwidth_MHz: 20
+ common_scs: 15
+ plmn: "00101"
+ tac: 7
+```
+
+## Run srsDU
+
+```bash
+cd ~/
+wget https://raw.githubusercontent.com/ngkore/srsRAN_F1AP_Split/refs/heads/main/config/du.yml
+cd ~/srsRAN_Project/build/apps/du
+sudo ./srsdu -c ~/du.yml
+```
+
+## Run srsUE (VM2)
+
+Create network namespace:
+
+```bash
+sudo ip netns add ue1
+```
+
+Run srsUE:
+
+```bash
+cd ~/
+wget https://docs.srsran.com/projects/project/en/latest/_downloads/fbb79b4ff222d1829649143ca4cf1446/ue_zmq.conf
+cd ~/srsRAN_4G/build/srsue/src
+sudo ./srsue ~/ue_zmq.conf
+```
+
+## Routing Configuration
+
+```bash
+# Host Routing
+sudo ip route add 10.45.0.0/16 via 10.53.1.2
+# UE Namespace Routing
+sudo ip netns exec ue1 ip route add default via 10.45.1.1 dev tun_srsue
+```
+
+## Connectivity Test
+
+### Uplink (UE → Core)
+
+```bash
+sudo ip netns exec ue1 ping 10.45.1.1
+```
+
+### Downlink (Core → UE)
+
+```bash
+ping 10.45.1.2
+```
+
+Successful ping confirms **end-to-end CU–DU split connectivity**.
+
+## Tutorial Video
+
+[srsRAN F1AP Split Tutorial (Diasggregated CU DU Mode)](https://youtu.be/3OEBkeyGQ3U?si=4GuQt9YJI8edlbY5)
diff --git a/tutorials/srsran/srsran-open5gs.md b/tutorials/srsran/srsran-open5gs.md
new file mode 100644
index 0000000..130f912
--- /dev/null
+++ b/tutorials/srsran/srsran-open5gs.md
@@ -0,0 +1,226 @@
+# srsRAN Open5GS
+
+**Author:** [Shankar Malik](https://www.linkedin.com/in/evershalik/)
+
+**Published:** January 15, 2026
+
+This repository documents a complete **end-to-end 5G standalone (SA) setup** using:
+
+- **Open5GS** as the 5G Core (Docker-based)
+- **srsRAN Project** as the 5G gNB
+- **srsRAN 4G (srsUE)** as the UE
+- **ZeroMQ** as a virtual RF interface (no SDR required)
+
+
+
+This setup is intended for **testing, learning, and research**, and runs entirely on a **single Ubuntu VM**. **End-to-End 5G Setup using Open5GS + srsRAN gNB + srsUE (ZMQ-based Virtual RF)**
+
+> **Note:** This documentation is a replica of the README available at the [github/ngkore/Open5GS-srsRAN](https://github.com/ngkore/Open5GS-srsRAN). Please refer to the original repository for the most up-to-date information.
+
+## Reference Documentation
+
+- srsUE with srsRAN gNB (ZMQ):
+ [https://docs.srsran.com/projects/project/en/latest/tutorials/source/srsUE/source/index.html](https://docs.srsran.com/projects/project/en/latest/tutorials/source/srsUE/source/index.html)
+
+## Architecture Overview
+
+```
++-----------------------------+
+| Ubuntu 22.04 VM |
+| |
+| +----------------------+ |
+| | Open5GS 5G Core | |
+| | (Docker Compose) | |
+| +----------+-----------+ |
+| | N2 / N3 |
+| +----------v-----------+ |
+| | srsRAN gNB | |
+| | (ZMQ RF) | |
+| +----------+-----------+ |
+| | ZMQ |
+| +----------v------------+ |
+| | srsUE | |
+| | (Network Namespace) | |
+| +-----------------------+ |
++-----------------------------+
+```
+
+## Hardware and Software Requirements
+
+### System
+
+- Ubuntu **22.04.1 LTS**
+- Single VM or bare-metal system
+- Internet access
+
+### Software Components
+
+- **Open5GS** (Docker-based 5G Core)
+- **srsRAN Project** (5G gNB)
+- **srsRAN 4G (23.11 or later)** (srsUE)
+- **ZeroMQ** (virtual RF transport)
+
+## Prerequisites
+
+### System Packages
+
+```bash
+sudo apt update
+sudo apt install -y git net-tools build-essential cmake \
+ libfftw3-dev libmbedtls-dev \
+ libboost-program-options-dev \
+ libconfig++-dev libsctp-dev \
+ libyaml-cpp-dev libgtest-dev \
+ libzmq3-dev
+```
+
+### Docker Installation (for Open5GS)
+
+```bash
+sudo apt install -y ca-certificates curl
+sudo install -m 0755 -d /etc/apt/keyrings
+sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
+ -o /etc/apt/keyrings/docker.asc
+sudo chmod a+r /etc/apt/keyrings/docker.asc
+
+echo "deb [arch=$(dpkg --print-architecture) \
+signed-by=/etc/apt/keyrings/docker.asc] \
+https://download.docker.com/linux/ubuntu \
+$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
+sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
+
+sudo apt update
+sudo apt install -y docker-ce docker-ce-cli \
+ containerd.io docker-buildx-plugin \
+ docker-compose-plugin
+```
+
+Add your user to the Docker group:
+
+```bash
+sudo usermod -aG docker $(whoami)
+newgrp docker
+```
+
+## Build srsRAN 4G (for srsUE)
+
+```bash
+cd ~
+git clone https://github.com/srsRAN/srsRAN_4G.git
+cd srsRAN_4G
+mkdir build && cd build
+
+cmake ../ -DENABLE_EXPORT=ON -DENABLE_ZEROMQ=ON
+make -j$(nproc)
+make test # optional
+```
+
+## Build srsRAN Project (for 5G gNB)
+
+```bash
+cd ~
+git clone https://github.com/srsran/srsRAN_Project.git
+cd srsRAN_Project
+mkdir build && cd build
+
+cmake ../ -DENABLE_EXPORT=ON -DENABLE_ZEROMQ=ON
+make -j$(nproc)
+```
+
+## Deploy Open5GS 5G Core (Docker)
+
+```bash
+cd ~/srsRAN_Project/docker
+docker compose up --build 5gc -d
+```
+
+Verify containers:
+
+```bash
+docker ps
+docker logs -f open5gs_5gc
+```
+
+## Configuration Files (ZMQ)
+
+Download reference configuration files:
+
+```bash
+cd ~
+wget https://docs.srsran.com/projects/project/en/latest/_downloads/a7c34dbfee2b765503a81edd2f02ec22/gnb_zmq.yaml
+wget https://docs.srsran.com/projects/project/en/latest/_downloads/fbb79b4ff222d1829649143ca4cf1446/ue_zmq.conf
+```
+
+## Run srsRAN gNB
+
+```bash
+cd ~/srsRAN_Project/build/apps/gnb
+sudo ./gnb -c ~/gnb_zmq.yaml
+```
+
+Ensure the gNB successfully:
+
+- Connects to AMF
+- Starts NGAP and GTP-U
+
+## Run srsUE (with Network Namespace)
+
+Create UE network namespace:
+
+```bash
+sudo ip netns add ue1
+ip netns list
+```
+
+Run srsUE:
+
+```bash
+cd ~/srsRAN_4G/build/srsue/src
+
+sudo ./srsue ~/ue_zmq.conf
+```
+
+Or explicitly via ZMQ arguments:
+
+```bash
+sudo ./srsue \
+ --rf.device_name=zmq \
+ --rf.device_args="tx_port=tcp://*:2001,rx_port=tcp://localhost:2000,id=ue,base_srate=23.04e6" \
+ --gw.netns=ue1
+```
+
+## Routing Configuration
+
+### Host Routing (Downlink)
+
+```bash
+sudo ip route add 10.45.0.0/16 via 10.53.1.2
+ip route
+```
+
+### UE Namespace Routing
+
+```bash
+sudo ip netns exec ue1 ip route add default via 10.45.1.1 dev tun_srsue
+sudo ip netns exec ue1 ip route
+```
+
+## Connectivity Test
+
+### Uplink Test (UE → Core)
+
+```bash
+sudo ip netns exec ue1 ping 10.45.1.1
+```
+
+### Downlink Test (Core → UE)
+
+```bash
+ping 10.45.1.2
+```
+
+Successful ping confirms **end-to-end user plane connectivity**.
+
+## Tutorial Video
+
+[Open5GS srsRAN E2E Deployment Tutorial](https://youtu.be/dn2V1daWnXY?si=w_aZmGpyUHKQHxEV)